0.1 Initial version 0.11 Minor tweaks, and improvements to pyobj_printer(). Added 'keep_containers()' function. 0.2 Grant Munsey pointed out my gaff in allowing ad-hoc contained instances (subtags) to collide with Python names already in use. Fixed by name-mangling ad-hoc classes to form "_XO_klass" corresponding with tag . Attributes still use actual tag name, e.g., >>> py_obj.klass 0.21 Costas Malamas pointed out that creating a template class does not actually *work* to create class behaviors. It is necessary to get this class into the xml_objectify namespace. Generally, this will involve an assignment similar to: xml_objectify._XO_Eggs = otherscope.Eggs A simple example can be found at: http://gnosis.cx/download/xo_test.py 0.30 Costas Malamas proposed the useful improvement of defining __getitem__ behavior for dynamically created child instances. As a result, you can use constructs like: for myegg in spam.egg: print pyobj_printer(myegg) without needing to worry whether spam.egg is a list of instances or a single instance. 0.40 Altered by Kapil Thangavelu to work with the latest version of PyXML 0.61. Mainly syntax changes to reflect PyXML's move to 4DOM. 0.45 Mario Ruggier goaded me to make xml_objectify compatible with Python 2.0 (his intent is presumably described differently :-) ). Always optimistic, I (dqm) hope this will continue working with later PyXML and Python versions. 0.50 Costas Malamas provided a far faster expat-based parser to replace the DOM-based 'pyobj_from_dom()' technique (orders of magnitude, with a better complexity order). However, when using 'ExpatFactory' to produce a 'py_obj', there no longer remains a 'xml_obj._dom' attribute to refer to for element-sequence or other DOM information. As well, 'ExpatFactory' does not collect the 'py_obj._XML' attribute that character- oriented markup might want preserved. Use of the new parser simply requires an extra (named) argument at 'XML_Objectify' initialization, e.g.: xml_obj = XML_Objectify('spam.xml',EXPAT) # or xml_obj = XML_Objectify('spam.xml',DOM) # or xml_obj = XML_Objectify('spam.xml',parser=EXPAT) Conceivably, other parsers could be added in the future (but probably not). The default option is the backward-compatible 'DOM'. 0.51 Minor cleanup of 0.50 changes. Also, gave 'keep_containers()' three states, rather than just two: NEVER: do not store the _XML attribute MAYBE: store _XML if there is char-level markup ALWAYS: keep _XML attribute for every element 0.52 Niggly bug fixes (mostly to Unicode handling, and a few Python 2.0+ enhancements). Definitely requires Python 2.0 now. Looking through old notes, I remembered Costas Malamas' suggestion for an _XO_.__len__() magic method. This enables calls like: poached_eggs = map(poach, spam.egg) raw_eggs = filter(isRaw, spam.egg) whether spam.egg is an object or a list of objects. See 0.30 history for comparison. 0.53 Attribute name mangling modified slightly. Dash in XML tag name now becomes double-underscore as a py_obj attribute name (important for [xml2sql]). 0.54 Added function '_dir()' for legacy behavior of builtin 'dir()' under both Python 2.2 and earlier versions 0.60 Module refactored into gnosis.xml package (along with [xml_pickle]. This is a first pass, and various documentation and test cases should be added later. 0.61 Additional name mangling fixes at the suggestion of Bruno Lienard and Frank McIngvale. Also changed all [string] functions to the string methods (since we already require 2.0+). 0.62 At the suggestion of Arnold Weis, XML_Objectify sources were made a bit more flexible, and now include any "file-like" objects (i.e. cStringIO, urllib, etc). mm/yy: --------- Revisions by date ----------------------------- 06/02: Greg Aumann added caching of dynamic class creation, for a significant speed increase. Reorganized xml_objectify into subpackage directories to fit better with the Gnosis_Utils structure. Should not affect import line for anyone already using the gnosis.xml package. Version numbering now only for 'gnosis' package as a whole. This HISTORY will simply use approximate dates of changes. 07/02: Rene Jager added the option to work directly with an existing DOM tree. This is helpful in contexts-- especially in Jython--where a DOM is already built for you. Usage is: xml_object = XML_Objectify(dom) 08/02: Bob Gibson and Ville Vainio observed that in adding objectification of DOM trees, I failed to rename a variable used in objectifying files. Fixed that. 05/03: At Rene Jagar's request XML_Objectify grew the class attributes expat_args, expat_kwargs to configure the EXPAT parser. This is needed to handle namespaces in XML documents (see test cases). 06/03: Additional check for passed DOM node (has either childNodes or documentElement), suggested by W.McVey. Added heuristic to let users pass in XML(-ish) string at initialization. 07/03: Added _XO_.__repr__ method to make nodes print in a nicer, more compact fashion. Added ._seq attribute to node objects to support structure preserving convenience functions. Specifically, older versions of gnosis.xml.objectify lost information about mixed content and the order of children. E.g., >>> xml = 'Mixed content is good' >>> obj = XO(xml,EXPAT).make_instance() >>> obj.PCDATA, obj.i.PCDATA, obj.b.PCDATA (u'Mixed is', u'content', u'good') We had no way of knowing where inside the and the occur, nor even which child element occurs first. Now we can recover that information: >>> from gnosis.xml.objectify import content, children >>> content(obj) [u'Mixed ', , u' is ', ] >>> children(obj) [, ] Sequence information and convenience methods are NOT SUPPORTED (yet?) for the DOM parser, only for EXPAT! Changed default parser to EXPAT. If you have relied on the special attribute ._XML that the DOM parser attaches to nodes, you will now need to explicitly specify DOM as the parser used. However, the new sequence functions pretty well handle the job pyobj._XML used to do (in a different way). 02/04: Frank von Delft addressed a Geometric slowdown in _objectify.ExpatFactory.__init__() when make_instance() is called many times. Still, rather than: newNode = gnosis.xml.objectify.make_instance('') it is certainly better to use: newNode = gnosis.xml.objectify.createPyObj('TD') ...which avoids the prior issue anyway. Added convenience function addChild(), Silly fixes in pyobj_printer() for Python version oddness For Python 2.3+, need to skip DocumentType nodes in pyobj_from_dom() function. Fixed/improved test cases. 01/05: Adam Feuer fixed two bugs causing leaks in long running processes (file close and expat base class). Created support functions in gnosis.xml.objectify.utils. See gnosis/docs/xml_matters_39.txt for more discussion. - addChild() # moved to utils subpackage - walk_xo() # Recursively traverse the nodes - write_xml() # Serialize an _XO_ object back into XML - XPath() # Find node(s) within an _XO_ object - pyobj_printer() # moved to utils subpackage Significant speedups by miscellaneous refactoring/cleanup.