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"""Classes and global objects related to resolving U{XML 17Namespaces<http://www.w3.org/TR/2006/REC-xml-names-20060816/index.html>}.""" 18 19import logging 20import pyxb 21import pyxb.utils.utility 22from pyxb.namespace import archive, utility 23from pyxb.utils import six 24 25_log = logging.getLogger(__name__) 26 27class _Resolvable_mixin (pyxb.cscRoot): 28 """Mix-in indicating that this object may have references to unseen named components. 29 30 This class is mixed-in to those XMLSchema components that have a reference 31 to another component that is identified by a QName. Resolution of that 32 component may need to be delayed if the definition of the component has 33 not yet been read. 34 """ 35 36 #_TraceResolution = True 37 _TraceResolution = False 38 39 def isResolved (self): 40 """Determine whether this named component is resolved. 41 42 Override this in the child class.""" 43 raise NotImplementedError("_Resolvable_mixin.isResolved in %s"% (type(self).__name__,)) 44 45 def _resolve (self): 46 """Perform whatever steps are required to resolve this component. 47 48 Resolution is performed in the context of the namespace to which the 49 component belongs. Invoking this method may fail to complete the 50 resolution process if the component itself depends on unresolved 51 components. The sole caller of this should be 52 L{_NamespaceResolution_mixin.resolveDefinitions}. 53 54 This method is permitted (nay, encouraged) to raise an exception if 55 resolution requires interpreting a QName and the named component 56 cannot be found. 57 58 Override this in the child class. In the prefix, if L{isResolved} is 59 true, return right away. If something prevents you from completing 60 resolution, invoke L{self._queueForResolution()} (so it is retried 61 later) and immediately return self. Prior to leaving after successful 62 resolution discard any cached dom node by setting C{self.__domNode=None}. 63 64 @return: C{self}, whether or not resolution succeeds. 65 @raise pyxb.SchemaValidationError: if resolution requlres a reference to an unknown component 66 """ 67 raise NotImplementedError("_Resolvable_mixin._resolve in %s"% (type(self).__name__,)) 68 69 def _queueForResolution (self, why=None, depends_on=None): 70 """Short-hand to requeue an object if the class implements _namespaceContext(). 71 """ 72 if (why is not None) and self._TraceResolution: 73 _log.info('Resolution delayed for %s: %s\n\tDepends on: %s', self, why, depends_on) 74 self._namespaceContext().queueForResolution(self, depends_on) 75 76class _NamespaceResolution_mixin (pyxb.cscRoot): 77 """Mix-in that aggregates those aspects of XMLNamespaces relevant to 78 resolving component references. 79 """ 80 81 # A set of namespaces which some schema imported while processing with 82 # this namespace as target. 83 __importedNamespaces = None 84 85 # A set of namespaces which appear in namespace declarations of schema 86 # with this namespace as target. 87 __referencedNamespaces = None 88 89 # A list of Namespace._Resolvable_mixin instances that have yet to be 90 # resolved. 91 __unresolvedComponents = None 92 93 # A map from Namespace._Resolvable_mixin instances in 94 # __unresolvedComponents to sets of other unresolved objects on which they 95 # depend. 96 __unresolvedDependents = None 97 98 def _reset (self): 99 """CSC extension to reset fields of a Namespace. 100 101 This one handles component-resolution--related data.""" 102 getattr(super(_NamespaceResolution_mixin, self), '_reset', lambda *args, **kw: None)() 103 self.__unresolvedComponents = [] 104 self.__unresolvedDependents = {} 105 self.__importedNamespaces = set() 106 self.__referencedNamespaces = set() 107 108 def _getState_csc (self, kw): 109 kw.update({ 110 'importedNamespaces': self.__importedNamespaces, 111 'referencedNamespaces': self.__referencedNamespaces, 112 }) 113 return getattr(super(_NamespaceResolution_mixin, self), '_getState_csc', lambda _kw: _kw)(kw) 114 115 def _setState_csc (self, kw): 116 self.__importedNamespaces = kw['importedNamespaces'] 117 self.__referencedNamespaces = kw['referencedNamespaces'] 118 return getattr(super(_NamespaceResolution_mixin, self), '_setState_csc', lambda _kw: self)(kw) 119 120 def importNamespace (self, namespace): 121 self.__importedNamespaces.add(namespace) 122 return self 123 124 def _referenceNamespace (self, namespace): 125 self._activate() 126 self.__referencedNamespaces.add(namespace) 127 return self 128 129 def importedNamespaces (self): 130 """Return the set of namespaces which some schema imported while 131 processing with this namespace as target.""" 132 return frozenset(self.__importedNamespaces) 133 134 def _transferReferencedNamespaces (self, module_record): 135 assert isinstance(module_record, archive.ModuleRecord) 136 module_record._setReferencedNamespaces(self.__referencedNamespaces) 137 self.__referencedNamespaces.clear() 138 139 def referencedNamespaces (self): 140 """Return the set of namespaces which appear in namespace declarations 141 of schema with this namespace as target.""" 142 return frozenset(self.__referencedNamespaces) 143 144 def queueForResolution (self, resolvable, depends_on=None): 145 """Invoked to note that a component may have references that will need 146 to be resolved. 147 148 Newly created named components are often unresolved, as are components 149 which, in the course of resolution, are found to depend on another 150 unresolved component. 151 152 @param resolvable: An instance of L{_Resolvable_mixin} that is later to 153 be resolved. 154 155 @keyword depends_on: C{None}, or an instance of L{_Resolvable_mixin} 156 which C{resolvable} requires to be resolved in order to resolve 157 itself. 158 159 @return: C{resolvable} 160 """ 161 assert isinstance(resolvable, _Resolvable_mixin) 162 if not resolvable.isResolved(): 163 assert depends_on is None or isinstance(depends_on, _Resolvable_mixin) 164 self.__unresolvedComponents.append(resolvable) 165 if depends_on is not None and not depends_on.isResolved(): 166 from pyxb.xmlschema import structures 167 assert isinstance(depends_on, _Resolvable_mixin) 168 assert isinstance(depends_on, structures._NamedComponent_mixin) 169 self.__unresolvedDependents.setdefault(resolvable, set()).add(depends_on) 170 return resolvable 171 172 def needsResolution (self): 173 """Return C{True} iff this namespace has not been resolved.""" 174 return self.__unresolvedComponents is not None 175 176 def _replaceComponent_csc (self, existing_def, replacement_def): 177 """Replace a component definition if present in the list of unresolved components. 178 """ 179 try: 180 index = self.__unresolvedComponents.index(existing_def) 181 if (replacement_def is None) or (replacement_def in self.__unresolvedComponents): 182 del self.__unresolvedComponents[index] 183 else: 184 assert isinstance(replacement_def, _Resolvable_mixin) 185 self.__unresolvedComponents[index] = replacement_def 186 # Rather than assume the replacement depends on the same 187 # resolvables as the original, just wipe the dependency record: 188 # it'll get recomputed later if it's still important. 189 if existing_def in self.__unresolvedDependents: 190 del self.__unresolvedDependents[existing_def] 191 except ValueError: 192 pass 193 return getattr(super(_NamespaceResolution_mixin, self), '_replaceComponent_csc', lambda *args, **kw: replacement_def)(existing_def, replacement_def) 194 195 def resolveDefinitions (self, allow_unresolved=False): 196 """Loop until all references within the associated resolvable objects 197 have been resolved. 198 199 This method iterates through all components on the unresolved list, 200 invoking the _resolve method of each. If the component could not be 201 resolved in this pass, it iis placed back on the list for the next 202 iteration. If an iteration completes without resolving any of the 203 unresolved components, a pyxb.NotInNamespaceError exception is raised. 204 205 @note: Do not invoke this until all top-level definitions for the 206 namespace have been provided. The resolution routines are entitled to 207 raise a validation exception if a reference to an unrecognized 208 component is encountered. 209 """ 210 if not self.needsResolution(): 211 return True 212 213 while 0 < len(self.__unresolvedComponents): 214 # Save the list of unresolved objects, reset the list to capture 215 # any new objects defined during resolution, and attempt the 216 # resolution for everything that isn't resolved. 217 unresolved = self.__unresolvedComponents 218 219 self.__unresolvedComponents = [] 220 self.__unresolvedDependents = {} 221 for resolvable in unresolved: 222 # Attempt the resolution. 223 resolvable._resolve() 224 225 # Either we resolved it, or we queued it to try again later 226 assert resolvable.isResolved() or (resolvable in self.__unresolvedComponents), 'Lost resolvable %s' % (resolvable,) 227 228 # We only clone things that have scope None. We never 229 # resolve things that have scope None. Therefore, we 230 # should never have resolved something that has 231 # clones. 232 if (resolvable.isResolved() and (resolvable._clones() is not None)): 233 assert False 234 if self.__unresolvedComponents == unresolved: 235 if allow_unresolved: 236 return False 237 # This only happens if we didn't code things right, or the 238 # there is a circular dependency in some named component 239 # (i.e., the schema designer didn't do things right). 240 failed_components = [] 241 from pyxb.xmlschema import structures 242 for d in self.__unresolvedComponents: 243 if isinstance(d, structures._NamedComponent_mixin): 244 failed_components.append('%s named %s' % (d.__class__.__name__, d.name())) 245 else: 246 failed_components.append('Anonymous %s' % (d.__class__.__name__,)) 247 raise pyxb.NotInNamespaceError('Infinite loop in resolution:\n %s' % ("\n ".join(failed_components),)) 248 249 # Replace the list of unresolved components with None, so that 250 # attempts to subsequently add another component fail. 251 self.__unresolvedComponents = None 252 self.__unresolvedDependents = None 253 254 # NOTE: Dependencies may require that we keep these around for a while 255 # longer. 256 # 257 # Remove the namespace context from everything, since we won't be 258 # resolving anything else. 259 self._releaseNamespaceContexts() 260 261 return True 262 263 def _unresolvedComponents (self): 264 """Returns a reference to the list of unresolved components.""" 265 return self.__unresolvedComponents 266 267 def _unresolvedDependents (self): 268 """Returns a map from unresolved components to sets of components that 269 must be resolved first.""" 270 return self.__unresolvedDependents 271 272def ResolveSiblingNamespaces (sibling_namespaces): 273 """Resolve all components in the sibling_namespaces. 274 275 @param sibling_namespaces : A set of namespaces expected to be closed 276 under dependency.""" 277 278 for ns in sibling_namespaces: 279 ns.configureCategories([archive.NamespaceArchive._AnonymousCategory()]) 280 ns.validateComponentModel() 281 282 def __keyForCompare (dependency_map): 283 """Sort namespaces so dependencies get resolved first. 284 285 Uses the trick underlying functools.cmp_to_key(), but optimized for 286 this special case. The dependency map is incorporated into the class 287 definition by scope. 288 """ 289 class K (object): 290 def __init__ (self, ns, *args): 291 self.__ns = ns 292 293 # self compares less than other if self.ns is in the dependency set 294 # of other.ns but not vice-versa. 295 def __lt__ (self, other): 296 return ((self.__ns in dependency_map.get(other.__ns, set())) \ 297 and not (other.__ns in dependency_map.get(self.__ns, set()))) 298 299 # self compares equal to other if their namespaces are either 300 # mutually dependent or independent. 301 def __eq__ (self, other): 302 return (self.__ns in dependency_map.get(other.__ns, set())) == (other.__ns in dependency_map.get(self.__ns, set())) 303 304 # All other order metrics are derived. 305 def __ne__ (self, other): 306 return not self.__eq__(other) 307 def __le__ (self, other): 308 return self.__lt__(other) or self.__eq__(other) 309 def __gt__ (self, other): 310 return other.__lt__(self.__ns) 311 def __ge__ (self, other): 312 return other.__lt__(self.__ns) or self.__eq__(other) 313 return K 314 315 need_resolved_set = set(sibling_namespaces) 316 dependency_map = {} 317 last_state = None 318 while need_resolved_set: 319 need_resolved_list = list(need_resolved_set) 320 if dependency_map: 321 need_resolved_list.sort(key=__keyForCompare(dependency_map)) 322 need_resolved_set = set() 323 dependency_map = {} 324 for ns in need_resolved_list: 325 if not ns.needsResolution(): 326 continue 327 if not ns.resolveDefinitions(allow_unresolved=True): 328 deps = dependency_map.setdefault(ns, set()) 329 for (c, dcs) in six.iteritems(ns._unresolvedDependents()): 330 for dc in dcs: 331 dns = dc.expandedName().namespace() 332 if dns != ns: 333 deps.add(dns) 334 _log.info('Holding incomplete resolution %s depending on: ', ns.uri(), six.u(' ; ').join([ six.text_type(_dns) for _dns in deps ])) 335 need_resolved_set.add(ns) 336 # Exception termination check: if we have the same set of incompletely 337 # resolved namespaces, and each has the same number of unresolved 338 # components, assume there's an truly unresolvable dependency: either 339 # due to circularity, or because there was an external namespace that 340 # was missed from the sibling list. 341 state = [] 342 for ns in need_resolved_set: 343 state.append( (ns, len(ns._unresolvedComponents())) ) 344 state = tuple(state) 345 if last_state == state: 346 raise pyxb.LogicError('Unexpected external dependency in sibling namespaces: %s' % (six.u('\n ').join( [six.text_type(_ns) for _ns in need_resolved_set ]),)) 347 last_state = state 348 349@six.python_2_unicode_compatible 350class NamespaceContext (object): 351 """Records information associated with namespaces at a DOM node. 352 """ 353 354 def __str__ (self): 355 rv = [ six.u('NamespaceContext ') ] 356 if self.defaultNamespace() is not None: 357 rv.extend([ '(defaultNamespace=', six.text_type(self.defaultNamespace()), ') ']) 358 if self.targetNamespace() is not None: 359 rv.extend([ '(targetNamespace=', six.text_type(self.targetNamespace()), ') ']) 360 rv.append("\n") 361 for (pfx, ns) in six.iteritems(self.inScopeNamespaces()): 362 if pfx is not None: 363 rv.append(' xmlns:%s=%s' % (pfx, six.text_type(ns))) 364 return six.u('').join(rv) 365 366 __ContextStack = [] 367 @classmethod 368 def PushContext (cls, ctx): 369 """Make C{ctx} the currently active namespace context. 370 371 Prior contexts are retained on a LIFO stack.""" 372 assert isinstance(ctx, cls) 373 cls.__ContextStack.append(ctx) 374 return ctx 375 376 @classmethod 377 def Current (cls): 378 """Access the currently active namespace context. 379 380 If no context is active, C{None} is returned. This probably 381 represents mis-use of the infrastructure (viz., failure to record the 382 context within which a QName must be resolved).""" 383 if cls.__ContextStack: 384 return cls.__ContextStack[-1] 385 return None 386 387 @classmethod 388 def PopContext (cls): 389 """Discard the currently active namespace context, restoring its 390 predecessor. 391 392 The discarded context is returned.""" 393 return cls.__ContextStack.pop() 394 395 __TargetNamespaceAttributes = { } 396 @classmethod 397 def _AddTargetNamespaceAttribute (cls, expanded_name, attribute_name): 398 assert expanded_name is not None 399 cls.__TargetNamespaceAttributes[expanded_name] = attribute_name 400 @classmethod 401 def _TargetNamespaceAttribute (cls, expanded_name): 402 return cls.__TargetNamespaceAttributes.get(expanded_name) 403 404 # Support for holding onto referenced namespaces until we have a target 405 # namespace to give them to. 406 __pendingReferencedNamespaces = None 407 408 def defaultNamespace (self): 409 """The default namespace in effect at this node. E.g., C{xmlns="URN:default"}.""" 410 return self.__defaultNamespace 411 __defaultNamespace = None 412 413 def setDefaultNamespace (self, default_namespace): 414 """Set the default namespace for the generated document. 415 416 Even if invoked post construction, the default namespace will affect 417 the entire document, as all namespace declarations are placed in the 418 document root. 419 420 @param default_namespace: The namespace to be defined as the default 421 namespace in the top-level element of the document. May be provided 422 as a real namespace, or just its URI. 423 @type default_namespace: L{pyxb.namespace.Namespace} or C{str} or 424 C{unicode}. 425 """ 426 427 if isinstance(default_namespace, six.string_types): 428 default_namespace = utility.NamespaceForURI(default_namespace, create_if_missing=True) 429 if (default_namespace is not None) and default_namespace.isAbsentNamespace(): 430 raise pyxb.UsageError('Default namespace must not be an absent namespace') 431 self.__defaultNamespace = default_namespace 432 433 # If C{True}, this context is within a schema that has no target 434 # namespace, and we should use the target namespace as a fallback if no 435 # default namespace is available and no namespace prefix appears on a 436 # QName. This situation arises when a top-level schema has an absent 437 # target namespace, or when a schema with an absent target namespace is 438 # being included into a schema with a non-absent target namespace. 439 __fallbackToTargetNamespace = False 440 441 def targetNamespace (self): 442 """The target namespace in effect at this node. Usually from the 443 C{targetNamespace} attribute. If no namespace is specified for the 444 schema, an absent namespace was assigned upon creation and will be 445 returned.""" 446 return self.__targetNamespace 447 __targetNamespace = None 448 449 def inScopeNamespaces (self): 450 """Map from prefix strings to L{Namespace} instances associated with those 451 prefixes. The prefix C{None} identifies the default namespace.""" 452 return self.__inScopeNamespaces 453 __inScopeNamespaces = None 454 455 """Map from L{Namespace} instances to sets of prefix strings associated 456 with the namespace. The default namespace is not represented.""" 457 __inScopePrefixes = None 458 459 def __removePrefixMap (self, pfx): 460 ns = self.__inScopeNamespaces.pop(pfx, None) 461 if ns is not None: 462 pfxs = self.__inScopePrefixes.get(ns) 463 if pfxs is not None: 464 pfxs.discard(pfx) 465 466 def __addPrefixMap (self, pfx, ns): 467 # Any previous assignment must have already been removed 468 self.__inScopeNamespaces[pfx] = ns 469 self.__inScopePrefixes.setdefault(ns, set()).add(pfx) 470 471 def __clonePrefixMap (self): 472 self.__inScopeNamespaces = self.__inScopeNamespaces.copy() 473 isp = {} 474 for (ns, pfxs) in six.iteritems(self.__inScopePrefixes): 475 isp[ns] = pfxs.copy() 476 self.__inScopePrefixes = isp 477 478 # Class-scope initial map from prefix to namespace 479 __InitialScopeNamespaces = None 480 # Class-scope initial map from namespace to prefix(es) 481 __InitialScopePrefixes = None 482 # Instance-specific initial map from prefix to namespace 483 __initialScopeNamespaces = None 484 # Instance-specific initial map from namespace to prefix(es) 485 __initialScopePrefixes = None 486 487 488 @classmethod 489 def __BuildInitialPrefixMap (cls): 490 if cls.__InitialScopeNamespaces is not None: 491 return 492 from pyxb.namespace import builtin 493 cls.__InitialScopeNamespaces = builtin._UndeclaredNamespaceMap 494 cls.__InitialScopePrefixes = {} 495 for (pfx, ns) in six.iteritems(cls.__InitialScopeNamespaces): 496 cls.__InitialScopePrefixes.setdefault(ns, set()).add(pfx) 497 498 def prefixForNamespace (self, namespace): 499 """Return a prefix associated with the given namespace in this 500 context, or None if the namespace is the default or is not in 501 scope.""" 502 pfxs = self.__inScopePrefixes.get(namespace) 503 if pfxs: 504 return next(iter(pfxs)) 505 return None 506 507 @classmethod 508 def GetNodeContext (cls, node, **kw): 509 """Get the L{NamespaceContext} instance that was assigned to the node. 510 511 If none has been assigned and keyword parameters are present, create 512 one treating this as the root node and the keyword parameters as 513 configuration information (e.g., default_namespace). 514 515 @raise pyxb.LogicError: no context is available and the keywords 516 required to create one were not provided 517 """ 518 try: 519 return node.__namespaceContext 520 except AttributeError: 521 return NamespaceContext(node, **kw) 522 523 def setNodeContext (self, node): 524 node.__namespaceContext = self 525 526 # Integer counter to help generate unique namespace prefixes 527 __namespacePrefixCounter = None 528 529 def declareNamespace (self, namespace, prefix=None, add_to_map=False): 530 """Record the given namespace as one to be used in this document. 531 532 @param namespace: The namespace to be associated with the document. 533 @type namespace: L{pyxb.namespace.Namespace} 534 535 @keyword prefix: Optional prefix to be used with this namespace. If 536 not provided, a unique prefix is generated or a standard prefix is 537 used, depending on the namespace. 538 539 @return: a prefix that may be used with the namespace. If C{prefix} 540 was C{None} the return value may be a previously-assigned prefix. 541 542 @todo: ensure multiple namespaces do not share the same prefix 543 @todo: provide default prefix in L{pyxb.namespace.Namespace} 544 """ 545 if not isinstance(namespace, pyxb.namespace.Namespace): 546 raise pyxb.UsageError('declareNamespace: must be given a namespace instance') 547 if namespace.isAbsentNamespace(): 548 raise pyxb.UsageError('declareNamespace: namespace must not be an absent namespace') 549 if prefix is None: 550 prefix = namespace.prefix() 551 if prefix is None: 552 pfxs = self.__inScopePrefixes.get(namespace) 553 if pfxs: 554 prefix = next(iter(pfxs)) 555 while prefix is None: 556 self.__namespacePrefixCounter += 1 557 candidate_prefix = 'ns%d' % (self.__namespacePrefixCounter,) 558 if not (candidate_prefix in self.__inScopeNamespaces): 559 prefix = candidate_prefix 560 ns = self.__inScopePrefixes.get(prefix) 561 if ns: 562 if ns != namespace: 563 raise pyxb.LogicError('Prefix %s is already in use for %s' % (prefix, ns)) 564 return prefix 565 if not self.__mutableInScopeNamespaces: 566 self.__clonePrefixMap() 567 self.__mutableInScopeNamespaces = True 568 self.__addPrefixMap(prefix, namespace) 569 return prefix 570 571 def processXMLNS (self, prefix, uri): 572 from pyxb.namespace import builtin 573 if not self.__mutableInScopeNamespaces: 574 self.__clonePrefixMap() 575 self.__mutableInScopeNamespaces = True 576 if builtin.XML.boundPrefix() == prefix: 577 # Bound prefix xml is permitted if it's bound to the right URI, or 578 # if the scope is being left. In neither case is the mapping 579 # adjusted. 580 if (uri is None) or builtin.XML.uri() == uri: 581 return 582 raise pyxb.LogicError('Cannot manipulate bound prefix xml') 583 if uri: 584 if prefix is None: 585 ns = self.__defaultNamespace = utility.NamespaceForURI(uri, create_if_missing=True) 586 self.__inScopeNamespaces[None] = self.__defaultNamespace 587 else: 588 ns = utility.NamespaceForURI(uri, create_if_missing=True) 589 self.__removePrefixMap(prefix) 590 self.__addPrefixMap(prefix, ns) 591 if self.__targetNamespace: 592 self.__targetNamespace._referenceNamespace(ns) 593 else: 594 self.__pendingReferencedNamespaces.add(ns) 595 else: 596 # NB: XMLNS 6.2 says that you can undefine a default 597 # namespace, but does not say anything explicitly about 598 # undefining a prefixed namespace. XML-Infoset 2.2 599 # paragraph 6 implies you can do this, but expat blows up 600 # if you try it. I don't think it's legal. 601 if prefix is not None: 602 raise pyxb.NamespaceError(self, 'Attempt to undefine non-default namespace %s' % (prefix,)) 603 self.__removePrefixMap(prefix) 604 self.__defaultNamespace = None 605 606 def finalizeTargetNamespace (self, tns_uri=None, including_context=None): 607 if tns_uri is not None: 608 assert 0 < len(tns_uri) 609 # Do not prevent overwriting target namespace; need this for WSDL 610 # files where an embedded schema inadvertently inherits a target 611 # namespace from its enclosing definitions element. Note that if 612 # we don't check this here, we do have to check it when schema 613 # documents are included into parent schema documents. 614 self.__targetNamespace = utility.NamespaceForURI(tns_uri, create_if_missing=True) 615 elif self.__targetNamespace is None: 616 if including_context is not None: 617 self.__targetNamespace = including_context.targetNamespace() 618 self.__fallbackToTargetNamespace = True 619 elif tns_uri is None: 620 self.__targetNamespace = utility.CreateAbsentNamespace() 621 else: 622 self.__targetNamespace = utility.NamespaceForURI(tns_uri, create_if_missing=True) 623 if self.__pendingReferencedNamespaces is not None: 624 [ self.__targetNamespace._referenceNamespace(_ns) for _ns in self.__pendingReferencedNamespaces ] 625 self.__pendingReferencedNamespace = None 626 assert self.__targetNamespace is not None 627 if (not self.__fallbackToTargetNamespace) and self.__targetNamespace.isAbsentNamespace(): 628 self.__fallbackToTargetNamespace = True 629 630 def reset (self): 631 """Reset this instance to the state it was when created, exclusive of 632 XMLNS directives passed in a constructor C{dom_node} parameter. 633 634 This preserves parent context and constructor-specified prefix maps, 635 but clears the namespace-prefix mapping of any additions made while 636 processing namespace directives in DOM nodes, or manually added 637 post-construction. 638 639 The defaultNamespace is also retained.""" 640 self.__inScopeNamespaces = self.__initialScopeNamespaces 641 self.__inScopePrefixes = self.__initialScopePrefixes 642 self.__mutableInScopeNamespaces = False 643 self.__namespacePrefixCounter = 0 644 645 def __init__ (self, 646 dom_node=None, 647 parent_context=None, 648 including_context=None, 649 recurse=True, 650 default_namespace=None, 651 target_namespace=None, 652 in_scope_namespaces=None, 653 expanded_name=None, 654 finalize_target_namespace=True): # MUST BE True for WSDL to work with minidom 655 """Determine the namespace context that should be associated with the 656 given node and, optionally, its element children. 657 658 Primarily this class maintains a map between namespaces and prefixes 659 used in QName instances. The initial map comprises the bound prefixes 660 (C{xml} and C{xmlns}), prefixes inherited from C{parent_context}, and 661 prefixes passed through the C{in_scope_namespaces} 662 parameter to the constructor. This map is then augmented by any 663 namespace declarations present in a passed C{dom_node}. The initial 664 map prior to augmentation may be restored through the L{reset()} 665 method. 666 667 @param dom_node: The DOM node 668 @type dom_node: C{xml.dom.Element} 669 @keyword parent_context: Optional value that specifies the context 670 associated with C{dom_node}'s parent node. If not provided, only the 671 C{xml} namespace is in scope. 672 @type parent_context: L{NamespaceContext} 673 @keyword recurse: If True (default), create namespace contexts for all 674 element children of C{dom_node} 675 @type recurse: C{bool} 676 @keyword default_namespace: Optional value to set as the default 677 namespace. Values from C{parent_context} would override this, as 678 would an C{xmlns} attribute in the C{dom_node}. 679 @type default_namespace: L{NamespaceContext} 680 @keyword target_namespace: Optional value to set as the target 681 namespace. Values from C{parent_context} would override this, as 682 would a C{targetNamespace} attribute in the C{dom_node} 683 @type target_namespace: L{NamespaceContext} 684 @keyword in_scope_namespaces: Optional value to set as the initial set 685 of in-scope namespaces. The always-present namespaces are added to 686 this if necessary. 687 @type in_scope_namespaces: C{dict} mapping prefix C{string} to L{Namespace}. 688 """ 689 from pyxb.namespace import builtin 690 691 if dom_node is not None: 692 try: 693 assert dom_node.__namespaceContext is None 694 except AttributeError: 695 pass 696 dom_node.__namespaceContext = self 697 698 self.__defaultNamespace = default_namespace 699 self.__targetNamespace = target_namespace 700 if self.__InitialScopeNamespaces is None: 701 self.__BuildInitialPrefixMap() 702 self.__inScopeNamespaces = self.__InitialScopeNamespaces 703 self.__inScopePrefixes = self.__InitialScopePrefixes 704 self.__mutableInScopeNamespaces = False 705 self.__namespacePrefixCounter = 0 706 707 if parent_context is not None: 708 self.__inScopeNamespaces = parent_context.__inScopeNamespaces 709 self.__inScopePrefixes = parent_context.__inScopePrefixes 710 if parent_context.__mutableInScopeNamespaces: 711 self.__clonePrefixMap() 712 self.__defaultNamespace = parent_context.defaultNamespace() 713 self.__targetNamespace = parent_context.targetNamespace() 714 self.__fallbackToTargetNamespace = parent_context.__fallbackToTargetNamespace 715 if in_scope_namespaces is not None: 716 self.__clonePrefixMap() 717 self.__mutableInScopeNamespaces = True 718 for (pfx, ns) in six.iteritems(in_scope_namespaces): 719 self.__removePrefixMap(pfx) 720 self.__addPrefixMap(pfx, ns) 721 722 # Record a copy of the initial mapping, exclusive of namespace 723 # directives from C{dom_node}, so we can reset to that state. 724 self.__initialScopeNamespaces = self.__inScopeNamespaces 725 self.__initialScopePrefixes = self.__inScopePrefixes 726 self.__mutableInScopeNamespaces = False 727 728 if self.__targetNamespace is None: 729 self.__pendingReferencedNamespaces = set() 730 attribute_map = {} 731 if dom_node is not None: 732 if expanded_name is None: 733 expanded_name = pyxb.namespace.ExpandedName(dom_node) 734 for ai in range(dom_node.attributes.length): 735 attr = dom_node.attributes.item(ai) 736 if builtin.XMLNamespaces.uri() == attr.namespaceURI: 737 prefix = attr.localName 738 if 'xmlns' == prefix: 739 prefix = None 740 self.processXMLNS(prefix, attr.value) 741 else: 742 if attr.namespaceURI is not None: 743 uri = utility.NamespaceForURI(attr.namespaceURI, create_if_missing=True) 744 key = pyxb.namespace.ExpandedName(uri, attr.localName) 745 else: 746 key = pyxb.namespace.ExpandedName(None, attr.localName) 747 attribute_map[key] = attr.value 748 749 if finalize_target_namespace: 750 tns_uri = None 751 tns_attr = self._TargetNamespaceAttribute(expanded_name) 752 if tns_attr is not None: 753 tns_uri = attribute_map.get(tns_attr) 754 self.finalizeTargetNamespace(tns_uri, including_context=including_context) 755 756 # Store in each node the in-scope namespaces at that node; 757 # we'll need them for QName interpretation of attribute 758 # values. 759 if (dom_node is not None) and recurse: 760 from xml.dom import Node 761 assert Node.ELEMENT_NODE == dom_node.nodeType 762 for cn in dom_node.childNodes: 763 if Node.ELEMENT_NODE == cn.nodeType: 764 NamespaceContext(dom_node=cn, parent_context=self, recurse=True) 765 766 def interpretQName (self, name, namespace=None, default_no_namespace=False): 767 """Convert the provided name into an L{ExpandedName}, i.e. a tuple of 768 L{Namespace} and local name. 769 770 If the name includes a prefix, that prefix must map to an in-scope 771 namespace in this context. Absence of a prefix maps to 772 L{defaultNamespace()}, which must be provided (or defaults to the 773 target namespace, if that is not absent). 774 775 @param name: A QName. 776 @type name: C{str} or C{unicode} 777 @param name: Optional namespace to use for unqualified names when 778 there is no default namespace. Note that a defined default namespace, 779 even if absent, supersedes this value. 780 @keyword default_no_namespace: If C{False} (default), an NCName in a 781 context where C{namespace} is C{None} and no default or fallback 782 namespace can be identified produces an exception. If C{True}, such an 783 NCName is implicitly placed in no namespace. 784 @return: An L{ExpandedName} tuple: ( L{Namespace}, C{str} ) 785 @raise pyxb.QNameResolutionError: The prefix is not in scope 786 @raise pyxb.QNameResolutionError: No prefix is given and the default namespace is absent 787 """ 788 if isinstance(name, pyxb.namespace.ExpandedName): 789 return name 790 assert isinstance(name, six.string_types) 791 if 0 <= name.find(':'): 792 (prefix, local_name) = name.split(':', 1) 793 assert self.inScopeNamespaces() is not None 794 namespace = self.inScopeNamespaces().get(prefix) 795 if namespace is None: 796 raise pyxb.QNameResolutionError('No namespace declaration for prefix', name, self) 797 else: 798 local_name = name 799 # Context default supersedes caller-provided namespace 800 if self.defaultNamespace() is not None: 801 namespace = self.defaultNamespace() 802 # If there's no default namespace, but there is a fallback 803 # namespace, use that instead. 804 if (namespace is None) and self.__fallbackToTargetNamespace: 805 namespace = self.targetNamespace() 806 if (namespace is None) and not default_no_namespace: 807 raise pyxb.QNameResolutionError('NCName with no fallback/default namespace cannot be resolved', name, self) 808 return pyxb.namespace.ExpandedName(namespace, local_name) 809 810 def queueForResolution (self, component, depends_on=None): 811 """Forwards to L{queueForResolution()<Namespace.queueForResolution>} in L{targetNamespace()}.""" 812 assert isinstance(component, _Resolvable_mixin) 813 return self.targetNamespace().queueForResolution(component, depends_on) 814 815## Local Variables: 816## fill-column:78 817## End: 818