def __init__ (self, archive_path=None, generation_uid=None, loadable=True, stage=None): """Create a new namespace archive. If C{namespaces} is given, this is an output archive. If C{namespaces} is absent, this is an input archive. @raise IOError: error attempting to read the archive file @raise pickle.UnpicklingError: something is wrong with the format of the library """ self.__namespaces = set() if generation_uid is not None: if archive_path: raise pyxb.LogicError('NamespaceArchive: cannot define both namespaces and archive_path') self.__generationUID = generation_uid self.__locateModuleRecords() elif archive_path is not None: if generation_uid is not None: raise pyxb.LogicError('NamespaceArchive: cannot provide generation_uid with archive_path') self.__archivePath = archive_path self.__stage = self._STAGE_UNOPENED self.__isLoadable = loadable if self.__isLoadable: if stage is None: stage = self._STAGE_moduleRecords self._readToStage(stage) else: pass
def createChildElement(self, expanded_name, parent=None): """Create a new element node in the tree. @param expanded_name: The name of the element. A plain string indicates a name in no namespace. @type expanded_name: L{pyxb.namespace.ExpandedName} or C{str} or C{unicode} @keyword parent: The node in the tree that will serve as the child's parent. If C{None}, the document element is used. (If there is no document element, then this call creates it as a side-effect.) @return: A newly created DOM element @rtype: C{xml.dom.Element} """ if parent is None: parent = self.document().documentElement if parent is None: parent = self.__document if isinstance(expanded_name, (str, unicode)): expanded_name = pyxb.namespace.ExpandedName(None, expanded_name) if not isinstance(expanded_name, pyxb.namespace.ExpandedName): raise pyxb.LogicError('Invalid type %s for expanded name' % (type(expanded_name), )) ns = expanded_name.namespace() name = expanded_name.localName() ns_uri = xml.dom.EMPTY_NAMESPACE pfx = self.namespacePrefix(ns) if pfx is not None: ns_uri = ns.uri() name = '%s:%s' % (pfx, name) element = self.__document.createElementNS(ns_uri, name) return parent.appendChild(element)
def bindingRequires(self, reset=False, include_lax=False): """Return a set of components upon whose bindings this component's bindings depend. For example, bindings that are extensions or restrictions depend on their base types. Complex type definition bindings require that the types of their attribute declarations be available at the class definition, and the types of their element declarations in the postscript. @keyword include_lax: if C{False} (default), only the requirements of the class itself are returned. If C{True}, all requirements are returned. @rtype: C{set(L{pyxb.xmlschema.structures._SchemaComponent_mixin})} """ if reset or (self.__bindingRequires is None): if isinstance( self, resolution._Resolvable_mixin) and not (self.isResolved()): raise pyxb.LogicError( 'Unresolved %s in %s: %s' % (self.__class__.__name__, self._namespaceContext().targetNamespace(), self.name())) self.__bindingRequires = self._bindingRequires_vx(include_lax) return self.__bindingRequires
def __new__ (cls, *args, **kw): """Pickling and singleton support. This ensures that no more than one Namespace instance exists for any given URI. We could do this up in __init__, but that doesn't normally get called when unpickling instances; this does. See also __getnewargs__().""" (uri,) = args if isinstance(uri, tuple): # Special handling to reconstruct absent namespaces. (variant, uid) = uri if cls.__SerializedVariantAbsent == variant: ns = cls.__AbsentNamespaceRegistry.get(uid) if ns is None: raise pyxb.UsageError('Unable to reconstruct instance of absent namespace') return ns raise pyxb.LogicError('Unrecognized serialized namespace variant %s uid %s' % (variant, uid)) elif not (uri in cls.__Registry): instance = object.__new__(cls) # Do this one step of __init__ so we can do checks during unpickling instance.__uri = uri instance._reset() # Absent namespaces are not stored in the registry. if uri is None: cls.__AbsentNamespaces.add(instance) return instance cls.__Registry[uri] = instance return cls.__Registry[uri]
def _readToStage(self, stage): if self.__stage is None: raise pyxb.NamespaceArchiveError( 'Attempt to read from invalid archive %s' % (self, )) try: while self.__stage < stage: if self.__stage < self._STAGE_uid: self.__unpickler = self.__createUnpickler() self.__stage = self._STAGE_uid continue if self.__stage < self._STAGE_readModules: assert self.__unpickler is not None self.__readModules(self.__unpickler) self.__stage = self._STAGE_readModules continue if self.__stage < self._STAGE_validateModules: self.__validateModules() self.__stage = self._STAGE_validateModules continue if self.__stage < self._STAGE_readComponents: assert self.__unpickler is not None self.__stage = self._STAGE_readComponents self.__readComponentSet(self.__unpickler) self.__unpickler = None continue raise pyxb.LogicError('Too many stages (at %s, want %s)' % (self.__stage, stage)) except: self.__stage = None self.__unpickler = None raise
def LateDatatypeBindsSuperclass(cls): """Return true if false if the proposed datatype should be used, or True if the base type definition of the proposed datatype should be used.""" if cls._LateDatatypeBindsSuperclass is None: raise pyxb.LogicError( 'Class %s did not set _LateDatatypeBindsSuperclass variable.') return cls._LateDatatypeBindsSuperclass
def _setObjectOrigin(self, object_origin, override=False): if (self.__objectOrigin is not None) and (not override): if self.__objectOrigin != object_origin: raise pyxb.LogicError( 'Inconsistent origins for object %s: %s %s' % (self, self.__objectOrigin, object_origin)) else: self.__objectOrigin = object_origin
def builtinModulePath (self): from pyxb.namespace import builtin if not self.__builtinModulePath: raise pyxb.LogicError('Namespace has no built-in module: %s' % (self,)) mr = self.lookupModuleRecordByUID(builtin.BuiltInObjectUID) assert mr is not None assert mr.modulePath() == self.__builtinModulePath return self.__builtinModulePath
def __init__ (self, **kw): namespace_set = set(kw.get('namespace_set', [])) namespace = kw.get('namespace') if namespace is not None: namespace_set.add(namespace) if 0 == len(namespace_set): raise pyxb.LogicError('NamespaceDependencies requires at least one root namespace') self.__rootNamespaces = namespace_set
def __getnewargs__(self): """Pickling support. To ensure that unpickled Namespace instances are unique per URI, we ensure that the routine that creates unpickled instances knows what it's supposed to return.""" if self.uri() is None: raise pyxb.LogicError('Illegal to serialize absent namespaces') return (self.uri(), )
def _bindingRequires_vx (self, include_lax): """Placeholder for subclass method that identifies the necessary components. @note: Override in subclasses. @return: The component instances on which this component depends @rtype: C{frozenset} @raise LogicError: A subclass failed to implement this method """ raise pyxb.LogicError('%s does not implement _bindingRequires_vx' % (type(self),))
def NamespaceInstance (namespace): """Get a namespace instance for the given namespace. This is used when it is unclear whether the namespace is specified by URI or by instance or by any other mechanism we might dream up in the future.""" if isinstance(namespace, pyxb.namespace.Namespace): return namespace if isinstance(namespace, basestring): return NamespaceForURI(namespace, True) raise pyxb.LogicError('Cannot identify namespace from value of type %s' % (type(namespace),))
def ClassForFacet(cls, name): """Given the name of a facet, return the Facet subclass that represents it.""" assert cls != Facet if 0 <= name.find(':'): name = name.split(':', 1)[1] facet_class = globals().get('%s_%s' % (cls._FacetPrefix, name), None) if facet_class is None: raise pyxb.LogicError( 'Unrecognized facet name %s: expect %s' % (name, ','.join([_f._Name for _f in cls.Facets]))) assert facet_class is not None return facet_class
def NamespaceForURI(uri, create_if_missing=False): """Given a URI, provide the L{Namespace} instance corresponding to it. This can only be used to lookup or create real namespaces. To create absent namespaces, use L{CreateAbsentNamespace}. @param uri: The URI that identifies the namespace @type uri: A non-empty C{str} or C{unicode} string @keyword create_if_missing: If C{True}, a namespace for the given URI is created if one has not already been registered. Default is C{False}. @type create_if_missing: C{bool} @return: The Namespace corresponding to C{uri}, if available @rtype: L{Namespace} or C{None} @raise pyxb.LogicError: The uri is not a non-empty string """ if not isinstance(uri, (str, unicode)): raise pyxb.LogicError('Cannot lookup absent namespaces') if 0 == len(uri): raise pyxb.LogicError('Namespace URIs must not be empty strings') rv = pyxb.namespace.Namespace._NamespaceForURI(uri) if (rv is None) and create_if_missing: rv = pyxb.namespace.Namespace(uri) return rv
def __identifyNamespace (self, nsval): """Identify the specified namespace, which should be a built-in. Normally we can just use a reference to the Namespace module instance, but when creating those instances we sometimes need to refer to ones for which the instance has not yet been created. In that case, we use the name of the instance, and resolve the namespace when we need to create the initial context.""" if nsval is None: return self if isinstance(nsval, six.string_types): nsval = globals().get(nsval) if isinstance(nsval, Namespace): return nsval raise pyxb.LogicError('Cannot identify namespace from %s' % (nsval,))
def __init__(self, uri, description=None, builtin_namespace=None, builtin_module_path=None, is_undeclared_namespace=False, is_loaded_namespace=False, bound_prefix=None, default_namespace=None, in_scope_namespaces=None): """Create a new Namespace. The URI must be non-None, and must not already be assigned to a Namespace instance. See _NamespaceForURI(). User-created Namespace instances may also provide a description. Users should never provide a builtin_namespace parameter. """ # New-style superclass invocation super(Namespace, self).__init__() self.__contextDefaultNamespace = default_namespace self.__contextInScopeNamespaces = in_scope_namespaces # Make sure that we're not trying to do something restricted to # built-in namespaces is_builtin_namespace = not (builtin_namespace is None) if not is_builtin_namespace: if bound_prefix is not None: raise pyxb.LogicError( 'Only permanent Namespaces may have bound prefixes') # We actually set the uri when this instance was allocated; # see __new__(). assert self.__uri == uri self.__boundPrefix = bound_prefix self.__description = description self.__isBuiltinNamespace = is_builtin_namespace self.__builtinNamespaceVariable = builtin_namespace self.__builtinModulePath = builtin_module_path self.__isUndeclaredNamespace = is_undeclared_namespace self.__isLoadedNamespace = is_loaded_namespace self._reset() assert (self.__uri is None) or (self.__Registry[self.__uri] == self)
def __init__(self, default_namespace=None, namespace_prefix_map=None, inherit_from=None): """Create a new namespace declaration configuration. @keyword default_namespace: Optional L{pyxb.namespace.Namespace} instance that serves as the default namespace (applies to unqualified names). @keyword namespace_prefix_map: Optional map from L{pyxb.namespace.Namespace} instances to C{str} values that are to be used as the corresponding namespace prefix when constructing U{qualified names<http://www.w3.org/TR/1999/REC-xml-names-19990114/#dt-qname>}. @keyword inherit_from: Optional instance of this class from which defaults are inherited. Inheritance is overridden by values of other keywords in the initializer. """ self.__prefixes = set() self.__namespacePrefixCounter = 0 self.__namespaces = {} self.__defaultNamespace = None self.__resetNamespacePrefixMap() if inherit_from is not None: if default_namespace is None: default_namespace = inherit_from.defaultNamespace() self.__namespacePrefixMap.update(inherit_from.__namespacePrefixMap) self.__namespacePrefixCount = inherit_from.__namespacePrefixCounter self.__namespaces.update(inherit_from.__namespaces) self.__prefixes.update(inherit_from.__prefixes) if default_namespace is not None: self.setDefaultNamespace(default_namespace) prefixes = set(self.__namespacePrefixMap.itervalues()) prefixes.update(self.__prefixes) if namespace_prefix_map is not None: prefixes = set() for (ns, pfx) in namespace_prefix_map.iteritems(): ns = pyxb.namespace.NamespaceInstance(ns) if pfx in prefixes: raise pyxb.LogicError( 'Cannot assign same prefix to multiple namespacess: %s' % (pfx, )) prefixes.add(pfx) self.__namespacePrefixMap[ns] = pfx
def declareNamespace(self, namespace, prefix=None, add_to_map=False): """Record the given namespace as one to be used in this document. @param namespace: The namespace to be associated with the document. @type namespace: L{pyxb.namespace.Namespace} @keyword prefix: Optional prefix to be used with this namespace. If not provided, a unique prefix is generated or a standard prefix is used, depending on the namespace. @return: a prefix that may be used with the namespace. If C{prefix} was C{None} the return value may be a previously-assigned prefix. @todo: ensure multiple namespaces do not share the same prefix @todo: provide default prefix in L{pyxb.namespace.Namespace} """ if not isinstance(namespace, pyxb.namespace.Namespace): raise pyxb.UsageError( 'declareNamespace: must be given a namespace instance') if namespace.isAbsentNamespace(): raise pyxb.UsageError( 'declareNamespace: namespace must not be an absent namespace') if prefix is None: prefix = namespace.prefix() if prefix is None: pfxs = self.__inScopePrefixes.get(namespace) if pfxs: prefix = next(iter(pfxs)) while prefix is None: self.__namespacePrefixCounter += 1 candidate_prefix = 'ns%d' % (self.__namespacePrefixCounter, ) if not (candidate_prefix in self.__inScopeNamespaces): prefix = candidate_prefix ns = self.__inScopePrefixes.get(prefix) if ns: if ns != namespace: raise pyxb.LogicError('Prefix %s is already in use for %s' % (prefix, ns)) return prefix if not self.__mutableInScopeNamespaces: self.__clonePrefixMap() self.__mutableInScopeNamespaces = True self.__addPrefixMap(prefix, namespace) return prefix
def declareNamespace(self, namespace, prefix=None, add_to_map=False): """Add the given namespace as one to be used in this document. @param namespace: The namespace to be associated with the document. @type namespace: L{pyxb.namespace.Namespace} @keyword prefix: Optional prefix to be used with this namespace. If not provided, a unique prefix is generated or a standard prefix is used, depending on the namespace. @keyword add_to_map: If C{False} (default), the prefix is not added to the namespace prefix map. If C{True} it is added. (Often, things added to the prefix map are preserved across resets, which is often not desired for specific prefix/namespace pairs). @todo: ensure multiple namespaces do not share the same prefix @todo: provide default prefix in L{pyxb.namespace.Namespace} @todo: support multiple prefixes for each namespace """ if not isinstance(namespace, pyxb.namespace.Namespace): raise pyxb.UsageError( 'declareNamespace: must be given a namespace instance') if namespace.isAbsentNamespace(): raise pyxb.UsageError( 'declareNamespace: namespace must not be an absent namespace') if prefix is None: prefix = self.__namespaces.get(namespace) if prefix is None: prefix = self.__namespacePrefixMap.get(namespace) if prefix is None: prefix = namespace.prefix() if prefix is None: self.__namespacePrefixCounter += 1 prefix = 'ns%d' % (self.__namespacePrefixCounter, ) if prefix == self.__namespaces.get(namespace): return prefix if prefix in self.__prefixes: raise pyxb.LogicError('Prefix %s is already in use' % (prefix, )) self.__namespaces[namespace] = prefix self.__prefixes.add(prefix) if add_to_map: self.__namespacePrefixMap[namespace] = prefix return prefix
def _makeURINodeNamePair(self, node): """Convert namespace information from a DOM node to text for new DOM node. The namespaceURI and nodeName are extracted and parsed. The namespace (if any) is registered within the document, along with any prefix from the node name. A pair is returned where the first element is the namespace URI or C{None}, and the second is a QName to be used for the expanded name within this document. @param node: An xml.dom.Node instance, presumably from a wildcard match. @rtype: C{( str, str )}""" ns = None if node.namespaceURI is not None: ns = pyxb.namespace.NamespaceForURI(node.namespaceURI, create_if_missing=True) if node.ELEMENT_NODE == node.nodeType: name = node.tagName elif node.ATTRIBUTE_NODE == node.nodeType: name = node.name # saxdom uses the uriTuple as the name field while minidom uses # the QName. @todo saxdom should be fixed. if isinstance(name, tuple): name = name[1] else: raise pyxb.UsageError('Unable to determine name from DOM node %s' % (node, )) pfx = None local_name = name if 0 < name.find(':'): (pfx, local_name) = name.split(':', 1) if ns is None: raise pyxb.LogicError( 'QName with prefix but no available namespace') ns_uri = None node_name = local_name if ns is not None: ns_uri = ns.uri() self.declareNamespace(ns, pfx) if pfx is None: pfx = self.namespacePrefix(ns) if pfx is not None: node_name = '%s:%s' % (pfx, local_name) return (ns_uri, node_name)
def ResolveSiblingNamespaces(sibling_namespaces, origin_uid): for ns in sibling_namespaces: ns.configureCategories([archive.NamespaceArchive._AnonymousCategory()]) ns.validateComponentModel() need_resolved = set(sibling_namespaces) while need_resolved: new_nr = set() for ns in need_resolved: if not ns.needsResolution(): continue #print 'Attempting resolution %s' % (ns.uri(),) if not ns.resolveDefinitions(allow_unresolved=True): print 'Holding incomplete resolution %s' % (ns.uri(), ) new_nr.add(ns) if need_resolved == new_nr: raise pyxb.LogicError( 'Unexpected external dependency in sibling namespaces: %s' % ("\n ".join([str(_ns) for _ns in need_resolved]), )) need_resolved = new_nr
def processXMLNS(self, prefix, uri): from pyxb.namespace import builtin if not self.__mutableInScopeNamespaces: self.__clonePrefixMap() self.__mutableInScopeNamespaces = True if builtin.XML.boundPrefix() == prefix: # Bound prefix xml is permitted if it's bound to the right URI, or # if the scope is being left. In neither case is the mapping # adjusted. if (uri is None) or builtin.XML.uri() == uri: return raise pyxb.LogicError('Cannot manipulate bound prefix xml') if uri: if prefix is None: ns = self.__defaultNamespace = utility.NamespaceForURI( uri, create_if_missing=True) self.__inScopeNamespaces[None] = self.__defaultNamespace else: ns = utility.NamespaceForURI(uri, create_if_missing=True) self.__removePrefixMap(prefix) self.__addPrefixMap(prefix, ns) if self.__targetNamespace: self.__targetNamespace._referenceNamespace(ns) else: self.__pendingReferencedNamespaces.add(ns) else: # NB: XMLNS 6.2 says that you can undefine a default # namespace, but does not say anything explicitly about # undefining a prefixed namespace. XML-Infoset 2.2 # paragraph 6 implies you can do this, but expat blows up # if you try it. I don't think it's legal. if prefix is not None: raise pyxb.NamespaceError( self, 'Attempt to undefine non-default namespace %s' % (prefix, )) self.__removePrefixMap(prefix) self.__defaultNamespace = None
def _resolve(self): """Perform whatever steps are required to resolve this component. Resolution is performed in the context of the namespace to which the component belongs. Invoking this method may fail to complete the resolution process if the component itself depends on unresolved components. The sole caller of this should be L{_NamespaceResolution_mixin.resolveDefinitions}. This method is permitted (nay, encouraged) to raise an exception if resolution requires interpreting a QName and the named component cannot be found. Override this in the child class. In the prefix, if L{isResolved} is true, return right away. If something prevents you from completing resolution, invoke L{self._queueForResolution()} (so it is retried later) and immediately return self. Prior to leaving after successful resolution discard any cached dom node by setting C{self.__domNode=None}. @return: C{self}, whether or not resolution succeeds. @raise pyxb.SchemaValidationError: if resolution requlres a reference to an unknown component """ raise pyxb.LogicError('Resolution not implemented in %s' % (self.__class__, ))
def _validateConstraint_vx(self, value): raise pyxb.LogicError("Facet %s does not implement constraints" % (self.Name(), ))
def PreLoadArchives(cls, archive_path=None, reset=False): """Scan for available archives, associating them with namespaces. This only validates potential archive contents; it does not load namespace data from the archives. @keyword archive_path: A list of files or directories in which namespace archives can be found. The entries are separated by os.pathsep, which is a colon on POSIX platforms and a semi-colon on Windows. See L{PathEnvironmentVariable}. Defaults to L{GetArchivePath()}. If not defaulted, C{reset} will be forced to C{True}. For any directory in the path, all files ending with C{.wxs} are examined. @keyword reset: If C{False} (default), the most recently read set of archives is returned; if C{True}, the archive path is re-scanned and the namespace associations validated. """ from pyxb.namespace import builtin reset = reset or (archive_path is not None) or (cls.__NamespaceArchives is None) if reset: # Get a list of pre-existing archives, initializing the map if # this is the first time through. if cls.__NamespaceArchives is None: cls.__NamespaceArchives = {} existing_archives = set(six.itervalues(cls.__NamespaceArchives)) archive_set = set() # Ensure we have an archive path. If not, don't do anything. if archive_path is None: archive_path = GetArchivePath() if archive_path is not None: # Get archive instances for everything in the archive path candidate_files = pyxb.utils.utility.GetMatchingFiles( archive_path, cls.__ArchivePattern_re, default_path_wildcard='+', default_path=GetArchivePath(), prefix_pattern='&', prefix_substituend=DefaultArchivePrefix) for afn in candidate_files: try: nsa = cls.__GetArchiveInstance( afn, stage=cls._STAGE_readModules) archive_set.add(nsa) except pickle.UnpicklingError: _log.exception('Cannot unpickle archive %s', afn) except pyxb.NamespaceArchiveError: _log.exception('Cannot process archive %s', afn) # Do this for two reasons: first, to get an iterable that won't # cause problems when we remove unresolvable archives from # archive_set; and second to aid with forced dependency inversion # testing ordered_archives = sorted(list(archive_set), key=lambda _a: _a.archivePath()) ordered_archives.reverse() # Create a graph that identifies dependencies between the archives archive_map = {} for a in archive_set: archive_map[a.generationUID()] = a archive_graph = pyxb.utils.utility.Graph() for a in ordered_archives: prereqs = a._unsatisfiedModulePrerequisites() if 0 < len(prereqs): for p in prereqs: if builtin.BuiltInObjectUID == p: continue da = archive_map.get(p) if da is None: _log.warning( '%s depends on unavailable archive %s', a, p) archive_set.remove(a) else: archive_graph.addEdge(a, da) else: archive_graph.addRoot(a) # Verify that there are no dependency loops. archive_scc = archive_graph.sccOrder() for scc in archive_scc: if 1 < len(scc): raise pyxb.LogicError( "Cycle in archive dependencies. How'd you do that?\n " + "\n ".join([_a.archivePath() for _a in scc])) archive = scc[0] if not (archive in archive_set): archive.discard() existing_archives.remove(archive) continue #archive._readToStage(cls._STAGE_COMPLETE) # Discard any archives that we used to know about but now aren't # supposed to. @todo make this friendlier in the case of archives # we've already incorporated. for archive in existing_archives.difference(archive_set): _log.info('Discarding excluded archive %s', archive) archive.discard()
def __init__(self, dom_node=None, parent_context=None, including_context=None, recurse=True, default_namespace=None, target_namespace=None, in_scope_namespaces=None, expanded_name=None, finalize_target_namespace=True ): # MUST BE True for WSDL to work with minidom """Determine the namespace context that should be associated with the given node and, optionally, its element children. @param dom_node: The DOM node @type dom_node: C{xml.dom.Element} @keyword parent_context: Optional value that specifies the context associated with C{dom_node}'s parent node. If not provided, only the C{xml} namespace is in scope. @type parent_context: L{NamespaceContext} @keyword recurse: If True (default), create namespace contexts for all element children of C{dom_node} @type recurse: C{bool} @keyword default_namespace: Optional value to set as the default namespace. Values from C{parent_context} would override this, as would an C{xmlns} attribute in the C{dom_node}. @type default_namespace: L{NamespaceContext} @keyword target_namespace: Optional value to set as the target namespace. Values from C{parent_context} would override this, as would a C{targetNamespace} attribute in the C{dom_node} @type target_namespace: L{NamespaceContext} @keyword in_scope_namespaces: Optional value to set as the initial set of in-scope namespaces. The always-present namespaces are added to this if necessary. @type in_scope_namespaces: C{dict} mapping C{string} to L{Namespace}. """ from pyxb.namespace import builtin if dom_node is not None: try: assert dom_node.__namespaceContext is None except AttributeError: pass dom_node.__namespaceContext = self self.__defaultNamespace = default_namespace self.__targetNamespace = target_namespace self.__inScopeNamespaces = builtin._UndeclaredNamespaceMap self.__mutableInScopeNamespaces = False if in_scope_namespaces is not None: if parent_context is not None: raise pyxb.LogicError( 'Cannot provide both parent_context and in_scope_namespaces' ) self.__inScopeNamespaces = builtin._UndeclaredNamespaceMap.copy() self.__inScopeNamespaces.update(in_scope_namespaces) self.__mutableInScopeNamespaces = True if parent_context is not None: self.__inScopeNamespaces = parent_context.inScopeNamespaces() self.__mutableInScopeNamespaces = False self.__defaultNamespace = parent_context.defaultNamespace() self.__targetNamespace = parent_context.targetNamespace() self.__fallbackToTargetNamespace = parent_context.__fallbackToTargetNamespace if self.__targetNamespace is None: self.__pendingReferencedNamespaces = set() attribute_map = {} if dom_node is not None: if expanded_name is None: expanded_name = pyxb.namespace.ExpandedName(dom_node) for ai in range(dom_node.attributes.length): attr = dom_node.attributes.item(ai) if builtin.XMLNamespaces.uri() == attr.namespaceURI: prefix = attr.localName if 'xmlns' == prefix: prefix = None self.processXMLNS(prefix, attr.value) else: if attr.namespaceURI is not None: uri = utility.NamespaceForURI(attr.namespaceURI, create_if_missing=True) key = pyxb.namespace.ExpandedName(uri, attr.localName) else: key = pyxb.namespace.ExpandedName(None, attr.localName) attribute_map[key] = attr.value if finalize_target_namespace: tns_uri = None tns_attr = self._TargetNamespaceAttribute(expanded_name) if tns_attr is not None: tns_uri = attribute_map.get(tns_attr) self.finalizeTargetNamespace( tns_uri, including_context=including_context) # Store in each node the in-scope namespaces at that node; # we'll need them for QName interpretation of attribute # values. if (dom_node is not None) and recurse: from xml.dom import Node assert Node.ELEMENT_NODE == dom_node.nodeType for cn in dom_node.childNodes: if Node.ELEMENT_NODE == cn.nodeType: NamespaceContext(cn, self, True)
def __init__ (self, *args, **kw): """Create an expanded name. Expected argument patterns are: - ( C{str} ) : the local name in an absent namespace - ( L{ExpandedName} ) : a copy of the given expanded name - ( C{xml.dom.Node} ) : The name extracted from node.namespaceURI and node.localName - ( C{str}, C{str} ) : the namespace URI and the local name - ( L{Namespace}, C{str} ) : the namespace and the local name - ( L{ExpandedName}, C{str}) : the namespace from the expanded name, and the local name Wherever C{str} occurs C{unicode} is also permitted. @keyword fallback_namespace: Optional Namespace instance to use if the namespace would otherwise be None. This is only used if it is an absent namespace. """ fallback_namespace = kw.get('fallback_namespace') if 0 == len(args): raise pyxb.LogicError('Too few arguments to ExpandedName constructor') if 2 < len(args): raise pyxb.LogicError('Too many arguments to ExpandedName constructor') if 2 == len(args): # Namespace(str, unicode, Namespace) and local name basestring ( ns, ln ) = args else: # Local name basestring or ExpandedName or Node assert 1 == len(args) ln = args[0] ns = None if isinstance(ln, six.string_types): pass elif isinstance(ln, tuple) and (2 == len(ln)): (ns, ln) = ln elif isinstance(ln, ExpandedName): ns = ln.namespace() ln = ln.localName() elif isinstance(ln, xml.dom.Node): if not(ln.nodeType in (xml.dom.Node.ELEMENT_NODE, xml.dom.Node.ATTRIBUTE_NODE)): raise pyxb.LogicError('Cannot create expanded name from non-element DOM node %s' % (ln.nodeType,)) ns = ln.namespaceURI ln = ln.localName else: raise pyxb.LogicError('Unrecognized argument type %s' % (type(ln),)) if (ns is None) and (fallback_namespace is not None): if fallback_namespace.isAbsentNamespace(): ns = fallback_namespace if isinstance(ns, six.string_types): ns = NamespaceForURI(ns, create_if_missing=True) if isinstance(ns, ExpandedName): ns = ns.namespace() if (ns is not None) and not isinstance(ns, Namespace): raise pyxb.LogicError('ExpandedName must include a valid (perhaps absent) namespace, or None.') self.__namespace = ns if self.__namespace is not None: self.__namespaceURI = self.__namespace.uri() self.__localName = ln assert self.__localName is not None self.__expandedName = ( self.__namespace, self.__localName ) self.__uriTuple = ( self.__namespaceURI, self.__localName ) super(ExpandedName, self).__init__(*args, **kw)
def isResolved(self): """Determine whether this named component is resolved. Override this in the child class.""" raise pyxb.LogicError('Resolved check not implemented in %s' % (self.__class__, ))
def PreLoadArchives(cls, archive_path=None, required_archive_files=None, reset=False): """Scan for available archives, associating them with namespaces. This only validates potential archive contents; it does not load namespace data from the archives. If invoked with no arguments, @keyword archive_path: A colon-separated list of files or directories in which namespace archives can be found; see L{PathEnvironmentVariable}. Defaults to L{GetArchivePath()}. If not defaulted, C{reset} will be forced to C{True}. For any directory in the path, all files ending with C{.wxs} are examined. @keyword required_archive_files: A list of paths to files that must resolve to valid namespace archives. @keyword reset: If C{False} (default), the most recently read set of archives is returned; if C{True}, the archive path is re-scanned and the namespace associations validated. @return: A list of L{NamespaceArchive} instances corresponding to the members of C{required_archive_files}, in order. If C{required_archive_files} was not provided, returns an empty list. @raise pickle.UnpicklingError: a C{required_archive_files} member does not contain a valid namespace archive. """ import builtin reset = reset or (archive_path is not None) or ( required_archive_files is not None) or (cls.__NamespaceArchives is None) required_archives = [] if reset: # Get a list of pre-existing archives, initializing the map if # this is the first time through. if cls.__NamespaceArchives is None: cls.__NamespaceArchives = {} existing_archives = set(cls.__NamespaceArchives.values()) archive_set = set(required_archives) # Get archives for all required files if required_archive_files is not None: for afn in required_archive_files: required_archives.append( cls.__GetArchiveInstance(afn, stage=cls._STAGE_readModules)) # Ensure we have an archive path. If not, don't do anything. if archive_path is None: archive_path = GetArchivePath() if archive_path is not None: # Get archive instances for everything in the archive path candidate_files = pyxb.utils.utility.GetMatchingFiles( archive_path, cls.__ArchivePattern_re, prefix_pattern='&', prefix_substituend=DefaultArchivePrefix) for afn in candidate_files: #print 'Considering %s' % (afn,) try: nsa = cls.__GetArchiveInstance( afn, stage=cls._STAGE_readModules) archive_set.add(nsa) except pickle.UnpicklingError, e: print 'Cannot use archive %s: %s' % (afn, e) except pyxb.NamespaceArchiveError, e: print 'Cannot use archive %s: %s' % (afn, e) # Do this for two reasons: first, to get an iterable that won't # cause problems when we remove unresolvable archives from # archive_set; and second to aid with forced dependency inversion # testing ordered_archives = sorted( list(archive_set), lambda _a, _b: cmp(_a.archivePath(), _b.archivePath())) ordered_archives.reverse() # Create a graph that identifies dependencies between the archives archive_map = {} for a in archive_set: archive_map[a.generationUID()] = a archive_graph = pyxb.utils.utility.Graph() for a in ordered_archives: prereqs = a._unsatisfiedModulePrerequisites() if 0 < len(prereqs): for p in prereqs: if builtin.BuiltInObjectUID == p: continue da = archive_map.get(p) if da is None: print 'WARNING: %s depends on unavailable archive %s' % ( a, p) archive_set.remove(a) else: #print '%s depends on %s' % (a, da) archive_graph.addEdge(a, da) else: #print '%s has no dependencies' % (a,) archive_graph.addRoot(a) # Verify that there are no dependency loops. archive_scc = archive_graph.sccOrder() for scc in archive_scc: if 1 < len(scc): raise pyxb.LogicError( "Cycle in archive dependencies. How'd you do that?\n " + "\n ".join([_a.archivePath() for _a in scc])) archive = scc[0] if not (archive in archive_set): #print 'Discarding unresolvable %s' % (archive,) archive.discard() existing_archives.remove(archive) continue
def ResolveSiblingNamespaces(sibling_namespaces): """Resolve all components in the sibling_namespaces. @param sibling_namespaces : A set of namespaces expected to be closed under dependency.""" for ns in sibling_namespaces: ns.configureCategories([archive.NamespaceArchive._AnonymousCategory()]) ns.validateComponentModel() def __keyForCompare(dependency_map): """Sort namespaces so dependencies get resolved first. Uses the trick underlying functools.cmp_to_key(), but optimized for this special case. The dependency map is incorporated into the class definition by scope. """ class K(object): def __init__(self, ns, *args): self.__ns = ns # self compares less than other if self.ns is in the dependency set # of other.ns but not vice-versa. def __lt__(self, other): return ((self.__ns in dependency_map.get(other.__ns, set())) \ and not (other.__ns in dependency_map.get(self.__ns, set()))) # self compares equal to other if their namespaces are either # mutually dependent or independent. def __eq__(self, other): return (self.__ns in dependency_map.get( other.__ns, set())) == (other.__ns in dependency_map.get(self.__ns, set())) # All other order metrics are derived. def __ne__(self, other): return not self.__eq__(other) def __le__(self, other): return self.__lt__(other) or self.__eq__(other) def __gt__(self, other): return other.__lt__(self.__ns) def __ge__(self, other): return other.__lt__(self.__ns) or self.__eq__(other) return K need_resolved_set = set(sibling_namespaces) dependency_map = {} last_state = None while need_resolved_set: need_resolved_list = list(need_resolved_set) if dependency_map: need_resolved_list.sort(key=__keyForCompare(dependency_map)) need_resolved_set = set() dependency_map = {} for ns in need_resolved_list: if not ns.needsResolution(): continue if not ns.resolveDefinitions(allow_unresolved=True): deps = dependency_map.setdefault(ns, set()) for (c, dcs) in six.iteritems(ns._unresolvedDependents()): for dc in dcs: dns = dc.expandedName().namespace() if dns != ns: deps.add(dns) _log.info( 'Holding incomplete resolution %s depending on: ', ns.uri(), six.u(' ; ').join([six.text_type(_dns) for _dns in deps])) need_resolved_set.add(ns) # Exception termination check: if we have the same set of incompletely # resolved namespaces, and each has the same number of unresolved # components, assume there's an truly unresolvable dependency: either # due to circularity, or because there was an external namespace that # was missed from the sibling list. state = [] for ns in need_resolved_set: state.append((ns, len(ns._unresolvedComponents()))) state = tuple(state) if last_state == state: raise pyxb.LogicError( 'Unexpected external dependency in sibling namespaces: %s' % (six.u('\n ').join( [six.text_type(_ns) for _ns in need_resolved_set]), )) last_state = state