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
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 µ 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 µ 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: µ</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 µs" constname="PA_BUF_LT_0US"/>
<enum val="1" text="2 µs" constname="PA_BUF_LT_2US"/>
<enum val="2" text="4 µs" constname="PA_BUF_LT_4US"/>
<enum val="3" text="6 µ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

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:
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