IOError: [Errno 9] Bad file descriptor

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
pixewakb
User
Beiträge: 1412
Registriert: Sonntag 24. April 2011, 19:43

:K

Jemand eine Idee, was dahinter steckt!? Wenn ich es richtig verstehe, dann kommt der Fehler, wenn eine Datei, die in Python geöffnet ist, von einem anderen Prozess geschlossen wurde!?

Jemand eine Idee, was konkret das auslösen kann und wie ich das ggf. umschiffen könnte!?

Mein Tool arbeitet Berichte ab, die andere Tools in einen Eingang-Ordner schreiben.

Code: Alles auswählen

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import codecs
import os
import pywikibot
import shutil
import sys
import time
import traceback

'''
Was soll das Tool machen?

In Dauerschleife checken,
ob im Aufgaben-Ordner Dateien liegen,
dazu Dateien erfassen

- falls ja, dann

  - sleep(5 Minuten)
    # falls noch geschrieben wurde
    
  - Dateien nach und nach
    ins Wiki schaufeln
    
  - Erledigte Dateien nach
    pfad_erledigt schaufeln
    in einen Unterordner
    
  - loggen (?)     ### Nicht implementiert


- falls nein, dann
  - sleep(60 Minuten)
  
'''

# Settings
pfad_aufgaben = "C:/Users/.../_Eingang"
pfad_erledigt = "C:/Users/.../_Ablage"



# Funktionen

def dateien_erfassen(pfad):
    # Erfasst alle Dateien in einem gegebenen Pfad
    files = os.listdir(pfad)
    files.sort()
    return files



def datei_verschieben(file, src, dst):
    # Verschiebt eine Datei in
    # ein Zielverzeichnis
    
    # Heutiges Datum
    datum = datum_erhalten()

    # Quell- und Zieldatei sowie
    # Zielverzeichnis festlegen
    quelldatei = src + "/" + file
    zieldatei  = dst + "/" + datum + "/" + file
    zielverzeichnis  = dst + "/" + datum

    # Zielverzeichnis anlegen,
    # falls es noch nicht existiert
    if os.path.isdir(zielverzeichnis) == True:
        pass
    else:
        os.mkdir(zielverzeichnis)

    shutil.move(quelldatei, zieldatei)



def datum_erhalten():
    # Liefert das heutige Datum
    # in der Form JJJJ-MM-DD zurück
    datum = time.strftime("%Y-%m-%d")
    return datum



# Main-Schleife
def main(pfad_aufgaben, pfad_erledigt):
    while True:

        files = dateien_erfassen(pfad_aufgaben)

        if len(files) == 0:
            
            print "Keine Arbeit..."
            # Mache weiter nach einer Wartezeit
            time.sleep(10 * 60) # 60 * 60 seconds

        else:
            # Warte kurz, falls noch geschrieben wird
            print "Arbeitsbeginn..."
            
            time.sleep(1 * 60)

        
            for file in files:
                
                print "Verarbeite: " + file
                
                with codecs.open(pfad_aufgaben + "/" + file,"r",'iso-8859-1') as f:
                    text = f.read()
                
                site = pywikibot.Site()
                page = pywikibot.Page(site, sitename)

                page.text = text
                page.save(u"Bot")

                datei_verschieben(file, pfad_aufgaben, pfad_erledigt)


if __name__ == "__main__":
    
    try:
        main(pfad_aufgaben, pfad_erledigt)
    except:
        var = traceback.format_exc()
        var += "\n - - - - - - \n\n"

        dateiname = "ausgabe " + datum_erhalten() + ".txt"
        with open(dateiname,"a") as f:
            f.write(var)
        
        print "Unexpected error:", sys.exc_info()[0]

        raise
    
    finally:

        print "OK"
Ich habe den Verdacht, dass vielleicht die files-Liste Schuld sein könnte, dass sie nicht geleert wird/wurde!? pywikibot ist ein Fremdmodul, denkbar wäre, dass ich das mal rauswerfe und schaue, ob der Fehler dann immer noch auftritt (noch nicht gemacht!).

[1] Im ersten Schritt wäre ich aber für eine Info zum Fehler "IOError: [Errno 9] Bad file descriptor" dankbar. Wann passiert so etwas!?

[2] BTW: Feedback zum Quellcode gerne, es wird allerdings etwas dauern, bis ich mich darum ggf. kümmern kann. Ich habe gerade einige andere Sachen, die ich programmieren muss, da ist viel Work-in-Progress (und hoffentlich läuft es erst einmal).
Benutzeravatar
pixewakb
User
Beiträge: 1412
Registriert: Sonntag 24. April 2011, 19:43

