Seite 2 von 2

Re: lxml: Alternative Fehlerbehandlung für try

Verfasst: Samstag 13. November 2010, 21:24
von burli
Hyperion hat geschrieben: Wieso wandelst Du nicht immer per

Code: Alles auswählen

unicode(value)
Weil ich Trottel immer "Unicode()" geschrieben habe. Keine Ahnung, wie ich darauf gekommen bin. Ich werd die Tipps auf jeden Fall mal einfließen lassen

Re: lxml: Alternative Fehlerbehandlung für try

Verfasst: Samstag 13. November 2010, 22:39
von burli
Ok, ich hab jetzt mein Programm umgeschrieben. Das Beispiel vom ersten Beitrag sieht jetzt so aus

Code: Alles auswählen

    for module in modules:
        attributes = module.attrib
        text=attributes.get("text", default="").decode("utf-8")
        implements=attributes.get("implements", default="").decode("utf-8")
        name=attributes.get("name", default="").decode("utf-8")

        # Create Module
        Module = model.modules_module(implements=implements, name=name, text=text)
        # Append Module to Device
        Device.module.append(Module)
Ich habe allerdings ein neues Problem, welches ich mir bisher nichtmal ansatzweise erklären kann. Es ist vorher durch die vielen try Blöcke wohl nicht aufgefallen.

Re: lxml: Alternative Fehlerbehandlung für try

Verfasst: Samstag 13. November 2010, 22:51
von burli
Ich hab wohl die Ursache. In den XML Files sind Kommentare, die lxml offensichtlich als reguläre Tags parst

Code: Alles auswählen

<reg name="FUSEBYTE4" offset="0x04" size="1" text="Start-up Configuration">
    <bitfield mask="0x10" name="RSTDISBL" text="External Reset Disable"/>
    <bitfield enum="SUT" mask="0x0C" name="SUT" text="Start-up Time"/>
    <bitfield mask="0x02" name="WDLOCK" text="Watchdog Timer Lock"/>
<!--bitfield name="JTAGEN" mask="0x01" text="JTAG Interface Enable"/-->
</reg>
Wenn ich die Kommentare entferne läuft es fehlerfrei durch
Ist das ein Bug oder muss ich da einen anderen Parser verwenden?

[EDIT] Problem gelöst. Ich muss in der Schleife for bitfield in reg: noch ein .xpath("bitfield") anhängen, damit die Kommentare gefiltert werden

Re: lxml: Alternative Fehlerbehandlung für try

Verfasst: Samstag 13. November 2010, 23:40
von Hyperion
Den Code könnte man einfacher und flexibler schreiben:

Code: Alles auswählen

keys = ["name", "implements", "text"]
for module in modules:
    attributes =  dict(zip(keys, map(module.attrib.get, keys)))
    # Create Module
    my_module = model.modules_module(**attributes)
    # Append Module to Device
    Device.module.append(my_module)
Das decodieren ist doch unnötig, wie wir ja nun wissen (denn in Deiner Module-Klasse wird das eh implizit konvertiert); zudem liegt ja nicht mal "utf-8" vor, sondern nur "ascii". (Ok, "ascii" ist eine Untermenge von "utf-8", dennoch emfpinde ich das als verwirrend)

Vor allem kannst Du daraus eine universelle Funktion basteln, die Dir basierend auf einer key-Liste ein neues Dictionary erstellt, welches die gewünschten Daten enthält.

Man könnte statt einer Liste sogar ein initiales Dictionary bauen und für jeden key ggf. noch eine Funktion angeben, die für die Umwandlung von solchen Sonderfällen (wie vorhin von Dir beschrieben) aufgerufen werden soll.

Re: lxml: Alternative Fehlerbehandlung für try

Verfasst: Samstag 13. November 2010, 23:48
von burli
Wie ich schon erwähnt habe sind auch Zahlen enthalten. Das sieht dann so aus

Code: Alles auswählen

                # Get Reg from Registers
                for reg in list(temp):
                    attributes = reg.attrib
                    size = attr2int(attributes.get("size"))
                    name = attributes.get("name", default = "").decode("utf-8")
                    text = attributes.get("text", default = "").decode("utf-8")
                    offset = attr2int(attributes.get("offset"))
