Changeset 129
- Timestamp:
- 04/28/02 14:20:57 (6 years ago)
- Files:
-
- trunk/RBFoundation/RBFoundation/XMLBuilder.py (modified) (6 diffs)
- trunk/RBFoundation/RBFoundation/XMLClassBuilder.py (modified) (7 diffs)
- trunk/RBFoundation/RBFoundation/XMLObjectify.py (modified) (13 diffs)
- trunk/RBFoundation/RBFoundation/__init__.py (modified) (1 diff)
- trunk/RBJabber/RBJabber/JabberConnection.py (modified) (2 diffs)
- trunk/RBSkinning/RBSkinning/XMLSkinner.py (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/RBFoundation/RBFoundation/XMLBuilder.py
r115 r129 31 31 ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 32 32 33 """Abstract base classes for building Python object trees from an XML stream. 34 35 Classes: 36 37 XMLBuilderObjectBase 38 XMLBuilderMixin 39 XMLBuilder 40 41 History: 42 43 Please see XMLObjectify's history. 44 """ 45 33 46 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 34 47 #~ Imports 35 48 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 36 49 37 from xml.parsers.expat import ParserCreate 38 from WeakBind import BindCallable 50 from xml.parsers.expat import ParserCreate as _ParserCreate 51 from WeakBind import BindCallable as _BindCallable 39 52 40 53 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ … … 43 56 44 57 class XMLBuilderObjectBase(object): 45 def __init__(self, owner, parent, namespace, node, attributes): 46 pass 47 48 def _addElement(self, namespace, node, object): 49 pass 50 51 def _addData(self, data): 52 pass 53 54 def _xmlInitStarted(self): 55 pass 56 57 def _xmlInitComplete(self): 58 pass 59 60 def _xmlChildFactory(self, owner, parent, namespace, node, attributes): 61 return None 58 """Base class for objects created by XMLBuilderMixin derived classes.""" 59 def __init__(self, owner, parent, namespace, node, attributes): pass 60 def _addElement(self, namespace, node, object): pass 61 def _addData(self, data): pass 62 def _xmlInitStarted(self): pass 63 def _xmlInitComplete(self): pass 64 def _xmlChildFactory(self, owner, parent, namespace, node, attributes): return None 62 65 63 66 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 67 68 class XMLBuilderMixin(object): 69 """Abstract base class that guides the building of python objects from XML. 70 Depends upon the interface defined by XMLBuilderObjectBase.""" 64 71 65 class XMLBuilderMixin(object): 72 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 73 #~ Constants / Variables / Etc. 74 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 75 76 _encoding = 'ASCII' 66 77 _seperator = '.' 67 e_no_class_registered = "No Class Registered" 78 79 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 80 #~ Special 81 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 68 82 69 83 def __init__(self): 70 84 self._elements = [] 71 85 86 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 87 #~ Protected Methods 88 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 89 72 90 def _GetElementFactory(self, owner, element, namespace, node, attributes): 73 raise KeyError, '%s: %s %s' % (self.e_no_class_registered, namespace, node) 91 """Allows a derived class to return an instance factory (a class, since python is so awesome) for 92 this particular data. The template method makes no restrictions, except that the instance factory 93 must be able to accept the same arguments as this method.""" 94 raise KeyError, 'No Class Registered: %s %s' % (namespace, node) 74 95 75 96 def _GetOwner(self): 97 """Used by _start_element to point to the owner of the node. Derived classes should 98 override this method the owner is different than the XMLBuilder""" 76 99 return self 77 100 78 101 def _start_element(self, name, attributes): 102 """Part of the tree-style template method, called at the beginning of an XML node parse. 103 Instantiates the element returned by _GetElementFactory.""" 79 104 idx = name.rfind(self._seperator) 80 105 if idx < 0: … … 84 109 namespace = name[0:idx] 85 110 node = name[idx + len(self._seperator):] 86 87 111 88 112 args = (self._GetOwner(), self._elements and self._elements[-1] or None, namespace, node, attributes) … … 95 119 96 120 def _end_element(self, name): 121 """Part of the tree-style template method, called at the closing of an XML node parse. 122 Simply notifies the element that it is complete.""" 97 123 self._elements[-1]._xmlInitComplete() 98 124 if self._elements: … … 101 127 102 128 def _char_data(self, data): 129 """Part of the tree-style template method, called when PCData is found.""" 103 130 self._elements[-1]._addData(data) 131 132 def _CreateParser(self): 133 """Creates the Expat parser in a python-OO way.""" 134 parser = _ParserCreate(self._encoding, self._seperator) 135 parser.returns_unicode = self._encoding != 'ASCII' and 1 or 0 136 parser.StartElementHandler = _BindCallable(self._start_element) 137 parser.EndElementHandler = _BindCallable(self._end_element) 138 parser.CharacterDataHandler = _BindCallable(self._char_data) 139 return parser 104 140 105 141 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 106 142 107 143 class XMLBuilder(XMLBuilderMixin): 144 """Abstract base class closer to actualizing python object building. 145 See XMLObjectify, or XMLClassBuilder for more concrete builders.""" 146 147 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 148 #~ Special 149 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 150 108 151 def __call__(self, *args, **kw): 152 """Calls ParseFile if the first argument is an open file, and Parse otherwise.""" 109 153 if isinstance(args[0], file): 110 154 return apply(self.ParseFile, args, kw) 111 else: 112 return apply(self.Parse, args, kw) 155 else: return apply(self.Parse, args, kw) 113 156 114 def _end_element(self, name): 115 self._LastCompleteElement = XMLBuilderMixin._end_element(self, name) 116 117 def _PreParse(self): 118 self._elements, self._LastCompleteElement = [], None 119 parser = ParserCreate('ASCII', self._seperator) 120 parser.returns_unicode = 0 121 parser.StartElementHandler = BindCallable(self._start_element) 122 parser.EndElementHandler = BindCallable(self._end_element) 123 parser.CharacterDataHandler = BindCallable(self._char_data) 124 return parser 125 126 def _PostParse(self, parser): 127 result, self._LastCompleteElement = self._LastCompleteElement, None 128 return result 157 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 158 #~ Public Methods 159 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 129 160 130 161 def Parse(self, *args, **kw): 162 """Starts the building of python objects using the XML parser. Assumes first argument is string-like object.""" 131 163 parser= self._PreParse() 132 164 apply(parser.Parse, args, kw) … … 134 166 135 167 def ParseFile(self, *args, **kw): 168 """Starts the building of python objects using the XML parser. Assumes first argument is a file-like object.""" 136 169 parser= self._PreParse() 137 170 apply(parser.ParseFile, args, kw) 138 171 return self._PostParse(parser) 139 172 173 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 174 #~ Protected Methods 175 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 176 177 def _end_element(self, name): 178 """Manages the _LastCompleteElement variable.""" 179 self._LastCompleteElement = XMLBuilderMixin._end_element(self, name) 180 181 def _PreParse(self): 182 """Few initialization things to be done at the beginning of a parse session.""" 183 self._elements, self._LastCompleteElement = [], None 184 return self._CreateParser() 185 186 def _PostParse(self, parser): 187 """Few cleanup things to be done at the end of a parse session.""" 188 result, self._LastCompleteElement = self._LastCompleteElement, None 189 return result 190 trunk/RBFoundation/RBFoundation/XMLClassBuilder.py
r115 r129 31 31 ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 32 32 33 """Concrete classes for building Python object trees where each node comes from a potentially different python module dynamically imported from the python path. 34 35 Classes: 36 37 ModuleByNamespaceMixin 38 ModuleByDictionaryMixin 39 40 XMLClassBuilderMixin 41 42 XMLClassBuilderByNamespace 43 XMLClassBuilderByDictionary 44 XMLClassBuilderByBoth 45 XMLClassBuilder 46 47 History: 48 49 Please see XMLObjectify's history. 50 """ 51 33 52 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 34 53 #~ Imports … … 43 62 44 63 class ModuleByNamespaceMixin(object): 64 """Mixin that forms the python import directly from the namespace and node name.""" 45 65 def _GetModuleClass(self, namespace, node): 66 """Emulates an import path like "from <namespace.node> import <node>" """ 46 67 # Simple construction to convert an XML namespace and node to a python import 47 68 return ('%s.%s' % (namespace, node), node) 48 69 49 # # Another plausable import scheme would be. However, I don't care for the50 ## directory structure that this creates; but, since this is an arbitrary51 ## opinion... You can do whatever you please. =)52 ## return (namespace, node)70 # return (namespace, node) 71 ## The previous is another plausable import scheme. However, 72 ## I don't care for the directory structure that this creates; but, since 73 ## this is an arbitrary opinion... You can do whatever you please. =) 53 74 54 75 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 55 76 56 77 class ModuleByDictionaryMixin(object): 78 """Mixin that forms the python import indirectly from a dictionary lookup of the namespace, 79 and then combines the result with the node name.""" 80 81 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 82 #~ Constants / Variables / Etc. 83 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 84 57 85 ModuleNamespaces = {} 58 86 87 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 88 #~ Public Methods 89 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 90 59 91 def AddModuleNamespaces(self, dictModuleNamespaces): 92 """Registeres a new mapping between xmlns and python import strings.""" 60 93 # Did they send us a real change? 61 94 if dictModuleNamespaces: … … 67 100 self.ModuleNamespaces.update(dictModuleNamespaces) 68 101 102 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 103 #~ Protected Methods 104 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 105 69 106 def _GetModuleClass(self, namespace, node): 107 """Emulates an import path like "from <mapping[namespace]> import <node>". 108 If this lookup fails, the next method in the super line is asked, or an 109 exception is raised.""" 70 110 try: 71 111 # Try returning the entry in the dictionary … … 74 114 try: 75 115 # Try asking the "next" class in line via super 76 self.__super._GetModuleClass116 Method = super(ModuleByDictionaryMixin, self)._GetModuleClass 77 117 except AttributeError: 78 118 # Oops... there didnt seem to be a next class in line … … 80 120 else: 81 121 # Ok, there is a next class in line, and their errors are their own 82 return self.__super._GetModuleClass(namespace, node)122 return Method(namespace, node) 83 123 84 ModuleByDictionaryMixin._ModuleByDictionaryMixin__super = super(ModuleByDictionaryMixin)85 86 124 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 87 125 88 126 class XMLClassBuilderMixin(XMLBuilder.XMLBuilder): 89 seperator = '.' 127 """Base class for building a python object tree from XML where each node comes 128 from a potentially different python module dynamically imported from the python path.""" 129 130 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 131 #~ Constants / Variables / Etc. 132 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 133 90 134 _CachedElementFactories = {} 91 135 136 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 137 #~ Public Methods 138 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 139 140 Build = XMLBuilder.XMLBuilder.Parse 141 BuildFile = XMLBuilder.XMLBuilder.ParseFile 142 143 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 144 #~ Protected Methods 145 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 146 92 147 def _GetElementFactory(self, owner, parent, namespace, node, attributes): 148 """Returns an instance factory. See XMLBuilderMixin._GetElementFactory for more information.""" 93 149 result = None 94 150 if self._elements: … … 105 161 106 162 def _LoadModule(self, tupleModuleClass): 163 """Attempts to use python's dynamic imports to retrieve an instance factory 164 (usually a class) from a python module.""" 107 165 result = self._CachedElementFactories.get(tupleModuleClass, None) 108 166 if not result: … … 116 174 117 175 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 176 #~ Concrete Class Definitions 177 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 118 178 119 179 class XMLClassBuilderByNamespace(ModuleByNamespaceMixin, XMLClassBuilderMixin): 120 Build = XMLClassBuilderMixin.Parse 121 BuildFile = XMLClassBuilderMixin.ParseFile 180 """Combines dynamic imports from XMLClassBuilderMixin with an import style from ModuleByNamespaceMixin""" 181 182 class XMLClassBuilderByDictionary(ModuleByDictionaryMixin, XMLClassBuilderMixin): 183 """Combines dynamic imports from XMLClassBuilderMixin with an import style from ModuleByDictionaryMixin""" 184 185 class XMLClassBuilderByBoth(ModuleByDictionaryMixin, ModuleByNamespaceMixin, XMLClassBuilderMixin): 186 """Combines dynamic imports from XMLClassBuilderMixin with an import style from ModuleByDictionaryMixin, 187 or from XMLClassBuilderByNamespace if the lookup fails.""" 188 189 XMLClassBuilder = XMLClassBuilderByBoth 122 190 123 class XMLClassBuilderByDictionary(ModuleByDictionaryMixin, XMLClassBuilderMixin):124 Build = XMLClassBuilderMixin.Parse125 BuildFile = XMLClassBuilderMixin.ParseFile126 127 class XMLClassBuilderByBoth(ModuleByDictionaryMixin, ModuleByNamespaceMixin, XMLClassBuilderMixin):128 Build = XMLClassBuilderMixin.Parse129 BuildFile = XMLClassBuilderMixin.ParseFile130 131 XMLClassBuilder = XMLClassBuilderByBoth132 trunk/RBFoundation/RBFoundation/XMLObjectify.py
r124 r129 33 33 """Builds a Python object tree from an XML stream. 34 34 35 History 36 37 XMLObjectify found its conceptual roots in a similar tool created by David Mertz <mertz@gnosis.cx> 35 Functions: 36 37 Objectify 38 ObjectifyFile 39 40 Classes: 41 42 ObjectifiedXML 43 Objectifier 44 45 Example: 46 47 from Foundation.XMLObjectify import Objectify 48 49 obj = Objectify('''<stream:stream xmlns:stream="Test Stream Namespace"> 50 <comment>Some generic xml</comment> 51 <comment author="Shane Holloway">Fun with xml!</comment> 52 </stream:stream>''') 53 54 # iteration 55 for each in obj.comment: 56 print 'Author: %-20s' % getattr(each, 'author', '?unknown?'), 57 print each() 58 59 # direct access 60 print obj.comment[1].author 61 62 History: 63 64 XMLObjectify found its conceptual roots in similar tool created by David Mertz <mertz@gnosis.cx> 38 65 named objectify.py, which is still extremely useful and can still be found at http://gnosis.cx . 39 66 Futher history of his module can be found there. Now I say conceptual roots can be found there, 40 because the present state of XMLObjectify represents a 2nd rewrite of that code to serve the new41 requirements.67 because the present state of XMLObjectify represents a 2nd rewrite code, maintaining only the 68 founding ideas, in order to serve new requirements. 42 69 43 70 One new requirement is the ability to "build" a python object tree by instantiating python classes … … 45 72 (Raising that exception is one of the essential reasons for the 2nd rewrite, as I did not factor it 46 73 in the 1st.) This requirement provides the Foundation of the Skinning framework found in the 47 RuneBlade distribution, as well as XMLClassBuilder module 74 RuneBlade distribution, as well as XMLClassBuilder module. 75 76 A second requirement was "the need for speed". In both Mertz's objectify code and my first rewrite, 77 a python object tree can take quite a while to build. (2.2 seconds for 31 k skin file.) This, 78 in part is due to the DOM model provided with python, and partly to creating new python classes 79 on the fly. This second rewrite notes that fact, and uses a prebuilt class (ObjectifiedXML) with 80 different data elements to avoid having to create new classes at run time. This requirement is the 81 corner stone of the RuneBlade Jabber package's XML socket stream. 82 83 However, note that the first and second requirements are pretty much in direct opposition, as 84 dynamic imports will always be slower than using a predefined class that is already in scope. 85 A conflict such as this points us in the direction of refactoring the functionality, allowing 86 for either speedy building of python object trees, or the great flexability of dynamically building 87 that object tree from modules on disk. Hence, a new (pseudo) hierarchy is formed for this: 88 89 + XMLBuilder (Abstract) 90 | 91 +-- XMLObjectify 92 | 93 +-+ XMLClassBuilder 94 | | 95 | +-- Skinning.XMLSkinner 96 | 97 +-- Jabber.Base 48 98 """ 49 99 … … 56 106 57 107 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 58 #~ Classes 108 #~ Classes 59 109 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 60 110 61 111 class ObjectifiedXML(XMLBuilder.XMLBuilderObjectBase): 112 """Represents an objectified XML node and its attributes with links to subnodes and contained PCData. 113 114 Example: 115 116 from Foundation.XMLObjectify import Objectify 117 118 obj = Objectify('''<stream:stream xmlns:stream="Test Stream Namespace"> 119 <comment>Some generic xml</comment> 120 <comment author="Shane Holloway"> 121 Fun with xml! 122 Lots of fun! 123 </comment> 124 </stream:stream>''') 125 126 assert isinstance(obj, ObjectifiedXML) 127 128 # Element Access (note: there are two comments in the example XML 129 assert isinstance(obj.comment, list) 130 assert isinstance(obj.comment[1], ObjectifiedXML) 131 132 # PCData Access 133 assert isinstance(obj.comment[0](''), str) 134 assert isinstance(obj.comment[0](None), list) 135 136 # Attribute Access 137 assert isinstance(obj.comment[1].author, str) 138 139 # Create new element 140 obj._addNewElement(None, 'comment', author='William') 141 assert obj.comment[2].author == 'William' 142 143 # Create new data 144 obj.comment[2]._addData("Some new data") 145 146 # Modify / create new attribute 147 obj.fun = "with XML!" 148 149 # Remove an attribute 150 del obj.fun 151 152 # Remove PCData 153 obj.comment[1]._removeElement(obj.comment[1](None)[-1]) 154 155 # Remove All PCData 156 obj.comment[0]._clearData() 157 158 # Remove an element 159 obj._removeElement(obj.comment[0]) 160 161 # Remove multiple elements 162 del obj.comments 163 """ 164 165 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 166 #~ Constants / Variables / Etc. 167 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 168 62 169 _default_attributes = {} 63 170 171 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 172 #~ Special 173 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 174 64 175 def __init__(self, owner, parent, namespace, node, attributes): 65 176 self.__namespace__ = namespace … … 70 181 71 182 def __getattribute__(self, name): 183 """Allows for the node.attribute or node.subnode semantics""" 72 184 if '_' != name[0]: 73 185 xmlName = name.replace('_', '-') … … 82 194 83 195 def __setattr__(self, name, value): 196 """Allows user to create new attributes, or change values using node.attribute semantics""" 84 197 if '_' != name[0:1]: 85 198 xmlName = name.replace('_', '-') … … 90 203 91 204 def __delattr__(self, name): 205 """Allows for deletion of attributes or subnodes through node.attribute or node.subnode semantics""" 92 206 if '_' != name[0:1]: 93 207 xmlName = name.replace('_', '-') … … 97 211 return 98 212 else: 99 elements = [x [-1]for x in self._elements if x[0][-1] != xmlName]213 elements = [x for x in self._elements if x[0][-1] != xmlName] 100 214 if len(elements) != len(self._elements): 101 215 self._elements = elements … … 106 220 107 221 def __call__(self, joinstr=''): 222 """One way to get the PC data out of the node. See __str__, __int__, __float__, and __complex__. 223 The joinstr argument adjusts how the different lines of PCData are joined, and passing None will 224 signal that the list should not be joined""" 108 225 result = [x[-1] for x in self._elements if not x[0][-1]] 109 226 if joinstr is not None: … … 112 229 113 230 def __str__(self): 231 """Returns the PCData of the node in str format""" 114 232 return self('') 115 233 116 234 def __int__(self): 117 return int(str(self)) 235 """Returns the PCData of the node in int format. Be prepared to catch exceptions in the face of incorrect data. (Or incorrect assumptions ;) )""" 236 return int(self('')) 118 237 119 238 def __long__(self): 120 return long(str(self)) 239 """Returns the PCData of the node in long format. Be prepared to catch exceptions in the face of incorrect data. (Or incorrect assumptions ;) ) 240 Warning: I think long type is being phased out? Not quite sure.""" 241 return long(self('')) 121 242 122 243 def __float__(self): 123 return float(str(self)) 244 """Returns the PCData of the node in float format. Be prepared to catch exceptions in the face of incorrect data. (Or incorrect assumptions ;) )""" 245 return float(self('')) 124 246 125 247 def __complex__(self): 126 return complex(str(self)) 248 """Returns the PCData of the node in complex format. Be prepared to catch exceptions in the face of incorrect data. (Or incorrect assumptions ;) )""" 249 return complex(self('')) 127 250 128 251 def __repr__(self): 252 """A slightly different repr""" 129 253 result = XMLBuilder.XMLBuilderObjectBase.__repr__(self) 130 254 return '<%s %r %r>' % (result[1:-1], self.__namespace__, self.__node__) 131 255 132 def _addElement(self, namespace, node, object): 133 self._elements.append(((namespace, node), object)) 256 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 257 #~ Protected Methods 258 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 259 260 def _addElement(self, namespace, node, obj): 261 """Adds a subnode obj, that is in namespace, and has name node. Obj is not necessarily an ObjectifiedXML class, but is required to implement _toXML.""" 262 self._elements.append(((namespace, node), obj)) 134 263 return self._elements[-1] 135 264 136 265 def _addObjectifiedElement(self, obj): 266 """Same as _addElement, but assumes object has attributes __namespace__ and __node__. A little more convenient for adding subnodes in code.""" 137 267 return self._addElement(obj.__namespace__, obj.__node__, obj) 138 268 139 269 def _addNewElement(self, namespace, node, **attributes): 270 """Creates and adds a new element in namespace, with name node, having attributes as given. Uses self.__class__ for creating the element instance.""" 140 271 namespace = namespace or self.__namespace__ 141 return self._addElement(namespace, node, ObjectifiedXML(self, self, namespace, node, attributes)) 142 272 return self._addElement(namespace, node, self.__class__(self, self, namespace, node, attributes)) 273 274 def _removeElement(self, element): 275 """Removes a subnode element based on != relationship. (Note: Can be a PCData element)""" 276 elements = [x[-1] for x in self._elements if x[-1] != element] 277 delta = len(self._elements) - len(elements) 278 if delta: self._elements = elements 279 return delta 280 281 def _clearData(self): 282 """Removes all PCData from the element node.""" 283 elements = [x[-1] for x in self._elements if x[0][-1]] 284 if len(elements) != len(self._elements): 285 self._elements = elements 286 143 287 def _addData(self, data): 288 """Adds PCData to the element node.""" 144 289 self._elements.append(((self.__namespace__, ''), data)) 145 290 291 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 292 146 293 def _toXML(self, strSplit='', nsOuter='', bHeaderOnly=0): 294 """Converts the python object back into XML. 295 296 - If strSplit is None, the result is a nested list structure; otherwise, strSplit is used to join those lists into a string. 297 (default is '') 298 299 - If nsOuter matches node.__namespace__, the namespace will not be included in the XML again. 300 (default is '') 301 302 - If bHeaderOnly is true, then only node, namespace, and attributes are included in the XML. If bHeaderOnly is greater than 1, 303 the header tag will also be closed. If bHeader is false, then the child elements are included. 304 (default is 0) 305 """ 147 306 result = ['<%s ' % self.__node__] 148 307 if nsOuter != self.__namespace__: … … 150 309 result[-1] += ' '.join(['%s=%s' % (x[0], quoteattr(str(x[1]))) for x in self._attributes.iteritems()]) 151 310 if bHeaderOnly: 152 result[-1] += '>' 311 if bHeaderOnly > 1: 312 result[-1] += '/>' 313 else: result[-1] += '>' 153 314 elif self._elements: 154 315 result[-1] += '>' … … 163 324 164 325 def _childrenToXML(self, strSplit='', nsOuter=''): 326 """Converts child python objects back into XML. 327 - If strSplit is None, the result is a nested list structure; otherwise, strSplit is used to join those lists into a string. 328 (default is '') 329 330 - If nsOuter matches subnode.__namespace__, the namespace will not be included in the XML again. 331 (default is '') 332 """ 165 333 result = [] 166 334 for tupleNSNode, each in self._elements: … … 177 345 178 346 class Objectifier(XMLBuilder.XMLBuilder): 347 """An implementation of XMLBuilder that creates ObjectifiedXML nodes from an XML stream. 348 See Objectify and ObjectifyFile for usage.""" 179 349 objectified_class = ObjectifiedXML 180 350 Objectify = XMLBuilder.XMLBuilder.Parse … … 182 352 183 353 def _GetElementFactory(self, owner, parent, namespace, node, attributes): 354 """Signals that we always want to create self.objectified_class, which defaults to the ObjectifiedXML class.""" 184 355 return self.objectified_class 185 356 186 357 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 187 #~ Functional Definitions 188 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 189 190 _defaultObjectifier = Objectifier() 191 Objectify = _defaultObjectifier.Objectify 192 ObjectifyFile = _defaultObjectifier.ObjectifyFile 193 194 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 195 #~ Testing 358 #~ Functional Definitions 359 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 360 361 def _defaultObjectifier(): 362 """Returns a shared Objectifier, created only on demand. See Objectify and ObjectifyFile.""" 363 result = getattr(_defaultObjectifier, 'Objectifier', None) 364 if not result: 365 result = _defaultObjectifier.Objectifier = Objectifier() 366 return result 367 368 def Objectify(*args, **kw): 369 """Simple access to Objectifier.Objectify or Objectifier.ParseFile. 370 Warning: Uses a shared object from _defaultObjectifier, and therefore is not threadsafe.""" 371 return apply(_defaultObjectifier().Objectify, args, kw) 372 373 def ObjectifyFile(*args, **kw): 374 """Simple access to Objectifier.ObjectifyFile or Objectifier.ParseFile. 375 Warning: Uses a shared object from _defaultObjectifier, and therefore is not threadsafe.""" 376 return apply(_defaultObjectifier().ObjectifyFile, args, kw) 377 378 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 379 #~ Testing 196 380 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 197 381 198 382 def _Test_XMLObjectify(): 199 383 from pprint import pprint 200 xml = open('test_objectify.xml', 'r') 201 objectifier = Objectifier() 202 obj = objectifier(xml) 384 xmlfile = open('test_objectify.xml', 'r') 385 obj = ObjectifyFile(xmlfile) 203 386 print repr(obj) 204 387 print ' ~ ' * 20 trunk/RBFoundation/RBFoundation/__init__.py
r68 r129 33 33 """RuneBlade Foundation Components 34 34 35 Python-Powered:36 Once you meet this snake, all other become dull.37 Plays well with others. Get bit.38 39 Please see http://www.python.org40 41 35 XML Objectify Inspiration: 42 36 David Mertz at Gnosis software just has a head for Python, trunk/RBJabber/RBJabber/JabberConnection.py
r115 r129 35 35 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 36 36 37 from Foundation.XMLBuilder import XMLBuilderMixin, XMLBuilderObjectBase , ParserCreate37 from Foundation.XMLBuilder import XMLBuilderMixin, XMLBuilderObjectBase 38 38 from Foundation import SmartSelect 39 39 from Foundation.ContextApply import BindCallable … … 82 82 83 83 # Create our xml parser 84 self._parser = ParserCreate('ASCII', self._seperator) 85 self._parser.returns_unicode = 0 86 self._parser.StartElementHandler = BindCallable(self._start_element) 87 self._parser.EndElementHandler = BindCallable(self._end_element) 88 self._parser.CharacterDataHandler = BindCallable(self._char_data) 84 self._parser = self._CreateParser() 89 85 90 86 # Send the initial header trunk/RBSkinning/RBSkinning/XMLSkinner.py
r119 r129 35 35 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 36 36 37 from xml.parsers.expat import ParserCreate, ExpatError38 37 from Foundation.XMLClassBuilder import XMLClassBuilderByDictionary 39 38 from Foundation.WeakBind import BindCallable … … 72 71 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 73 72 74 def _PreParse(self):75 parser = ParserCreate('ASCII', self._seperator)76 parser.returns_unicode = 077 parser.StartElementHandler = BindCallable(self._start_element)78 parser.EndElementHandler = BindCallable(self._end_element)79 parser.CharacterDataHandler = BindCallable(self._char_data)80 return parser81 82 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~83 84 73 def SkinXML(self, xml, contextIn=None, **kwAddedContext): 85 74 rootPath = self.rootPath or os.getcwd() 86 75 self._elements, self._LastCompleteElement = [], None 87 parser = self._PreParse()76 parser = self._CreateParser() 88 77 self.context = SkinContext.SkinContext(contextIn) 89 78 self.context.__skinner__ = weakref.ref(self) … … 107 96 108 97 self._elements, self._LastCompleteElement = [], None 109 parser = self._PreParse()98 parser = self._CreateParser() 110 99 self.context = SkinContext.SkinContext(contextIn) 111 100 self.context.__skinner__ = weakref.ref(self) … … 123 112 def _SkinXMLFromTopElement(self, xml, kwAddedContext): 124 113 element = self._elements[-1] 125 parser = self._PreParse()114 parser = self._CreateParser() 126 115 element.context.__skinner__ = weakref.ref(self) 127 116 element.context.__dict__.update(kwAddedContext) … … 142 131 else: rootPath = '' 143 132 144 parser = self._PreParse()133 parser = self._CreateParser() 145 134 element.context.__skinner__ = weakref.ref(self) 146 135 if rootPath: element.context.__root__ = rootPath