PS Die Kommentare sind teilweise nicht mehr auf dem aktuellen Stand, was aber v. a. Zeiträume usw. betrifft. Das korrigiere ich mal, wenn ich das Tool mal aufräume.
BlackJack

@pixewakb: Ein kompletter Traceback wäre nett, damit man nicht raten muss *wo* in dem Programm der Fehler auftritt.
Benutzeravatar
pixewakb
User
Beiträge: 1412
Registriert: Sonntag 24. April 2011, 19:43

Sorry.

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Users\...\bot.pyw", line 163, in <module>
    main(pfad_aufgaben, pfad_erledigt)
  File "C:\Users\...\bot.pyw", line 111, in main
    print "Keine Arbeit..."
IOError: [Errno 9] Bad file descriptor
Ich habe etliche Kommentarzeilen, u. a. zu Version und Autor herausgenommen, so dass die Zeilennummern hier nicht mit den Zeilennummern im Traceback übereinstimmen.
BlackJack

@pixewakb: Wie führst Du das denn aus? Siehst Du irgendeine ``print``-Ausgabe? Kann es sein dass der Prozess keine beziehungsweise eine geschlossene Standardausgabe hat und deswegen ``print`` fehlschlägt?
Benutzeravatar
pixewakb
User
Beiträge: 1412
Registriert: Sonntag 24. April 2011, 19:43

Es handelt sich um eine pyw-Datei, die morgens über den Autostart von Windows gestartet wird und unter Python 2.7 läuft.
@pixewakb: Wie führst Du das denn aus? Siehst Du irgendeine ``print``-Ausgabe? Kann es sein dass der Prozess keine beziehungsweise eine geschlossene Standardausgabe hat und deswegen ``print`` fehlschlägt?
Was genau meinst du mit geschlossene Standardausgabe!? Ich bin von Hause aus kein Programmierer, d. h. die Hintergründe habe ich nicht, weshalb mir auch Prozess nicht viel sagt.
BlackJack

@pixewakb: Der Prozess ist eine konkrete Ausführung Deines Programms die vom Betriebssystem verwaltet wird. Jeder Prozess hat eine Standardausgabe, das ist eine Datei wo ”normale” Textausgaben hingeschrieben werden, also bei Python zum Beispiel das was mit ``print`` ausgegeben wird. Beziehungsweise hat nicht jeder Prozess eine Standardausgabe — unter Windows zum Beispiel wird zwischen Konsolen- und Fensterprogrammen unterschieden und Fensterprogramme haben keine beziehungsweise eine geschlossene Standardausgabe. Versuche dort Daten hin zu schreiben, zum Beispiel mit ``print``, führen zu der Ausnahme die Du bekommst.
Benutzeravatar
pixewakb
User
Beiträge: 1412
Registriert: Sonntag 24. April 2011, 19:43

D. h. ich müsste die print-Ausgaben aus einem Konsolenprogramm herausnehmen, das ich als pyw-Datei im Hintergrund laufen lassen will. Das ist mir noch nicht aufgefallen, dass das ein Problem ist.
BlackJack

@pixewakb: Es ist dann ja kein Konsolenprogramm. `logging` ist da immer eine interessante Alternative weil man die Ausgaben bei Bedarf einfach ausschalten oder in eine Datei umleiten kann. Da muss man sich dann für's protokollieren von Ausnahmen auch nicht selber etwas basteln.
Benutzeravatar
pixewakb
User
Beiträge: 1412
Registriert: Sonntag 24. April 2011, 19:43

Ich habe mal ein Testskript geschrieben, bei dem der Fehler nicht auftritt, wenn ich das Skript (Python 3.4) mittels Doppelklick starte. Ich habe gerade mal eine Zeile ergänzt, dass es als Python 2.7 startet und dort gibt es auch keine Probleme!? Ich hätte jetzt gedacht, dass das Skript ein identisches Verhalten zeigt. BTW: Könnte es sein, dass eines der obigen Module irgendwie dazwischenfunkt!? Denke auch an pywikibot, weil das Fremdsoftware ist. Und leider noch eine Frage: Könnte es auftreten, weil es über eine Liste läuft!?

Code: Alles auswählen

try:
    a = 0
    for datei in range(1,10):
        a = a + datei
        filename = str(datei) + ".txt"
        with open(filename,"w") as g:
            g.write(str(a))
        print(datei)
