Home | Trees | Indices | Help |
|
---|
|
1 # -*- coding: utf-8 -*- 2 # Copyright 2009-2013, Peter A. Bigot 3 # 4 # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 # not use this file except in compliance with the License. You may obtain a 6 # copy of the License at: 7 # 8 # http://www.apache.org/licenses/LICENSE-2.0 9 # 10 # Unless required by applicable law or agreed to in writing, software 11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 # License for the specific language governing permissions and limitations 14 # under the License. 15 16 """This module contains support for generating bindings from an XML stream 17 using a SAX parser.""" 18 19 import logging 20 import xml.dom 21 import pyxb.namespace 22 import pyxb.utils.saxutils 23 import pyxb.utils.saxdom 24 import pyxb.utils.utility 25 from pyxb.binding import basis 26 from pyxb.namespace.builtin import XMLSchema_instance as XSI 27 28 _log = logging.getLogger(__name__) 2931 """State required to generate bindings for a specific element. 32 33 If the document being parsed includes references to unrecognized elements, 34 a DOM instance of the element and its content is created and treated as a 35 wildcard element. 36 """ 37 38 # An expanded name corresponding to xsi:nil 39 __XSINilTuple = XSI.nil.uriTuple() 40 41 # The binding instance being created for this element. When the 42 # element type has simple content, the binding instance cannot be 43 # created until the end of the element has been reached and the 44 # content of the element has been processed accumulated for use in 45 # the instance constructor. When the element type has complex 46 # content, the binding instance must be created at the start of 47 # the element, so contained elements can be properly stored. 48 __bindingInstance = None 49 50 # The schema binding for the element being constructed. 51 __elementBinding = None 5224654 """Record the binding to be used for this element. 55 56 Generally ignored, except at the top level this is the only way to 57 associate a binding instance created from an xsi:type description with 58 a specific element.""" 59 self.__elementBinding = element_binding60 61 # The nearest enclosing complex type definition63 """The nearest enclosing complex type definition, as used for 64 resolving local element/attribute names. 65 66 @return: An instance of L{basis.complexTypeDefinition}, or C{None} if 67 the element is top-level 68 """ 69 return self.__enclosingCTD70 __enclosingCTD = None 71 72 # The factory that is called to create a binding instance for this 73 # element; None if the binding instance was created at the start 74 # of the element. 75 __delayedConstructor = None 76 77 # An xml.sax.xmlreader.Attributes instance providing the 78 # attributes for the element. 79 __attributes = None 80 81 # An xml.dom.Node corresponding to the (sub-)document 82 __domDocument = None 83 84 __domDepth = None 8587 super(_SAXElementState, self).__init__(**kw) 88 self.__bindingInstance = None 89 parent_state = self.parentState() 90 if isinstance(parent_state, _SAXElementState): 91 self.__enclosingCTD = parent_state.enclosingCTD() 92 self.__domDocument = parent_state.__domDocument 93 if self.__domDocument is not None: 94 self.__domDepth = parent_state.__domDepth + 19597 """Set the enclosing complex type definition for this element. 98 99 @param enclosing_ctd: The scope for a local element. 100 @type enclosing_ctd: L{basis.complexTypeDefinition} 101 @return: C{self} 102 """ 103 self.__enclosingCTD = enclosing_ctd104 105 # Create the binding instance for this element.107 kw = { '_from_xml' : True, 108 '_location' : self.location() } 109 110 # Note whether the node is marked nil 111 if self.__XSINilTuple in attrs: 112 kw['_nil'] = pyxb.binding.datatypes.boolean(attrs.getValue(self.__XSINilTuple)) 113 114 if content is None: 115 content = [] 116 self.__bindingInstance = new_object_factory(*content, **kw) 117 if isinstance(self.__bindingInstance, pyxb.utils.utility.Locatable_mixin): 118 self.__bindingInstance._setLocation(self.location()) 119 120 # Record the namespace context so users of the binding can 121 # interpret QNames within the attributes and content. 122 self.__bindingInstance._setNamespaceContext(self.__namespaceContext) 123 124 # Set instance attributes 125 # NB: attrs implements the SAX AttributesNS interface, meaning 126 # that names are pairs of (namespaceURI, localName), just like we 127 # want them to be. 128 for attr_name in self.__attributes.getNames(): 129 attr_en = pyxb.namespace.ExpandedName(attr_name) 130 # Ignore xmlns and xsi attributes; we've already handled those 131 if attr_en.namespace() in ( pyxb.namespace.XMLNamespaces, XSI ): 132 continue 133 # The binding instance may be a simple type that does not support 134 # attributes; the following raises an exception in that case. 135 self.__bindingInstance._setAttribute(attr_en, attrs.getValue(attr_name)) 136 137 return self.__bindingInstance138140 return self.__domDocument is not None141143 """Actions upon first encountering an element for which we cannot create a binding. 144 145 Invoking this transitions the parser into DOM mode, creating a new DOM 146 document that will represent this element including its content.""" 147 assert not self.__domDocument 148 self.__domDocument = pyxb.utils.saxdom.Document(namespace_context=self.namespaceContext()) 149 self.__domDepth = 0 150 return self.startDOMElement(attrs)151153 """Actions upon entering an element that is part of a DOM subtree.""" 154 self.__domDepth += 1 155 self.__attributes = pyxb.utils.saxdom.NamedNodeMap() 156 ns_ctx = self.namespaceContext() 157 for name in attrs.getNames(): 158 attr_en = pyxb.namespace.ExpandedName(name) 159 self.__attributes._addItem(pyxb.utils.saxdom.Attr(expanded_name=attr_en, namespace_context=ns_ctx, value=attrs.getValue(name), location=self.location()))160162 """Actions upon leaving an element that is part of a DOM subtree.""" 163 ns_ctx = self.namespaceContext() 164 element = pyxb.utils.saxdom.Element(namespace_context=ns_ctx, expanded_name=self.expandedName(), attributes=self.__attributes, location=self.location()) 165 for info in self.content(): 166 if isinstance(info.item, xml.dom.Node): 167 element.appendChild(info.item) 168 else: 169 element.appendChild(pyxb.utils.saxdom.Text(info.item, namespace_context=ns_ctx)) 170 self.__domDepth -= 1 171 if 0 == self.__domDepth: 172 self.__domDocument.appendChild(element) 173 #pyxb.utils.saxdom._DumpDOM(self.__domDocument) 174 self.__domDepth = None 175 self.__domDocument = None 176 parent_state = self.parentState() 177 parent_state.addElementContent(self.location(), element, None) 178 return element179181 """Actions upon entering an element that will produce a binding instance. 182 183 The element use is recorded. If the type is a subclass of 184 L{basis.simpleTypeDefinition}, a delayed constructor is recorded so 185 the binding instance can be created upon completion of the element; 186 otherwise, a binding instance is created and stored. The attributes 187 are used to initialize the binding instance (now, or upon element 188 end). 189 190 @param type_class: The Python type of the binding instance 191 @type type_class: subclass of L{basis._TypeBinding_mixin} 192 @param new_object_factory: A callable object that creates an instance of the C{type_class} 193 @param element_decl: The element use with which the binding instance is associated. Will be C{None} for top-level elements 194 @type element_decl: L{basis.element} 195 @param attrs: The XML attributes associated with the element 196 @type attrs: C{xml.sax.xmlreader.Attributes} 197 @return: The generated binding instance, or C{None} if creation is delayed 198 """ 199 self.__delayedConstructor = None 200 self.__elementDecl = element_decl 201 self.__attributes = attrs 202 if type_class._IsSimpleTypeContent(): 203 self.__delayedConstructor = new_object_factory 204 else: 205 try: 206 pyxb.namespace.NamespaceContext.PushContext(self.namespaceContext()) 207 self.__constructElement(new_object_factory, attrs) 208 finally: 209 pyxb.namespace.NamespaceContext.PopContext() 210 return self.__bindingInstance211213 """Perform any end-of-element processing. 214 215 For simple type instances, this creates the binding instance. 216 @return: The generated binding instance 217 """ 218 if self.__delayedConstructor is not None: 219 args = [] 220 for info in self.content(): 221 if info.maybe_element or (info.element_decl is not None): 222 raise pyxb.NonElementValidationError(info.item, info.location) 223 args.append(info.item) 224 try: 225 pyxb.namespace.NamespaceContext.PushContext(self.namespaceContext()) 226 self.__constructElement(self.__delayedConstructor, self.__attributes, args) 227 except pyxb.ValidationError as e: 228 if e.location is None: 229 e.location = self.location() 230 raise 231 finally: 232 pyxb.namespace.NamespaceContext.PopContext() 233 else: 234 for info in self.content(): 235 self.__bindingInstance.append(info.item, 236 _element_decl=info.element_decl, 237 _maybe_element=info.maybe_element, 238 _location=info.location) 239 parent_state = self.parentState() 240 if parent_state is not None: 241 parent_state.addElementContent(self.location(), self.__bindingInstance, self.__elementDecl) 242 # As CreateFromDOM does, validate the resulting element 243 if self.__bindingInstance._element() is None: 244 self.__bindingInstance._setElement(self.__elementBinding) 245 return self.__bindingInstance._postDOMValidate()248 """A SAX handler class which generates a binding instance for a document 249 through a streaming parser. 250 251 An example of using this to parse the document held in the (unicode) text 252 value C{xmlt} is:: 253 254 import pyxb.binding.saxer 255 import io 256 257 saxer = pyxb.binding.saxer.make_parser() 258 handler = saxer.getContentHandler() 259 saxer.parse(io.StringIO(xmlt)) 260 instance = handler.rootObject() 261 262 """ 263 264 # Whether invocation of handler methods should be traced 265 __trace = False 266 267 # An expanded name corresponding to xsi:type 268 __XSITypeTuple = XSI.type.uriTuple() 269 270 __domHandler = None 271 __domDepth = None 272403274 """Return the binding object corresponding to the top-most 275 element in the document 276 277 @return: An instance of L{basis._TypeBinding_mixin} (most usually a 278 L{basis.complexTypeDefinition}. 279 280 @raise pyxb.[UnrecognizedDOMRootNodeError: No binding could be found to 281 match the top-level element in the document.""" 282 if not isinstance(self.__rootObject, basis._TypeBinding_mixin): 283 # Happens if the top-level element got processed as a DOM instance. 284 assert isinstance(self.__rootObject, xml.dom.Node) 285 raise pyxb.UnrecognizedDOMRootNodeError(self.__rootObject) 286 return self.__rootObject._postDOMValidate()287 __rootObject = None 288290 """Reset the state of the handler in preparation for processing a new 291 document. 292 293 @return: C{self} 294 """ 295 super(PyXBSAXHandler, self).reset() 296 self.__rootObject = None 297 return self298300 """Create a parser instance for converting XML to bindings. 301 302 @keyword element_state_constructor: Overridden with the value 303 L{_SAXElementState} before invoking the L{superclass 304 constructor<pyxb.utils.saxutils.BaseSAXHandler.__init__>}. 305 """ 306 307 kw.setdefault('element_state_constructor', _SAXElementState) 308 super(PyXBSAXHandler, self).__init__(**kw) 309 self.reset()310312 (this_state, parent_state, ns_ctx, name_en) = super(PyXBSAXHandler, self).startElementNS(name, qname, attrs) 313 314 # Delegate processing if in DOM mode 315 if this_state.inDOMMode(): 316 return this_state.startDOMElement(attrs) 317 318 # Resolve the element within the appropriate context. Note 319 # that global elements have no use, only the binding. 320 if parent_state.enclosingCTD() is not None: 321 (element_binding, element_decl) = parent_state.enclosingCTD()._ElementBindingDeclForName(name_en) 322 else: 323 element_decl = None 324 element_binding = name_en.elementBinding() 325 this_state.setElementBinding(element_binding) 326 327 # Non-root elements should have an element use, from which we can 328 # extract the binding if we couldn't find one elsewhere. (Keep any 329 # current binding, since it may be a member of a substitution group.) 330 if (element_decl is not None) and (element_binding is None): 331 assert self.__rootObject is not None 332 element_binding = element_decl.elementBinding() 333 assert element_binding is not None 334 335 # Start knowing nothing 336 type_class = None 337 if element_binding is not None: 338 element_binding = element_binding.elementForName(name) 339 type_class = element_binding.typeDefinition() 340 341 # Process an xsi:type attribute, if present 342 if self.__XSITypeTuple in attrs: 343 (did_replace, type_class) = XSI._InterpretTypeAttribute(attrs.getValue(self.__XSITypeTuple), ns_ctx, self.fallbackNamespace(), type_class) 344 if did_replace: 345 element_binding = None 346 347 if type_class is None: 348 # Bother. We don't know what this thing is. But that's not an 349 # error, if the schema accepts wildcards. For consistency with 350 # the DOM-based interface, we need to build a DOM node. 351 return this_state.enterDOMMode(attrs) 352 353 if element_binding is not None: 354 # Invoke binding __call__ method not Factory, so can check for 355 # abstract elements. 356 new_object_factory = element_binding 357 else: 358 new_object_factory = type_class.Factory 359 360 # Update the enclosing complex type definition for this 361 # element state. 362 assert type_class is not None 363 if issubclass(type_class, pyxb.binding.basis.complexTypeDefinition): 364 this_state.setEnclosingCTD(type_class) 365 else: 366 this_state.setEnclosingCTD(parent_state.enclosingCTD()) 367 368 # Process the element start. This may or may not return a 369 # binding object. 370 binding_object = this_state.startBindingElement(type_class, new_object_factory, element_decl, attrs) 371 372 # If the top-level element has complex content, this sets the 373 # root object. If it has simple content, see endElementNS. 374 if self.__rootObject is None: 375 self.__rootObject = binding_object376378 this_state = super(PyXBSAXHandler, self).endElementNS(name, qname) 379 if this_state.inDOMMode(): 380 # Delegate processing if in DOM mode. Note that completing this 381 # element may take us out of DOM mode. In any case, the returned 382 # binding object is a DOM element instance. 383 binding_object = this_state.endDOMElement() 384 else: 385 # Process the element end. This will return a binding object, 386 # either the one created at the start or the one created at 387 # the end. 388 binding_object = this_state.endBindingElement() 389 assert binding_object is not None 390 391 # If we don't have a root object, save it. No, there is not a 392 # problem doing this on the close of the element. If the 393 # top-level element has complex content, the object was 394 # created on start, and the root object has been assigned. If 395 # it has simple content, then there are no internal elements 396 # that could slip in and set this before we get to it here. 397 # 398 # Unless we're still in DOM mode, in which case this isn't really the 399 # root object. Then the real root object will be the one that caused 400 # us to enter DOM mode. 401 if (self.__rootObject is None) and not this_state.inDOMMode(): 402 self.__rootObject = binding_object405 """Extend L{pyxb.utils.saxutils.make_parser} to change the default 406 C{content_handler_constructor} to be L{PyXBSAXHandler}. 407 """ 408 kw.setdefault('content_handler_constructor', PyXBSAXHandler) 409 return pyxb.utils.saxutils.make_parser(*args, **kw)410 411 ## Local Variables: 412 ## fill-column:78 413 ## End: 414
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Sun Sep 18 22:33:49 2016 | http://epydoc.sourceforge.net |