XML_Objects - Tester und Kritiker gesucht

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Benutzeravatar
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Beitragvon Dookie » Mittwoch 28. Juli 2004, 01:19

Hier mal eine Beispielklasse die Strings als base64 codiert speichert.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
    Modul:          XML_Base64
    Description:    Base64 codet strings to load and store as xml
    Version:        1.0
    Copyright:      2004 by Fritz Cizmarov fritz@sol.at
    Created:        27. Jul. 2004
    Last modified:  28. Jul. 2004
    License:        free
    Requirements:   Python2.3 XML_Objects.py
    Exports:        Base64
"""

import XML_Objects

class Base64(str, XML_Objects.XML_Object):
    """
        Class for saving base64 encoded strings,
        b64 = Base64(<string>) -> return Base64-string
        b64 = Base64(allready_encoded, encoded=True) -> allready encoded
            string convertet to Base64
        b64.base64 is the base64-encoded representation of the string
    """
   
    __slots__ = []
   
    xml_attrs = {"encoded" : bool}

    def base64(self):
        return self.encode("base64")
    base64 = property(base64, doc="base64-encoded representation")

    def __new__(cls, string, encoded=False):
        if encoded:
            if not isinstance(string, basestring):
                raise TypeError("Argument musst be string!")
            return super(Base64, cls).__new__(cls, string.decode("base64"))
        else:
            return super(Base64, cls).__new__(cls, string)
       
    def __repr__(self):
        cls_name = self.__class__.__name__
        return "%s('%s', encoded=True)" % (cls_name, self.base64)
       
    def to_xml(self, indent=0):
        myind1 = XML_Objects.indentstr*indent
        myind2 = myind1+XML_Objects.indentstr
        fmt = '%s<Base64 encoded="true">\n%s%s\n%s</Base64>\n'
        b64 =  self.base64.replace("\n","\n"+myind2).rstrip() # prepare for xml
        return fmt % (myind1, myind2, b64, myind1)


if __name__ == "__main__":
    demostr = "".join([chr(x) for x in xrange(256)]) # String with all Chars
    test = Base64(demostr) # Encode to base64
    XML_Objects.XML_Object.save("base64test.xml", test) # save to *.xml
    neu = XML_Objects.XML_Object.load("base64test.xml") # load from *.xml
    print repr(str(test)) # saved string
    print repr(str(neu))  # reloaded string
    print "Are generated data equal to loaded from XML?", test == neu


hiermit können einfach binäre Daten oder vorformatierter Text base64-codiert in einer XML-Datei gespeichert werden.
Dazu erzeugt man ein Base64 Objekt mit dem String der gespeichert werden soll. Dieses Objekt kann natürlich in ein XML_Container-Objekt eingefügt werden und so dann als Teil einer Datenstruktur gespeichert werden. Und es kann überall verwendet werden, wo sonst ein String-Objekt verwendet werden kann.


Gruß

Dookie

Code: Alles auswählen

#!/usr/bin/env python
import this
joerg
User
Beiträge: 188
Registriert: Samstag 17. August 2002, 17:48
Wohnort: Berlin
Kontaktdaten:

Beitragvon joerg » Donnerstag 29. Juli 2004, 15:06

Hallo Dookie und alle anderen,

ich habe lange gesucht, um einen passenden XML-Serialisierer zu finden. Vielleicht wird XML_Objects ja mein Tool der Wahl, der Ansatz gefällt mir. ;-)

Leider ist bisher die Performance recht schlecht für umfangreichere Objekte. Ich nehme an, daß die Stringaddition hier alles ausbremst, da kann man bestimmt noch mächtig optimieren. Probier mal folgenden Testcode aus, Du wirst staunen (und warten...):

Code: Alles auswählen

from XML_Object import XML_Object
from time import clock
from cStringIO import StringIO

l = 100
for i in range(10):
    o = StringIO()
    l *= 2
    x = range(l)
    s = clock()
    XML_Object.save(o, x)
    print l, clock()-s


Das gleiche Problem hatte ich auch bei xml.marshal.generic (aus PyXML), weshalb ich es etwas umgeschrieben habe. Das Ergebnis (ein Hack, wenig getestet und gar nicht mehr gewartet) ist hier zu finden: http://dezentral.de/soft/XMarshaL/, da gibt es auch einen Geschwindigkeitsvergleich bestehender Lösungen.

Grüße aus Berlin

Jörg
"Sie sind nicht berechtigt, unrechtmäßige Kopien dieses Datenträgers zu erstellen." - Microsoft-Weisheit auf einer CD von MS-VisualC++-6.0
Benutzeravatar
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Beitragvon Dookie » Donnerstag 29. Juli 2004, 15:11

Hi Jörg,

stimmt optimiert ist da noch gar nichts. Ich werd mir das mal anschaun und
StringIO oder Arrays testen.


Gruß

Dookie

Code: Alles auswählen

#!/usr/bin/env python
import this
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Beitragvon Milan » Donnerstag 29. Juli 2004, 16:04

Hi. Ich bin nun nach einigem Testen zum selben Schluss wie joerg gekommen, allerdings bin ich auf base64 Darstellung z.T. angewiesen. Wenn ich nun aber deinen Code ausführen will, bekomme ich diesen Fehler:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Python\PyEC\test.py", line 5, in ?
    class Base64(str, XML_Object):
  File "C:/Python/PyEC\XML_Objects.py", line 65, in __init__
    cls.__xml_classes[tag_name] = (cls, cls.xml_attributes, None)
  File "C:/Python/PyEC\XML_Objects.py", line 49, in xml_attributes
    attrs.update(cls.__slots__)
AttributeError: keys


Es wird versucht, dass leere Attributedict in xml_attributes ducrh die leere Liste __slots__ upzudaten, was den Fehler ergibt. Da gehöhrt ein try - except herum, sonst passieren bei selbstdefinierten Klassen derselbe Fehler, wie hier:

Code: Alles auswählen

{}.update([])

Ansonsten muss ich sagen, ist es ein wirklich brauchbares Modul, ich werd es wohl auch noch einsetzen. :)
Benutzeravatar
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Beitragvon Dookie » Donnerstag 29. Juli 2004, 16:15

Da hast Du wohl noch eine alte Version. In der aktuellen ist der Bug gefixt.

XML_Objects.py


Gruß

Dookie

Code: Alles auswählen

#!/usr/bin/env python
import this
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Beitragvon Milan » Donnerstag 29. Juli 2004, 18:00

Hi. Jetzt hab ich es doch geschafft ein wneig tiefer in den Quellcode einzusteigen... ich hoff mal, er ist nicht schon wieder veraltet :wink:

Ich hab auf jeden Fall zwei Vorschläge zur Verbesserung:
Für die __init__ in XML_Type, die ich folgendermaßen aufbauen würde:

Code: Alles auswählen

    def __init__(cls, name, bases, cdict):
        tag_name = cdict.get("xml_tag_name", name)
        setattr(cls,"xml_tag_name", tag_name)
        if cls.__xml_classes.has_key(tag_name) and cls.__xml_classes[tag_name] is not cls:
            raise TypeError("%s already exists!" % name)
        super(XML_Type, cls).__init__(name, bases, cdict)
        cls.__xml_classes[tag_name] = (cls, cls.xml_attributes, None)
        cls.xml_stack = []

Somit würde mir eine lästige Fehlermeldung beim Erneuten ausführen in IDLE erspaart bleiben, da die Klassen dann bereits registriert sind und er meckert. So wird extra nochmal nachgeprüft, ob nicht dieselbe Klasse registriert wird. Das dürfte kaum Performance kosten (ist ja eigentlich ein Fehler von IDLE), aber Nerven spaaren.

Für das Escapen von "<", ">" und "&" würd ich eher xml.sax.saxutils.escape bzw xml.sax.saxutils.unescape nehmen. Die sind zwar im wesentlichen das gleiche wie bei dir, jedoch kosten die Mehrfachverkettungen von replace ( .replace("&","&").replace(...).replace(...)) gleich 3 mal soviel Speicher, da der Garbage Collector zwischen den Schritten nicht arbeitet.


Sonst hab ich nix weiter zu bemängeln (:wink:), aber es soll ja auch ein erstklassiges Modul werden :P .

mfg, Milan
Benutzeravatar
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Beitragvon Dookie » Donnerstag 29. Juli 2004, 19:46

Hi Milan,

ich arbeite gerade an einer Version, die beim Speichern direkt in ein "filelike" Objekt schreibt, anstatt die Datei in ungezählten Strings aufzubauen.
Zum ersetzen der Sonderzeichen, werde ich dann auch mein Entities.py verwenden. Das Parsen externer Entities (Umlaute u.s.w) ist aber leider etwas schwierig, aber es reicht ja auch die unicodes mit &#x20ac; zu codieren.


Gruß

Dookie

Code: Alles auswählen

#!/usr/bin/env python
import this
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Beitragvon Milan » Donnerstag 29. Juli 2004, 20:42

Hi. Du weißt aber auch, dass in XML fast nur dezimale Entitäten erlaubt sind? Die große Mehrheit der in HTML erlaubten ENtitäten existiert da ja nicht, deswegen wäre es ganz angebracht (wie du schon sagst) nur die Zahlen zu verwenden. Auf dein Modul würde ich dann deswegen eher verzichten...
Benutzeravatar
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Beitragvon Dookie » Donnerstag 29. Juli 2004, 21:23

Hallo nochmal,

ich hab mein Modul jetzt so umgeschreiben, daß es nur die in xml erlaubten Entitäten einsetzt, optional können die Sachen aus htmlentitydefs verwendet werden.

Ich hab jetzt das Modul, daß statt der Strings direkt ins file schreibt lauffähig, der Unterschied mit dem Code von joerg ist schon beachtlich:

hier mal eine Testsitzung:

Code: Alles auswählen

fritz@seneca:~/Python/module$ python XML_Test.py
200 0.0
400 0.02
800 0.03
1600 0.07
3200 0.28
6400 1.52
12800 7.71
25600 32.43
51200 129.76
102400 515.56
fritz@seneca:~/Python/module$ python XMLS_Test.py
200 0.02
400 0.02
800 0.06
1600 0.09
3200 0.21
6400 0.41
12800 0.87
25600 1.78
51200 3.6
102400 7.21


Besonders zu Beachten ist, daß die alte Version für doppelt so viele Objekte viermal so lange braucht. Bei der neuen ist der Anstieg gleich dem Multiplikator der Objekte.


Gruß

Dookie

Code: Alles auswählen

#!/usr/bin/env python
import this
Benutzeravatar
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Beitragvon Dookie » Freitag 30. Juli 2004, 18:30

Hallo,

Eine neue Version von XML_Objects ist online
http://www.boa3d.de/python/modules/XML_Objects.php

Die Dokumentation muss ich erst überarbeiten.

Jetzt werden die Objekte direkt in ein Fileobjekt geschrieben, anstatt die xml-Daten in unzähligen Strings zusammenzupfriemeln.
D.h. es können XML-Daten jetzt auch gestreamt werden und es ist ein starker Geschwindigkeitsgewinn zu spühren.

Bitte verwendet zum Testen auch die neuen Testscripts auf der Seite.


Gruß

Dookie

Code: Alles auswählen

#!/usr/bin/env python
import this
Benutzeravatar
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Beitragvon Dookie » Samstag 31. Juli 2004, 15:53

Jetzt ist die Version 1.2, mit aktualisierter Dokumentation, online.

http://www.boa3d.de/python/modules/XML_Objects.php


Am Interface wird es keine grossen Änderungen mehr geben.


Gruß

Dookie

Code: Alles auswählen

#!/usr/bin/env python
import this
Benutzeravatar
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Beitragvon Dookie » Montag 2. August 2004, 23:59

So jetzt heisst das Modul PyXO und liegt in der Version 1.2.2 vor.
Neben verbessertem Unicodesupport, ist auch ein XML_CData-Objekt dazugekommen.


Gruß

Dookie

Code: Alles auswählen

#!/usr/bin/env python
import this
Benutzeravatar
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

PyXO 1.3

Beitragvon Dookie » Dienstag 3. August 2004, 19:08

Hallo,

gerade hab ich die Version 1.3 von PyXO hochgeladen.
http://www.boa3d.de/python/modules/PyXO.php
Es hat sich doch noch einiges am Interface geändert. Vor allem sind die XML_ vor den Klassennamen weggefallen, jetzt kann auch einfach PyXO als ganzes mit import PyXO importiert werden und die Namen der Klassen und Methoden dann qualifiziert verwendet werden, also als PyXO.Object
Auch die beiden Beispiellistings sind überarbeitet worden.


Gruß

Dookie

Code: Alles auswählen

#!/usr/bin/env python
import this
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

PyXO 1.4

Beitragvon Leonidas » Samstag 6. November 2004, 13:17

Sehr schöne Idee, endlich mal ein Modul zu haben das sowas kann... nur: ich bin irgendwie zu blöd es zu nutzen. Ich habe mir auch XMarshaL angeschaut, das ist zwar sehr nett, aber hat mit dump() Probleme.
Ich wollte diese Klasse serialisieren:

Code: Alles auswählen

class t:
    var = 'saveme'


Das habe ich dann mit PyXO versucht:

Code: Alles auswählen

a = t()
PyXO.Object.save(a, 'xo.xml')
dann kam aber sowas:

Code: Alles auswählen

Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "PyXO.py", line 362, in save
    Root([obj], name=name, del_root = True).to_xml(stream=xml)
  File "PyXO.py", line 439, in to_xml
    stream.write(xml_prolog)
AttributeError: t instance has no attribute 'write'

Das habe ich auch mit einer Klasse versucht die von PyXO.Object erbt, aber da war keine veränderung.

XMarshaL hatte damit keine Probleme... aber PyXO scheint mir irgendwie mächtiger und mehr in Entwicklung, und verlangt kein PyXML. Das Problem mit PyXO ist, das es irgendwie arg kompliziert ist, er wäre schön wenn es noch einen Wrapper gäbe, der die pickle funktionen kopiert, dann könnte man PyXO als drop-in replacement nutzen.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Beitragvon Dookie » Samstag 6. November 2004, 14:33

Hi Leonidas,

Du hast die Parameter vertauscht beim save, erster Parameter ist die Datei oder der Dateiname und der 2. Parameter ist das Objekt.
Klassenvariablen wie in deinem Beispiel werden sowieso nicht gespeichert, da sie ja beim Erzeugen der Klasse sowieso initialisiert werden.
Ein Blick in die Doku sollte klarmachen, daß "Newstyle"-Klassen mit __slots__ verwendet werden sollten wenn du selber Klassen definierst. Hierbei sollte __slots__ ein Dictionary enthalten, welches die Typen der zu xmlisierenden Attribute enthält.

Code: Alles auswählen

import PyXO


class t(PyXO.Object):
    __slots__ = {"var" : str}

    def __init__(self, var="save_me", **kw):
        super(t, self).__init__(**kw)
        self.var = var


a = t()
PyXO.Object.save("xo.xml", a)


Gruß

Dookie

Code: Alles auswählen

#!/usr/bin/env python
import this

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder