ANNOUNCE: xmlpickle.py 0.1
John Wiegley
johnw at gnu.org
Wed Jul 26 19:59:00 EDT 2000
This is a very poor man's XML pickling scheme, but I was unable to
find anything else.
It also provides a way of doing disk mirroring of in-memory objects.
That is, the object is represented both in memory, as a Python object,
and on disk, as an XML data file. Whichever one is changed first, the
other will be updated appropriately on the next access to the object.
To use this behavior, all that's necessary is:
import xmlpickle
class Derived(xmlpickle.Persistent):
def __init__(self, name):
xmlpickle.Persistent.__init__(self, name + ".xml")
All changes to Derived objects will now be mirrored to name.xml files,
which, if changed on disk, will alter their in-memory counterparts the
next time someone accesses them.
-----[ file: xmlpickle.py ]--------------------------------------------
import os
import os.path
import sys
import time
import types
import string
from xml.sax import saxexts
from xml.dom.core import TEXT_NODE
from xml.dom.builder import Builder
from xml.dom.sax_builder import SaxBuilder
class Renderable:
def first_nontext_child(self, node, count = 0):
for child in node.childNodes:
if child.nodeType != TEXT_NODE:
if count:
count = count - 1
else:
return child
def input_data(self, node):
if node.nodeName == 'string':
text = ""
for child in node.childNodes:
text = text + child.nodeValue
return text
elif node.nodeName == 'int':
text = ""
for child in node.childNodes:
text = text + child.nodeValue
return int(string.strip(text))
elif node.nodeName == 'list':
newlist = []
for item in node.childNodes:
if item.nodeName != 'listitem':
continue
value = self.input_data(self.first_nontext_child(item))
newlist.append(value)
return newlist
elif node.nodeName == 'tuple':
newtuple = ()
for item in node.childNodes:
if item.nodeName != 'tupleitem':
continue
value = self.input_data(self.first_nontext_child(item))
newtuple = newtuple + (value,)
return newtuple
elif node.nodeName == 'hash':
newhash = {}
for item in node.childNodes:
if item.nodeName != 'hashitem':
continue
hi = self.first_nontext_child(item)
hk = self.first_nontext_child(hi)
key = self.input_data(hk)
hi = self.first_nontext_child(item, 1)
hv = self.first_nontext_child(hi)
value = self.input_data(hv)
newhash[key] = value
return newhash
elif node.nodeName == 'type':
obj = eval(node.attributes['name'])()
return self.input_data(obj, node)
else:
pass # ignore tag
def input_object(self, obj, node):
for child in node.childNodes:
if child.nodeName != 'property':
continue
data = self.input_data(self.first_nontext_child(child))
obj.__dict__[child.attributes['name'].value] = data
def read_from_xml(self, file):
"Read in the XML description of the project."
# Create a SAX parser and a SaxBuilder instance
p = saxexts.make_parser()
dh = SaxBuilder()
p.setDocumentHandler(dh)
# Parse the input, and close the parser
data = open(file, "r")
p.parseFile(data)
p.close()
data.close()
# Retrieve the DOM tree
doc = dh.document
return self.input_object(self, doc.documentElement)
def output_data(self, builder, value, depth):
vtype = type(value)
if vtype == types.InstanceType:
# Recursively process subobjects
self.do_convert_to_dom(builder, value, depth)
elif vtype == types.StringType:
builder.text("\n" + (" " * depth))
builder.startElement('string')
builder.text(value)
builder.endElement('string')
elif vtype == types.IntType:
builder.text("\n" + (" " * depth))
builder.startElement('int')
builder.text(str(value))
builder.endElement('int')
elif vtype == types.ListType:
builder.text("\n" + (" " * depth))
builder.startElement('list')
for member in value:
builder.text("\n" + (" " * (depth + 2)))
builder.startElement('listitem')
self.output_data(builder, member, depth + 4)
builder.text("\n" + (" " * (depth + 2)))
builder.endElement('listitem')
builder.text("\n" + (" " * depth))
builder.endElement('list')
elif vtype == types.TupleType:
builder.text("\n" + (" " * depth))
builder.startElement('tuple')
for member in value:
builder.text("\n" + (" " * (depth + 2)))
builder.startElement('tupleitem')
self.output_data(builder, member, depth + 4)
builder.text("\n" + (" " * (depth + 2)))
builder.endElement('tupleitem')
builder.text("\n" + (" " * depth))
builder.endElement('tuple')
elif vtype == types.DictType:
builder.text("\n" + (" " * depth))
builder.startElement('hash')
for key, value in value.items():
builder.text("\n" + (" " * (depth + 2)))
builder.startElement('hashitem')
builder.text("\n" + (" " * (depth + 4)))
builder.startElement('hashkey')
self.output_data(builder, key, depth + 6)
builder.text("\n" + (" " * (depth + 4)))
builder.endElement('hashkey')
builder.text("\n" + (" " * (depth + 4)))
builder.startElement('hashvalue')
self.output_data(builder, value, depth + 6)
builder.text("\n" + (" " * (depth + 4)))
builder.endElement('hashvalue')
builder.text("\n" + (" " * (depth + 2)))
builder.endElement('hashitem')
builder.text("\n" + (" " * depth))
builder.endElement('hash')
def do_convert_to_dom(self, builder, obj, depth = 2):
builder.startElement('type', {"name": obj.__class__.__name__})
L = obj.__dict__.keys()
L.sort()
for attr in L:
if attr[:1] == '__' or attr[:3] == "_v_":
continue
value = getattr(obj, attr)
builder.text('\n' + (" " * depth))
builder.startElement('property', {"name": attr})
self.output_data(builder, value, depth + 2)
builder.text('\n' + (" " * depth))
builder.endElement('property')
builder.text('\n')
builder.endElement('type')
def write_to_xml(self, file):
builder = Builder()
self.do_convert_to_dom(builder, self)
data = open(file, "w")
data.write(builder.document.toxml())
data.close()
class Persistent(Renderable):
def __init__(self, filename):
self.__dict__['_v_filename'] = filename
self._revert_object(force = 1)
def _revert_object(self, force = 0):
"""Check the XML representation of this object.
If the memory resident copy is out of date, read it from disk
again."""
if not self.__dict__.has_key('_v_filename'):
return
if not os.path.isfile(self.__dict__['_v_filename']):
return
mtime = os.stat(self.__dict__['_v_filename'])[8]
if force or mtime > self.__dict__['_v_timestamp']:
self.read_from_xml(self.__dict__['_v_filename'])
self.__dict__['_v_timestamp'] = time.time()
def _save_project(self):
if not self.__dict__.has_key('_v_filename'):
return
self.write_to_xml(self._v_filename)
self.__dict__['_v_timestamp'] = time.time()
def __setattr__(self, prop, value):
self.__dict__[prop] = value
self._save_project()
def __getattr__(self, prop):
self._revert_object()
return self.__dict__[prop]
def __hasattr__(self, prop):
self._revert_object()
return self.__dict__.has_key(prop)
More information about the Python-list
mailing list