1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """Classes and global objects related to resolving U{XML
17 Namespaces<http://www.w3.org/TR/2006/REC-xml-names-20060816/index.html>}."""
18
19 import logging
20 import pyxb
21 import pyxb.utils.utility
22 from pyxb.namespace import archive, utility
23 from pyxb.utils import six
24
25 _log = logging.getLogger(__name__)
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
37 _TraceResolution = False
38
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
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
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
77 """Mix-in that aggregates those aspects of XMLNamespaces relevant to
78 resolving component references.
79 """
80
81
82
83 __importedNamespaces = None
84
85
86
87 __referencedNamespaces = None
88
89
90
91 __unresolvedComponents = None
92
93
94
95
96 __unresolvedDependents = None
97
107
114
119
123
128
130 """Return the set of namespaces which some schema imported while
131 processing with this namespace as target."""
132 return frozenset(self.__importedNamespaces)
133
138
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
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
173 """Return C{True} iff this namespace has not been resolved."""
174 return self.__unresolvedComponents is not None
175
194
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
215
216
217 unresolved = self.__unresolvedComponents
218
219 self.__unresolvedComponents = []
220 self.__unresolvedDependents = {}
221 for resolvable in unresolved:
222
223 resolvable._resolve()
224
225
226 assert resolvable.isResolved() or (resolvable in self.__unresolvedComponents), 'Lost resolvable %s' % (resolvable,)
227
228
229
230
231
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
238
239
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
250
251 self.__unresolvedComponents = None
252 self.__unresolvedDependents = None
253
254
255
256
257
258
259 self._releaseNamespaceContexts()
260
261 return True
262
266
268 """Returns a map from unresolved components to sets of components that
269 must be resolved first."""
270 return self.__unresolvedDependents
271
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
294
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
300
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
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
337
338
339
340
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
350 -class 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
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
405
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
434
435
436
437
438
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
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
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
479 __InitialScopeNamespaces = None
480
481 __InitialScopePrefixes = None
482
483 __initialScopeNamespaces = None
484
485 __initialScopePrefixes = None
486
487
488 @classmethod
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):
525
526
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
578
579
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
597
598
599
600
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
610
611
612
613
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
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):
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
723
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
757
758
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
800 if self.defaultNamespace() is not None:
801 namespace = self.defaultNamespace()
802
803
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
816
817
818