Die Funktion attr2int() wandelt alle vorkommenden Stringvarianten in einen Integer oder None um. An der Stelle komme ich mit deiner Methode leider nicht weiter, sonst hätte ich das schon so gemacht. Ich müsste für jeden Key dann noch hinterlegen, ob es ein String oder Integer werden soll.

Das Dekodieren wäre nicht nötig, aber ich will durchgängig mit utf-8 arbeiten. Wenn ich aber einen <str> der Datenbank übergebe bekomme ich immer Warnungen

Code: Alles auswählen

/usr/local/lib/python2.6/dist-packages/SQLAlchemy-0.6.5-py2.6.egg/sqlalchemy/engine/default.py:506: SAWarning: Unicode type received non-unicode bind param value.
  param.append(processors[key](compiled_params[key]))

Re: lxml: Alternative Fehlerbehandlung für try

Verfasst: Sonntag 14. November 2010, 00:23
von Hyperion
Ok, dann schlage ich diesen Ansatz vor:

Code: Alles auswählen

attributes = {
    "size": attr2int
    "name": unicode,
    "text": unicode,
    "offset": attr2int
}

for module in modules:
    params = {}
    for name, func in attributes:
        try:
            params[name] = func(module.attrib[name])
        except KeyError:
            pass
    # Create Module
    my_module = model.modules_module(**params)
    # Append Module to Device
    Device.module.append(my_module)
Auf diese Weise hast Du eine hohe Flexibilität und hältst Dich auch an DRY.

Re: lxml: Alternative Fehlerbehandlung für try

Verfasst: Sonntag 14. November 2010, 00:29
von burli
Ah, interessanter Ansatz. Danke. Ich knabbere gerade an einem anderen Problem. In den XML Files sind teilweise solche Codes enthalten &#xB5; und lxml scheint die zu interpretieren. Dann geht die Unicode Konvertierung in die Binsen.

Wenn ich das gelöst habe schau ich mir das mal an

Re: lxml: Alternative Fehlerbehandlung für try

Verfasst: Sonntag 14. November 2010, 00:42
von Hyperion
burli hat geschrieben:Ich knabbere gerade an einem anderen Problem. In den XML Files sind teilweise solche Codes enthalten &#xB5; und lxml scheint die zu interpretieren. Dann geht die Unicode Konvertierung in die Binsen.
Also ich habe da keine Probleme:

Code: Alles auswählen

In [174]: xml = u"""<test>Zeichen: &#xB5;</test>"""

In [175]: root = etree.fromstring(xml)

In [176]: print root.text
--------> print(root.text)
Zeichen: µ

In [177]: print type(root.text)
--------> print(type(root.text))
<type 'unicode'>

In [178]: print unicode(root.text)
--------> print(unicode(root.text))
Zeichen: µ

Re: lxml: Alternative Fehlerbehandlung für try

Verfasst: Sonntag 14. November 2010, 00:46
von burli
Ok, die Lösung für das Problem ist etwas rabiat, aber funktioniert

Code: Alles auswählen

text=attributes.get("text", default = "").encode("utf-8").decode("utf-8")

Re: lxml: Alternative Fehlerbehandlung für try

Verfasst: Sonntag 14. November 2010, 00:51
von Hyperion
burli hat geschrieben:Ok, die Lösung für das Problem ist etwas rabiat, aber funktioniert

Code: Alles auswählen

text=attributes.get("text", default = "").encode("utf-8").decode("utf-8")
Hu? Das kann keine Lösung sein!

Code: Alles auswählen

text = attributes.get("text", default = "").encode("utf-8").decode("utf-8")
# ist identisch zu:
text = attributes.get("text", default = "")
# sofern das ein Unicode-Objekt beinhaltet, ansonsten geh auf Nummer sicher:
text = unicode(attributes.get("text", default = ""))

Re: lxml: Alternative Fehlerbehandlung für try

Verfasst: Sonntag 14. November 2010, 01:02
von burli
Hyperion hat geschrieben: Also ich habe da keine Probleme:
& steht aber nicht drin

Code: Alles auswählen

       <enumerator name="PA_BUF_LT_bitf">
          <enum val="0" text="0 &#xB5;s" constname="PA_BUF_LT_0US"/>
          <enum val="1" text="2 &#xB5;s" constname="PA_BUF_LT_2US"/>
          <enum val="2" text="4 &#xB5;s" constname="PA_BUF_LT_4US"/>
          <enum val="3" text="6 &#xB5;s" constname="PA_BUF_LT_6US"/>
        </enumerator>
