FileDB

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
BlackJack

@deadshox: Wenn Du hinter den Logging-Aufruf nicht noch ein ``raise`` setzt, dann kannst Du Dir das ``except`` vielleicht wirklich sparen. Die Frage ist doch an jeder Stelle, ob Du die Ausnahme überhaupt ”verschlucken” solltest. In dem Beispiel hat irgendetwas beim Laden/Parsen der Datei nicht funtkioniert. Das Programm würde aber weitermachen, als wäre nichts passiert. Sollte es das tun? Denn danach wird ja sicher mit den Daten irgendetwas gemacht. Also mit den Daten die nicht geladen werden konnten — also wird es dort entweder wieder eine Ausnahme geben, oder ein falsches Ergebnis.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

deadshox hat geschrieben:Also dann eher so etwas wie:

Code: Alles auswählen

try:
    with open(self._bin, 'rb') as handle
        int("spam")
        self._data = pickle.load(handle)
except Exeption, e:
    logging.error('Error: ' % e)
Wenn nicht, kannst du etwas genauer werden? Wenn es oberes nicht ist, kann ich mir ja das try:except gleich sparen.
`Exception` fängt noch immer alle Fehler ab, du muss schon prüfen, welche Fehler genau auftreten können. Dazu hilft ein Blick in die Dokumentation zu `open`. Die mögliche Exception ist `IOError`, also behandelst du genau diese:

Code: Alles auswählen

try:
    with open(self._bin, 'rb') as handle
        int("spam")
        self._data = pickle.load(handle)
except IOError:
    logging.error("Could not open file: %s" % self._bin)
Der `ValueError` läuft dann irgendwo anders auf und du bekommst den Fehler noch mit.
Das Leben ist wie ein Tennisball.
Benutzeravatar
lynadge
User
Beiträge: 112
Registriert: Sonntag 4. April 2010, 10:17

@blackjack, in der _load Methode macht es mMn Sinn, da, wenn die Datei nicht existiert, sie angelegt wird. Da muss ja nichts abgebrochen werden.

@EyDu, wäre Variante 1 nicht einfacher, da man die Fehlermeldung dort einfach nur zum Loggen durch reicht? Denn eine direkte Ausgabe wäre für mich persönlich nicht nötig. Evtl könnte man noch ein 'print' setzen für die Konsole. Jeden möglichen Fehler abfangen würde ich irgendwie als zu aufwendig empfinden.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Einfacher vielleicht, aber unter umständen auch vollkommen falsch. Angenommen, du möchtest zwei Werte (in diesem etwas seltsamen Szenario) vertauschen. Leider verschreibst du dich ein wenig:

Code: Alles auswählen

>>> a, b = 42, 23
>>> try:
...     a, b = beh, ah
... except:
...     pass
... 
>>> a, b                                                                                                             
(42, 23)
Ganz offensichtlich sind `ah` und `beh` nicht vorhanden, da du alle Fehler behandelst, wird dir dies aber nicht auffallen. Das Programm läuft gemütlilch weiter, obwohl hier ein ganz offensichtlicher Fehler vorhanden ist. Vielleicht irgendwann später im Verlauf des Programms gibt es einen Folgefehler, was eine Fehlersuche unglaublich anstregend machen kann. Da ist es natürlich schön, wenn du den Fehler geloggt hast, dir wird das Problem aber unter Umständen gar nicht erst aufflallen (je nach Umfang des Logs). Du verwirfst damit also nicht nur Fehler von außen, sondern möglicherweise auch Programmierfehler.
Das Leben ist wie ein Tennisball.
Benutzeravatar
lynadge
User
Beiträge: 112
Registriert: Sonntag 4. April 2010, 10:17

Ich hoffe ich verstehe dich nicht falsch aber ein:

Code: Alles auswählen

>>> a, b = 42, 23
>>> try:
...     a, b = beh, ah
... except Exception, e:
...     print e
... 
name 'beh' is not defined
Sagt mir doch an, was falsch gemacht wurde.

Es ist ja im Endeffekt nichts anderes als:

Code: Alles auswählen

>>> a, b = 42, 23
>>> a, b = beh, ah
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'beh' is not defined
Nur das es mehr oder weniger abgefangen wurde.

Vielleicht hätte ich noch dazu schreiben sollen das es mehr mein Ziel ist, ein reibungslosen Programmablauf zu erreichen, als eine eine Programm zu haben was mit Fehlermeldungen um sich wirft, wenn es welche gibt.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

deadshox hat geschrieben:Nur das es mehr oder weniger abgefangen wurde.
Merkst du nicht das Problem bei "mehr oder weniger"? Du weisst nämlich nicht, weche Exceptions du möglicherweise noch alles abfängst. Nur weil du sie bemerkst heißt es nicht, dass es eine ordentliche Behandlung ist. Den Fehler in ein Log zu schreiben ist nur dann eine vernünftige Lösung wenn man garantieren kann, dass der Zustands des Programms danach *immer* gültig ist. Dadurch, dass du einfach alle Fehler wegwirfst baust du unter Umständen ganz neue ein. Ich versuche es noch einmal mit einem komplexeren Beispiel:

