class Typed(Core): """ A I{typed} marshaller. This marshaller is semi-typed as needed to support both I{document/literal} and I{rpc/literal} soap message styles. @ivar schema: An xsd schema. @type schema: L{xsd.schema.Schema} @ivar resolver: A schema type resolver. @type resolver: L{GraphResolver} """ def __init__(self, schema, xstq=True): """ @param schema: A schema object @type schema: L{xsd.schema.Schema} @param xstq: The B{x}ml B{s}chema B{t}ype B{q}ualified flag indicates that the I{xsi:type} attribute values should be qualified by namespace. @type xstq: bool """ Core.__init__(self) self.schema = schema self.xstq = xstq self.resolver = GraphResolver(self.schema) def reset(self): self.resolver.reset() def start(self, content): # # Start marshalling the 'content' by ensuring that both the # 'content' _and_ the resolver are primed with the XSD type # information. The 'content' value is both translated and # sorted based on the XSD type. Only values that are objects # have their attributes sorted. # log.debug('starting content:\n%s', content) if content.type is None: name = content.tag if name.startswith('_'): name = '@'+name[1:] content.type = self.resolver.find(name, content.value) if content.type is None: raise TypeNotFound(content.tag) else: known = None if isinstance(content.value, Object): known = self.resolver.known(content.value) if known is None: log.debug('object has no type information', content.value) known = content.type frame = Frame(content.type, resolved=known) self.resolver.push(frame) frame = self.resolver.top() content.real = frame.resolved content.ancestry = frame.ancestry self.translate(content) self.sort(content) if self.skip(content): log.debug('skipping (optional) content:\n%s', content) self.resolver.pop() return False else: return True def suspend(self, content): # # Suspend to process a list content. Primarily, this # involves popping the 'list' content off the resolver's # stack so the list items can be marshalled. # self.resolver.pop() def resume(self, content): # # Resume processing a list content. To do this, we # really need to simply push the 'list' content # back onto the resolver stack. # self.resolver.push(Frame(content.type)) def end(self, parent, content): # # End processing the content. Make sure the content # ending matches the top of the resolver stack since for # list processing we play games with the resolver stack. # log.debug('ending content:\n%s', content) current = self.resolver.top().type if current == content.type: self.resolver.pop() else: message = 'content (end) mismatch: top=({0}) cont=({1})' raise Exception(message.format(current, content)) def node(self, content): # # Create an XML node and namespace qualify as defined # by the schema (elementFormDefault). # ns = content.type.namespace() if content.type.form_qualified: node = Element(content.tag, ns=ns) if ns[0]: node.addPrefix(ns[0], ns[1]) else: node = Element(content.tag) self.encode(node, content) log.debug('created - node:\n%s', node) return node def setnil(self, node, content): # # Set the 'node' nil only if the XSD type # specifies that it is permitted. # if content.type.nillable: node.setnil() def setdefault(self, node, content): # # Set the node to the default value specified # by the XSD type. # default = content.type.default if default is None: pass else: node.setText(default) return default def optional(self, content): if content.type.optional(): return True for a in content.ancestry: if a.optional(): return True return False def encode(self, node, content): # Add (soap) encoding information only if the resolved # type is derived by extension. Further, the xsi:type values # is qualified by namespace only if the content (tag) and # referenced type are in different namespaces. if content.type.any(): return if not content.real.extension(): return if content.type.resolve() == content.real: return ns = None name = content.real.name if self.xstq: ns = content.real.namespace('ns1') Typer.manual(node, name, ns) def skip(self, content): """ Get whether to skip this I{content}. Should be skipped when the content is optional and either the value=None or the value is an empty list. @param content: The content to skip. @type content: L{Object} @return: True if content is to be skipped. @rtype: bool """ if self.optional(content): v = content.value if v is None: return True if isinstance(v, (list,tuple)) and len(v) == 0: return True return False def optional(self, content): if content.type.optional(): return True for a in content.ancestry: if a.optional(): return True return False def translate(self, content): """ Translate using the XSD type information. Python I{dict} is translated to a suds object. Most importantly, primative values are translated from python types to XML types using the XSD type. @param content: The content to translate. @type content: L{Object} @return: self @rtype: L{Typed} """ v = content.value if v is None: return if isinstance(v, dict): cls = content.real.name content.value = Factory.object(cls, v) md = content.value.__metadata__ md.sxtype = content.type return v = content.real.translate(v, False) content.value = v return self def sort(self, content): """ Sort suds object attributes based on ordering defined in the XSD type information. @param content: The content to sort. @type content: L{Object} @return: self @rtype: L{Typed} """ v = content.value if isinstance(v, Object): md = v.__metadata__ md.ordering = self.ordering(content.real) return self def ordering(self, type): """ Get the attribute ordering defined in the specified XSD type information. @param type: An XSD type object. @type type: SchemaObject @return: An ordered list of attribute names. @rtype: list """ result = [] for child, ancestry in type.resolve(): name = child.name if child.name is None: continue if child.isattr(): name = '_%s' % child.name result.append(name) return result
class Literal(MBase): """ A I{literal} marshaller. This marshaller is semi-typed as needed to support both document/literal and rpc/literal soap styles. @ivar schema: An xsd schema. @type schema: L{xsd.schema.Schema} @ivar resolver: A schema type resolver. @type resolver: L{GraphResolver} """ def __init__(self, schema): """ @param schema: A schema object @type schema: L{xsd.schema.Schema} """ MBase.__init__(self) self.schema = schema self.resolver = GraphResolver(self.schema) def reset(self): """ Reset the resolver. """ self.resolver.reset() def start(self, content): """ Processing of I{content} has started, find and set the content's schema type using the resolver. @param content: The content for which proccessing has stated. @type content: L{Object} @return: True to continue appending @rtype: boolean @note: This will I{push} the type in the resolver. """ log.debug('starting content:\n%s', content) if content.type is None: name = content.tag if name.startswith('_'): name = '@'+name[1:] content.type = self.resolver.find(name, content.value) if content.type is None: raise TypeNotFound(content.tag) else: known = None if isinstance(content.value, Object): known = self.resolver.known(content.value) if known is None: log.debug('object has no type information', content.value) known = content.type self.sort(content.value, known) frame = Frame(content.type, resolved=known) self.resolver.push(frame) resolved = self.resolver.top().resolved content.value = self.translated(content.value, resolved) if self.skip(content): log.debug('skipping (optional) content:\n%s', content) self.resolver.pop() return False else: return True def suspend(self, content): """ Appending this content has suspended. @param content: The content for which proccessing has been suspended. @type content: L{Object} """ content.suspended = True self.resolver.pop() def resume(self, content): """ Appending this content has resumed. @param content: The content for which proccessing has been resumed. @type content: L{Object} """ frame = Frame(content.type) self.resolver.push(frame) def end(self, content): """ Processing of I{content} has ended, mirror the change in the resolver. @param content: The content for which proccessing has ended. @type content: L{Object} """ log.debug('ending content:\n%s', content) current = self.resolver.top().type if current == content.type: self.resolver.pop() else: raise Exception( 'content (end) mismatch: top=(%s) cont=(%s)' % \ (current, content)) def node(self, content): """ Create and return an XML node that is qualified using the I{type}. Also, make sure all referenced namespace prefixes are declared. @param content: The content for which proccessing has ended. @type content: L{Object} @return: A new node. @rtype: L{Element} """ ns = content.type.namespace() if content.type.form_qualified: node = Element(content.tag, ns=ns) node.addPrefix(ns[0], ns[1]) else: node = Element(content.tag) self.encode(node, content) log.debug('created - node:\n%s', node) return node def setnil(self, node, content): """ Set the value of the I{node} to nill when nillable by the type or the resolved type is a builtin B{and} it is nillable. @param node: A I{nil} node. @type node: L{Element} @param content: The content for which proccessing has ended. @type content: L{Object} """ resolved = content.type.resolve() if ( content.type.nillable or \ ( resolved.builtin() and resolved.nillable ) ): node.setnil() def encode(self, node, content): """ Add (soap) encoding information only if the resolved type is derived by extension. Further, the xsi:type values is qualified by namespace only if the content (tag) and referenced type are in different namespaces. @param node: The node to update. @type node: L{Element} @param content: The content for which proccessing has ended. @type content: L{Object} """ if content.type.any(): return resolved = self.resolver.top().resolved if resolved is None: resolved = content.type.resolve() if not resolved.extension(): return ns = None name = resolved.name ns0 = content.type.namespace('ns0') ns1 = resolved.namespace('ns1') if ns0[1] != ns1[1]: ns = ns1 Typer.manual(node, name, ns) def skip(self, content): """ skip this content """ if self.optional(content): v = content.value if v is None: return True if isinstance(v, (list,tuple)) and len(v) == 0: return True return False def optional(self, content): """ this content is optional """ if content.type.optional(): return True ancestry = self.resolver.top().ancestry for a in ancestry: if a.optional(): return True return False def translated(self, value, resolved): """ translate using the schema type """ if value is not None: return resolved.translate(value, False) else: return None def sort(self, sobject, resolved): """ sort attributes using the schema type """ md = sobject.__metadata__ md.ordering = self.ordering(resolved) def ordering(self, type): """ get the ordering """ result = [] for child, ancestry in type.resolve(): name = child.name if child.name is None: continue if child.isattr(): name = '_%s' % child.name result.append(name) return result
class Typed(Core): """ A I{typed} marshaller. This marshaller is semi-typed as needed to support both I{document/literal} and I{rpc/literal} soap message styles. @ivar schema: An xsd schema. @type schema: L{xsd.schema.Schema} @ivar resolver: A schema type resolver. @type resolver: L{GraphResolver} """ def __init__(self, schema, xstq=True): """ @param schema: A schema object @type schema: L{xsd.schema.Schema} @param xstq: The B{x}ml B{s}chema B{t}ype B{q}ualified flag indicates that the I{xsi:type} attribute values should be qualified by namespace. @type xstq: bool """ Core.__init__(self) self.schema = schema self.xstq = xstq self.resolver = GraphResolver(self.schema) def reset(self): self.resolver.reset() def start(self, content): # # Start marshalling the 'content' by ensuring that both the # 'content' _and_ the resolver are primed with the XSD type # information. The 'content' value is both translated and # sorted based on the XSD type. Only values that are objects # have their attributes sorted. # log.debug('starting content:\n%s', content) if content.type is None: name = content.tag if name.startswith('_'): name = '@' + name[1:] content.type = self.resolver.find(name, content.value) if content.type is None: raise TypeNotFound(content.tag) else: known = None if isinstance(content.value, Object): known = self.resolver.known(content.value) if known is None: log.debug('object has no type information', content.value) known = content.type frame = Frame(content.type, resolved=known) self.resolver.push(frame) frame = self.resolver.top() content.real = frame.resolved content.ancestry = frame.ancestry self.translate(content) self.sort(content) if self.skip(content): log.debug('skipping (optional) content:\n%s', content) self.resolver.pop() return False else: return True def suspend(self, content): # # Suspend to process a list content. Primarily, this # involves popping the 'list' content off the resolver's # stack so the list items can be marshalled. # self.resolver.pop() def resume(self, content): # # Resume processing a list content. To do this, we # really need to simply push the 'list' content # back onto the resolver stack. # self.resolver.push(Frame(content.type)) def end(self, parent, content): # # End processing the content. Make sure the content # ending matches the top of the resolver stack since for # list processing we play games with the resolver stack. # log.debug('ending content:\n%s', content) current = self.resolver.top().type if current == content.type: self.resolver.pop() else: raise Exception, \ 'content (end) mismatch: top=(%s) cont=(%s)' % \ (current, content) def node(self, content): # # Create an XML node and namespace qualify as defined # by the schema (elementFormDefault). # ns = content.type.namespace() if content.type.form_qualified: node = Element(content.tag, ns=ns) node.addPrefix(ns[0], ns[1]) else: node = Element(content.tag) self.encode(node, content) log.debug('created - node:\n%s', node) return node def setnil(self, node, content): # # Set the 'node' nil only if the XSD type # specifies that it is permitted. # if content.type.nillable: node.setnil() def setdefault(self, node, content): # # Set the node to the default value specified # by the XSD type. # default = content.type.default if default is None: pass else: node.setText(default) return default def optional(self, content): if content.type.optional(): return True for a in content.ancestry: if a.optional(): return True return False def encode(self, node, content): # Add (soap) encoding information only if the resolved # type is derived by extension. Further, the xsi:type values # is qualified by namespace only if the content (tag) and # referenced type are in different namespaces. if content.type.any(): return if not content.real.extension(): return if content.type.resolve() == content.real: return ns = None name = content.real.name if self.xstq: ns = content.real.namespace('ns1') Typer.manual(node, name, ns) def skip(self, content): """ Get whether to skip this I{content}. Should be skipped when the content is optional and either the value=None or the value is an empty list. @param content: The content to skip. @type content: L{Object} @return: True if content is to be skipped. @rtype: bool """ if self.optional(content): v = content.value if v is None: return True if isinstance(v, (list, tuple)) and len(v) == 0: return True return False def optional(self, content): if content.type.optional(): return True for a in content.ancestry: if a.optional(): return True return False def translate(self, content): """ Translate using the XSD type information. Python I{dict} is translated to a suds object. Most importantly, primative values are translated from python types to XML types using the XSD type. @param content: The content to translate. @type content: L{Object} @return: self @rtype: L{Typed} """ v = content.value if v is None: return if isinstance(v, dict): cls = content.real.name content.value = Factory.object(cls, v) md = content.value.__metadata__ md.sxtype = content.type return v = content.real.translate(v, False) content.value = v return self def sort(self, content): """ Sort suds object attributes based on ordering defined in the XSD type information. @param content: The content to sort. @type content: L{Object} @return: self @rtype: L{Typed} """ v = content.value if isinstance(v, Object): md = v.__metadata__ md.ordering = self.ordering(content.real) return self def ordering(self, type): """ Get the attribute ordering defined in the specified XSD type information. @param type: An XSD type object. @type type: SchemaObject @return: An ordered list of attribute names. @rtype: list """ result = [] for child, ancestry in type.resolve(): name = child.name if child.name is None: continue if child.isattr(): name = '_%s' % child.name result.append(name) return result
class Literal(Core): """ A I{literal} marshaller. This marshaller is semi-typed as needed to support both document/literal and rpc/literal soap styles. @ivar schema: An xsd schema. @type schema: L{xsd.schema.Schema} @ivar resolver: A schema type resolver. @type resolver: L{GraphResolver} """ def __init__(self, schema): """ @param schema: A schema object @type schema: L{xsd.schema.Schema} """ Core.__init__(self) self.schema = schema self.options = schema.options self.resolver = GraphResolver(self.schema) def reset(self): self.resolver.reset() def start(self, content): log.debug('starting content:\n%s', content) if content.type is None: name = content.tag if name.startswith('_'): name = '@'+name[1:] content.type = self.resolver.find(name, content.value) if content.type is None: raise TypeNotFound(content.tag) else: known = None if isinstance(content.value, Object): known = self.resolver.known(content.value) if known is None: log.debug('object has no type information', content.value) known = content.type self.sort(content.value, known) frame = Frame(content.type, resolved=known) self.resolver.push(frame) resolved = self.resolver.top().resolved content.value = self.translated(content.value, resolved) if self.skip(content): log.debug('skipping (optional) content:\n%s', content) self.resolver.pop() return False else: return True def suspend(self, content): content.suspended = True self.resolver.pop() def resume(self, content): frame = Frame(content.type) self.resolver.push(frame) def end(self, content): log.debug('ending content:\n%s', content) current = self.resolver.top().type if current == content.type: self.resolver.pop() else: raise Exception( 'content (end) mismatch: top=(%s) cont=(%s)' % \ (current, content)) def node(self, content): ns = content.type.namespace() if content.type.form_qualified: node = Element(content.tag, ns=ns) node.addPrefix(ns[0], ns[1]) else: node = Element(content.tag) self.encode(node, content) log.debug('created - node:\n%s', node) return node def setnil(self, node, content): if content.type.nillable: node.setnil() def setdefault(self, node, content): default = content.type.default if default is None: pass else: node.setText(default) return default def optional(self, content): if content.type.optional(): return True resolver = self.resolver ancestry = resolver.top().ancestry for a in ancestry: if a.optional(): return True return False def encode(self, node, content): # Add (soap) encoding information only if the resolved # type is derived by extension. Further, the xsi:type values # is qualified by namespace only if the content (tag) and # referenced type are in different namespaces. if content.type.any(): return resolved = self.resolver.top().resolved if resolved is None: resolved = content.type.resolve() if not resolved.extension(): return ns = None name = resolved.name if self.options.xstq: ns = resolved.namespace('ns1') Typer.manual(node, name, ns) def skip(self, content): """ skip this content """ if self.optional(content): v = content.value if v is None: return True if isinstance(v, (list,tuple)) and len(v) == 0: return True return False def optional(self, content): if content.type.optional(): return True ancestry = self.resolver.top().ancestry for a in ancestry: if a.optional(): return True return False def translated(self, value, resolved): """ translate using the schema type """ if value is not None: return resolved.translate(value, False) else: return None def sort(self, sobject, resolved): """ sort attributes using the schema type """ md = sobject.__metadata__ md.ordering = self.ordering(resolved) def ordering(self, type): """ get the ordering """ result = [] for child, ancestry in type.resolve(): name = child.name if child.name is None: continue if child.isattr(): name = '_%s' % child.name result.append(name) return result