except:
    print("Fehler!")
    with open("__report.txt", "w") as f:
        f.write("Fehler")
Das Modul zum Loggen werde ich mir mittelfristig ansehen (wollen), aber im Moment kriege ich das nicht unter.
BlackJack

@pixewakb: Die Frage ist ja immer mit was die Standardausgabe verbunden ist. Es kann durchaus sein dass es einen Unterschied macht ob das der Windowsdesktop startet oder ob das über den Autostart gestartet wird.
Benutzeravatar
pixewakb
User
Beiträge: 1412
Registriert: Sonntag 24. April 2011, 19:43

Danke! Wäre nicht darauf gekommen! Ich schaue mal, dass ich das umstricke.
BlackJack

Habe noch gar keine Anmerkungen zum Quelltext losgelassen. :-) Aaalsooo:

Die Zeichenkette mit der Erklärung was das Modul macht muss über die Importe damit daraus ein Docstring wird.

Namen von Konstanten werden per Konvention komplett in Grossbuchstaben geschrieben.

Auf Modulebene steht zu viel Hauptprogramm. Auch wenn die Ausführung durch das ``if __name__ == '__main__':``-Idiom geschützt ist, so werden dort wenn es ausgeführt wird Namen von Variablen auf Modulebene erzeugt.

Datumskram mache ich lieber mit dem `datetime`-Modul statt mit den Low-Level-C-ähnlichen Funktionen aus dem `time`-Modul.

Werte und Zeichenketten mit ``+`` zusammenzusetzen ist eher BASIC als Python. In Python gibt es dafür die `format()`-Methode. Für Pfadteile gibt es `os.path.join()`.

`file` und `files` in der Hauptschleife sind eher `filename` und `filenames`. `file` ist ausserdem ungünstig weil so der Dateityp heisst.

`sitename` wird benutzt aber nicht definiert‽

Der Test auf Existenz eines Verzeichnisses bevor man es anlegt ist nicht sicher weil zwischen dem Test und dem Anlegen das Verzeichnis angelegt werden könnte. Man kann aber einfach das Verzeichnis anlegen und den Fehlerfall ignorieren falls es schon existierte.

Ich komme dann ungefähr bei so etwas heraus (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Was soll das Tool machen?
 
In Dauerschleife checken,
ob im Aufgaben-Ordner Dateien liegen,
dazu Dateien erfassen
 
- falls ja, dann
 
 - sleep(5 Minuten)
   # falls noch geschrieben wurde
   
 - Dateien nach und nach
   ins Wiki schaufeln
   
 - Erledigte Dateien nach
   pfad_erledigt schaufeln
   in einen Unterordner
   
 - loggen (?)     ### Nicht implementiert
 
- falls nein, dann
 
 - sleep(60 Minuten)

"""
import codecs
import errno
import os
import shutil
import time
from datetime import date as Date
from traceback import format_exc

from pywikibot import Page, Site

TODOS_PATH = 'C:/Users/.../_Eingang'
DONE_PATH = 'C:/Users/.../_Ablage'
DATE_FORMAT = '%Y-%m-%d'


def format_today():
    return format(Date.today(), DATE_FORMAT)


def get_sorted_filenames(path):
    filenames = os.listdir(path)
    filenames.sort()
    return filenames


def move_file(source_path, destination_path):
    destination_path = os.path.join(destination_path, format_today())
    try:
        os.mkdir(destination_path)
    except OSError as error:
        if error.errno != errno.EEXIST:
            raise
    shutil.move(source_path, destination_path)


def main_loop(todos_path, done_path):
    while True:
        filenames = get_sorted_filenames(todos_path)
        message, delay = (
            ('Arbeitsbeginn...', 60) if filenames else ('Keine Arbeit...', 600)
        )
        print message
        time.sleep(delay)
        for filename in filenames:
            print 'Verarbeite: ', filename
            full_path = os.path.join(todos_path, filename)
            with codecs.open(full_path, 'r', 'iso-8859-1') as todo_file:
                text = todo_file.read()
            page = Page(Site(), 'sitename')
            page.text = text
            page.save(u'Bot')
            move_file(full_path, done_path)


def main():
    try:
        main_loop(TODOS_PATH, DONE_PATH)
    except:
        traceback = format_exc() + '\n - - - - - - \n\n'
        with open('ausgabe_{0}.txt'.format(format_today()), 'a') as log_file:
            log_file.write(traceback)
        raise


if __name__ == "__main__":
    main()
Antworten