Code: Alles auswählen

>>> def bubblesort(data):
...     n = len(data)
...     while True:
...         switched = False
...         for i in range(n-1):
...             if data[i] > data[i+1]:
...                 try:
...                     data[i], data[i+1] = dataasdf[i+1], dataasd[i]
...                     switched = True
...                 except Exception, e:
...                     pass
...         n = n - 1
...         if not (n and switched):
...             break
... 
>>> def stupid_min(data):
...     data = data[:]
...     bubblesort(data)
...     return data[0]
... 
>>> import random
>>> data = range(10)
>>> random.shuffle(data)
>>> data
[9, 8, 1, 3, 2, 4, 6, 0, 7, 5]
>>> stupid_min(data)
9
Ganz offensichtlich gibt es oben wieder zwei `NameErrors` und es werden alle Exceptions abgefangen (oder in ein Log geschrieben, was hier kein Unterschied macht). `stupid_min` verwendet nun Bubblesort um das Minimum zu bestimmen. Da beim Sortieren kein Fehler geworfen wird, macht `stupid_min` einfach mit den falschen Daten weiter. Ganz offensichtlich ist das Programm zu diesem Zeitpunkt in keinem gültigen Zustand mehr. Statt dem Minimum könnte hier natürlich alles mögliche schief gehen, wie das ungewollte Überschreiben von Dateien, unbeabsichtigen Operationen in der Datenbank und was man sich noch alles Vorstellen kann.

Durch wildes Abfangen aller Fehler machst du dein Programm im Allgemeinen nicht fehlertoleranter, sondern baust noch mehr ein. Wenn es irgendwo im Programm unterwartet zu Problemen kommt, dann muss es krachen, da du über den Zustand des Programms keine Aussage mehr treffen kannst. Es funkioniert dann ggf. nur noch weil du Glück hattest und das ist ganz sicher kein Verhalten welches du dir wünscht.
Das Leben ist wie ein Tennisball.
Benutzeravatar
lynadge
User
Beiträge: 112
Registriert: Sonntag 4. April 2010, 10:17

In der Hoffnung dich nun verstanden zu haben, habe einmal die _load() Methode ausgebaut. :)

Code: Alles auswählen

def _load(self):
    """Load data from file."""

    try:
        with open(self._bin, 'rb') as handle
            self.data = pickle.load(handle)
    except IOError as e:
        if e.errno == 2:
            logging.warning('Try to create file, given was not found. %s'\
                            % self._bin)
            open(self._bin, 'w').close()
        elif e.errno == 13:
            logging.error('Permission denied. %s' % self._bin)
Durch diese Variante konnte ich auch die Methode 'check()' löschen, da sie nun überflüssig ist.

Bin ich auf dem richtigen Weg? ;)
BlackJack

@deadshox: Vorausgesetzt, dass es in Ordnung ist, wenn die Daten nicht geladen werden ohne dass der Aufrufer das mitbekommt und `self.data` auch nach dem Aufruf noch an die alten Daten gebunden ist, könnte man das so machen.

Die Zahlen für die Fehlernummer würde ich durch Konstanten aus `errno` ersetzen. Dann ist der Quelltext (etwas) verständlicher und vor allem auch plattformunabhängiger. In diesem Falle also `errno.ENOENT` und `errno.EACCESS`.
Benutzeravatar
lynadge
User
Beiträge: 112
Registriert: Sonntag 4. April 2010, 10:17

Ok. Das würde dann also wie folgt aussehen.

Code: Alles auswählen

import errno

def _load(self):
    """Load data from file."""

    try:
        with open(self._bin, 'rb') as handle
            self.data = pickle.load(handle)
    except IOError as e:
        if e.errno == errno.ENOENT:
            logging.warning('Try to create file, given was not found. {0}'\
                            .formatself._bin))
            open(self._bin, 'w').close()
        elif e.errno == errno.EACCES:
            logging.error('Permission denied to given file. {0}'\
                          .format(self._bin))

        self.data = {}
Der 'import' sitzt jetzt nur zum zeigen da und wird sonst im Kopf des Modules stehen.
Benutzeravatar
lynadge
User
Beiträge: 112
Registriert: Sonntag 4. April 2010, 10:17

Ich habe mal eine überarbeitete Version hoch geladen.

https://github.com/deadshox/FileDB/tree/0.4

Ich hoffe ich konnte nun alle von euch genannten Punkte mit einbauen und bin auf dem Weg das diese kleine Lib halbwegs brauchbar ist. :)
Benutzeravatar
lynadge
User
Beiträge: 112
Registriert: Sonntag 4. April 2010, 10:17

Neue Version 0.5.

https://github.com/deadshox/FileDB/tree/0.5

Dort gibt es nun eine reload() Methode, diese schaut anhand einer Checksumme ob die Daten neu geladen werden müssen oder nicht. Für diese Methode dazugekommen ist auch die get_checksum() Methode.

Um sich dann noch eine ID erstellen zu können, habe ich noch create_id() hinzugefügt, diese Methode erstellt aus einem Integer einen Hex wert, den man dann als ID nutzen kann. Z.b. könnte der Integer ein Timestamp sein.

:)
Antworten