Außerdem definierst du den String als Unicode. Ich hab mir mal den Typ ausgeben lassen. Es passiert also genau das, was du oben schon festgestellt hast
<type 'str'> PLL_ON (TX_ON)
<type 'str'> RX_AACK_ON
<type 'str'> TX_ARET_ON
<type 'unicode'> 0 µs
<type 'unicode'> 2 µs
<type 'unicode'> 4 µs
<type 'unicode'> 6 µs
<type 'unicode'> 2 µs
<type 'unicode'> 4 µs
<type 'unicode'> 6 µs
<type 'unicode'> 8 µs
<type 'str'> 3.0 dBm
<type 'str'> 2.8 dBm
<type 'str'> 2.3 dBm
unicode() scheint aber auch zu funktionieren

Re: lxml: Alternative Fehlerbehandlung für try

Verfasst: Sonntag 14. November 2010, 01:10
von Hyperion
K.A. wieso da ein "amp" dazwischengekommen ist :K In Meiner Shell ist es nicht da ;-)

Ob ich meinen String als Unicode deklariere spielt keine Rolle, da die Zeichen eh alle ASCII-Codes sind und zudem beim Parsing Vorgang eh in Unicode (bzw. Bytestrings im ASCII-Bereich, wie am Anfang schon geklärt) gewandelt wird.

Deine "Lösung" ist eben keine, da Du nichts veränderst. Quasi wie 2 +1 - 1 brauch man nicht rechnen, man kann doch gleich bei 2 bleiben ;-)

Re: lxml: Alternative Fehlerbehandlung für try

Verfasst: Sonntag 14. November 2010, 01:36
von burli
Hyperion hat geschrieben:Deine "Lösung" ist eben keine, da Du nichts veränderst. Quasi wie 2 +1 - 1 brauch man nicht rechnen, man kann doch gleich bei 2 bleiben ;-)
Naja, aber sie eliminiert den Fehler und es kommt das gewünschte Ergebnis raus. Als kann es doch nicht ganz 2+1-1 sein. Mal überlegen. Ich bekomme vom XML Parser einen Unicode zurück statt eines ASCII String. Wenn ich .encode("utf-8") darauf anwende erhalte ich durchgehend Bytecode Strings

Code: Alles auswählen

<type 'str'> RX_AACK_ON
<type 'str'> TX_ARET_ON
<type 'str'> 0 µs
<type 'str'> 2 µs
Darauf nochmal .decode("utf-8") und alles passt.

Aber ich hatte ja vorher nur .decode("utf-8"). Also habe ich versucht, einen Unicode in Unicode zu wandeln, aber .decode() erwartet in dem Fall wohl einen Byte String und spuckt daher einen Fehler aus. unicode() macht das anscheinend automatisch.

Zu später Stunde noch was gelernt :D

Re: lxml: Alternative Fehlerbehandlung für try

Verfasst: Sonntag 14. November 2010, 02:25
von Hyperion
burli hat geschrieben: Naja, aber sie eliminiert den Fehler und es kommt das gewünschte Ergebnis raus. Als kann es doch nicht ganz 2+1-1 sein. Mal überlegen. Ich bekomme vom XML Parser einen Unicode zurück statt eines ASCII String. Wenn ich .encode("utf-8") darauf anwende erhalte ich durchgehend Bytecode Strings

Code: Alles auswählen

<type 'str'> RX_AACK_ON
<type 'str'> TX_ARET_ON
<type 'str'> 0 µs
<type 'str'> 2 µs
Darauf nochmal .decode("utf-8") und alles passt.
Fast richtig, denn genau das ist doch das "2 + 1 - 1" ;-) Du kannst diese Operationen def. weglassen und erhältst sofort das gewünschte Ergebnis. Wie ja schon öfter festgestellt, liefert lxml eben nur dann Byte-Strings zurück, wenn diese sich vollständig mittels ASCII codieren lassen. Da ASCII aber für so ziemlich alle Encodings (oder irre ich hier?) den "kleinsten gemeinsamen Nenner" bildet, macht das nichts. Eine implizite Wandlung, etwa durch Konkatenierung mit einem Unicode-String liefert in Python 2.x ja wieder Unicode. Da ASCII zunächst immer als Standard-Codierung angenommen wird (und bei Umstellung auf z.B. "utf-8" ja immer noch dieselben Bytewerte besitzt), funktioniert das ohne Probleme.
burli hat geschrieben: Aber ich hatte ja vorher nur .decode("utf-8"). Also habe ich versucht, einen Unicode in Unicode zu wandeln, aber .decode() erwartet in dem Fall wohl einen Byte String und spuckt daher einen Fehler aus.
Eben! Da lag wohl Dein Problem. Dein Vorgehen war beim Auftreten des Fehlers wohl so:

