XML_Objects - Tester und Kritiker gesucht

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

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]#!/usr/bin/env python
import this[/code]
joerg
User
Beiträge: 188
Registriert: Samstag 17. August 2002, 17:48
Wohnort: Berlin
Kontaktdaten:

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
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Hi Jörg,

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


Gruß

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

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. :)
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

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

XML_Objects.py


Gruß

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

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("&","&amp").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
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

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]#!/usr/bin/env python
import this[/code]
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

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...
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

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]#!/usr/bin/env python
import this[/code]
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

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]#!/usr/bin/env python
import this[/code]
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

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]#!/usr/bin/env python
import this[/code]
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

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]#!/usr/bin/env python
import this[/code]
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

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]#!/usr/bin/env python
import this[/code]
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

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 (former) Modvoice
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

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]#!/usr/bin/env python
import this[/code]
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Ahh, schon wieder was gelernt :) Mit new-style Klassen hatte ich schon immer Probleme...

Aber ist es nicht möglich irgendwelche Klassen mit PyXO zu serialisieren, oder müssen sie dann von PyXO.Object erben? In PyXO_Base64.py zeigst du das das auch mit einer Klasse geht die von str erbt möglich ist, diese aber to_xml() implementieren muss. Heist das also das man damit nur Newstyle Klassen serialisieren kann die to_xml() implementieren (oder eben von PyXO.Object erben)?

Wie schaut es mit der Performance aus, unter http://dezentral.de/soft/XMarshaL/ ist ein Benchmark, in dem PyXO leider fehlt. Ich würde PyXO gerne zum speichern eines mini-VFS nutzen, das wäre ein Dictionary, das alle möglichen Daten enthalten kann (Python Objekte, newstyle, oldstyle). Dazu könnte ich zwar pickle verwenden, aber es als XML abzuspeichern würde mir viel mehr gefallen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

PyXO ist ursprünglich nicht als Serialisierer für irgendwelche Objekte gedacht gewesen. Es ist eher dazu gedacht hierarchische Datenstrukturen in XML zu speichern z.B. Szenen für ein 3D-Programm. Du kannst auch beliebige Oldstyle Objekte speichern, dann musst du aber die Klasse erst bei PyXO registrieren lassen mit einer Funktion die dann das Objekt in XML umwandelt und mit einer Funktion die aus den Daten, die xml liefert wieder ein Objekt erzeugt. Dies ist allerdings nur für besondere Fälle gedacht, wo man eben nicht mit eigenen Klassen arbeitet.

Die Performace dürfte wohl im Bereich von gnosis liegen.


Gruß

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

zum Testen der Performace kannst du ja wie auf der xmarshal Seite gemacht mit a = xrange(100000) eine Liste mit 100000 Integers erzeugen und die mit PyXO.Object.save("test.xml", a) speichern lassen. Das machste dann auch mit den anderen tools und hast so einen einfachen Benchmark.

Dookie
[code]#!/usr/bin/env python
import this[/code]
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Dookie hat geschrieben:zum Testen der Performace kannst du ja wie auf der xmarshal Seite gemacht mit a = xrange(100000) eine Liste mit 100000 Integers erzeugen und die mit PyXO.Object.save("test.xml", a) speichern lassen. Das machste dann auch mit den anderen tools und hast so einen einfachen Benchmark.
Für den Benchmark Trick ist PyXO viel zu schlau, es pickelt nämlich so:

Code: Alles auswählen

<?xml version="1.0" encoding="utf-8"?>
<PyXO del_root="true" id="Root_001" name="xrange.xml" python_module="PyXO">
  <xrange>xrange(100000)</xrange>
</PyXO>
Das geht zweifellos sehr schnell *g*, XMarshaL kann xrange überhaupt nicht pickeln. Ich werde das mal mit time und range versuchen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

:oops: meinte eh range statt xrange, nur beim tippen hab ich xrange schon so verinnerlicht :D


Gruß

Dookie
[code]#!/usr/bin/env python
import this[/code]
Antworten