Code: Alles auswählen

In [186]: u"Hallöle".decode("utf-8")
---------------------------------------------------------------------------
UnicodeEncodeError                        Traceback (most recent call last)

C:\Dokumente und Einstellungen\nelson\Eigene Dateien\<ipython console> in <modul
e>()

C:\Programme\Python27\lib\encodings\utf_8.py in decode(input, errors)
     14
     15 def decode(input, errors='strict'):
---> 16     return codecs.utf_8_decode(input, errors, True)
     17
     18 class IncrementalEncoder(codecs.IncrementalEncoder):

UnicodeEncodeError: 'ascii' codec can't encode character u'\x94' in position 4:
ordinal not in range(128)

In [187]: u"Hallöle".encode("utf-8").decode("utf-")
Out[187]: u'Hall\x94le'
Du hast eben nach Schirtt 186 das "encode()" dazwischengeschaltet, dabei hätte es so sofort geklappt:

Code: Alles auswählen

In [188]: u"Hallöle"
Out[188]: u'Hall\x94le'
burli hat geschrieben: unicode() macht das anscheinend automatisch.
Jein. unicode() ist ja als Basisfunktion dokumentiert. Bei einem Unicode-Objekt liefert unicode exakt dasselbe Objekt zurück:

Code: Alles auswählen

In [183]: u = u"Hallo"

In [184]: unicode(u) == u
Out[184]: True

In [185]: unicode(u) is u
Out[185]: True
Und nun kommt alles zusammen: lxml liefert eben entweder Unicode-Objekte, dann würde der Aufruf von unicode() eben nichts ändern, oder aber lxml liefert Bytestrings in ASCII-Codierung. Diese kann aber eben ohne Angabe eines expliziten Encodings von Python in Unicode umgewandelt werden.

Daher meine "Idee", dein Dictionary zu erstellen, in dem neben den gewünschten Attributnamen auch eine Funktion angegeben wird, die den Inhalt entsprechend aufbereitet. Will man nichts ändern, kann man da eben auf die unicode()-Funktion zurückgreifen und Du hast dann def. Unicode-Objekte in Deinem Zielmodell. (da ja Elixir hier wohl keine implizite Konvertierung vornimmt und es dann zu der Warnung kommt, selbst wenn nur "ASCII"-Bytestrings drin stehen)

Re: lxml: Alternative Fehlerbehandlung für try

Verfasst: Sonntag 14. November 2010, 11:02
von burli
Um das ganze mal abzuschließen: das Problem war nicht, dass der Parser wahlweise ASCII und Unicode zurückgibt. Das Problem war, dass SQLAlchemy eine Warnung ausgibt, wenn man versucht, einen String in ein Unicode Feld zu schreiben. Es funktioniert zwar trotzdem, ist aber unschön.

Da ich anfangs noch nicht wusste, dass der XML Parser je nach Bedarf String oder Unicode ausspuckt und ich im Wiki als erstes auf .decode() gestoßen bin habe ich eben so angefangen. Zusätzlich haben die ganzen try Blöcke viele Fehler vertuscht und da es sich um über 150 XML Dateien mit teilweise über 12000 Zeilen handelt habe ich zum Testen nur eine Handvoll davon rausgesucht, damit ein Durchlauf nicht so lange dauert. Bedauerlicherweise unterscheiden sich die Dateien wie bereits erwähnt an vielen Stellen und ich hab dadurch den einen oder anderen Fehler erst bemerkt, als ich einen Test über alle Dateien durchgeführt habe. Und so kam halt eins zum anderen.

Jedenfalls funktioniert die aktuelle Version ohne try und ohne Warnungen. Jetzt muss ich noch überprüfen, ob wirklich alles an der richtigen Stelle ist. Bisher hab ich nur Stichproben gemacht