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"""The really ugly code that generates the Python bindings.  This
17whole thing is going to be refactored once customized generation makes
18it to the top of the task queue."""
19
20import sys
21import os.path
22import logging
23import logging.config
24import io
25import datetime
26import errno
27
28import pyxb
29import pyxb.xmlschema as xs
30from pyxb.utils import utility, templates, six
31from pyxb.utils.utility import repr2to3
32from pyxb.binding import basis, datatypes, facets
33
34_log = logging.getLogger(__name__)
35
36def PrefixModule (value, text=None):
37    if text is None:
38        text = value.__name__
39    if value.__module__ == datatypes.__name__:
40        return 'pyxb.binding.datatypes.%s' % (text,)
41    if value.__module__ == facets.__name__:
42        return 'pyxb.binding.facets.%s' % (text,)
43    raise ValueError('No standard name for module of value', value)
44
45class ReferenceLiteral (object):
46    """Base class for something that requires fairly complex activity
47    in order to generate its literal value."""
48
49    # Either a STD or a subclass of _Enumeration_mixin, this is the
50    # class in which the referenced object is a member.
51    __ownerClass = None
52
53    # The value to be used as a literal for this object
54    __literal = None
55
56    def __init__ (self, **kw):
57        # NB: Pre-extend __init__
58        self.__ownerClass = kw.get('type_definition')
59
60    def setLiteral (self, literal):
61        self.__literal = literal
62        return literal
63
64    def asLiteral (self):
65        return self.__literal
66
67    def _addTypePrefix (self, text, **kw):
68        if self.__ownerClass is not None:
69            text = '%s.%s' % (pythonLiteral(self.__ownerClass, **kw), text)
70        return text
71
72class ReferenceFacetMember (ReferenceLiteral):
73    __facetClass = None
74
75    def __init__ (self, **kw):
76        variable = kw.get('variable')
77        assert (variable is None) or isinstance(variable, facets.Facet)
78
79        if variable is not None:
80            kw.setdefault('type_definition', variable.ownerTypeDefinition())
81            self.__facetClass = type(variable)
82        self.__facetClass = kw.get('facet_class', self.__facetClass)
83
84        super(ReferenceFacetMember, self).__init__(**kw)
85
86        self.setLiteral(self._addTypePrefix('_CF_%s' % (self.__facetClass.Name(),), **kw))
87
88class ReferenceWildcard (ReferenceLiteral):
89    __wildcard = None
90
91    def __init__ (self, wildcard, **kw):
92        self.__wildcard = wildcard
93        super(ReferenceWildcard, self).__init__(**kw)
94
95        template_map = { }
96        template_map['Wildcard'] = 'pyxb.binding.content.Wildcard'
97        if (xs.structures.Wildcard.NC_any == wildcard.namespaceConstraint()):
98            template_map['nc'] = templates.replaceInText('%{Wildcard}.NC_any', **template_map)
99        elif isinstance(wildcard.namespaceConstraint(), (set, frozenset)):
100            namespaces = []
101            for ns in wildcard.namespaceConstraint():
102                if ns is None:
103                    namespaces.append(None)
104                else:
105                    namespaces.append(ns.uri())
106            template_map['nc'] = 'set([%s])' % (",".join( [ repr2to3(_ns) for _ns in namespaces ]))
107        else:
108            assert isinstance(wildcard.namespaceConstraint(), tuple)
109            ns = wildcard.namespaceConstraint()[1]
110            if ns is not None:
111                ns = ns.uri()
112            template_map['nc'] = templates.replaceInText('(%{Wildcard}.NC_not, %{namespace})', namespace=repr2to3(ns), **template_map)
113        template_map['pc'] = wildcard.processContents()
114        self.setLiteral(templates.replaceInText('%{Wildcard}(process_contents=%{Wildcard}.PC_%{pc}, namespace_constraint=%{nc})', **template_map))
115
116class ReferenceSchemaComponent (ReferenceLiteral):
117    __component = None
118
119    def __init__ (self, component, **kw):
120        self.__component = component
121        binding_module = kw['binding_module']
122        in_class = kw.get('in_class', False)
123        super(ReferenceSchemaComponent, self).__init__(**kw)
124        rv = binding_module.referenceSchemaComponent(component, in_class)
125        self.setLiteral(rv)
126
127class ReferenceNamespace (ReferenceLiteral):
128    __namespace = None
129
130    def __init__ (self, **kw):
131        self.__namespace = kw['namespace']
132        binding_module = kw['binding_module']
133        super(ReferenceNamespace, self).__init__(**kw)
134        rv = binding_module.referenceNamespace(self.__namespace)
135        self.setLiteral(rv)
136
137class ReferenceExpandedName (ReferenceLiteral):
138    __expandedName = None
139
140    def __init__ (self, **kw):
141        self.__expandedName = kw['expanded_name']
142        super(ReferenceExpandedName, self).__init__(**kw)
143        self.setLiteral('pyxb.namespace.ExpandedName(%s, %s)' % (pythonLiteral(self.__expandedName.namespace(), **kw), pythonLiteral(self.__expandedName.localName(), **kw)))
144
145class ReferenceFacet (ReferenceLiteral):
146    __facet = None
147
148    def __init__ (self, **kw):
149        self.__facet = kw['facet']
150        super(ReferenceFacet, self).__init__(**kw)
151        self.setLiteral('%s._CF_%s' % (pythonLiteral(self.__facet.ownerTypeDefinition(), **kw), self.__facet.Name()))
152
153class ReferenceEnumerationMember (ReferenceLiteral):
154    enumerationElement = None
155
156    def __init__ (self, **kw):
157        # NB: Pre-extended __init__
158
159        # All we really need is the enumeration element, so we can get
160        # its tag, and a type definition or datatype, so we can create
161        # the proper prefix.
162
163        # See if we were given a value, from which we can extract the
164        # other information.
165        value = kw.get('enum_value')
166        assert (value is None) or isinstance(value, facets._Enumeration_mixin)
167
168        # Must provide facet_instance, or a value from which it can be
169        # obtained.
170        facet_instance = kw.get('facet_instance')
171        if facet_instance is None:
172            assert isinstance(value, facets._Enumeration_mixin)
173            facet_instance = value._CF_enumeration
174        assert isinstance(facet_instance, facets.CF_enumeration)
175
176        # Must provide the enumeration_element, or a facet_instance
177        # and value from which it can be identified.
178        self.enumerationElement = kw.get('enumeration_element')
179        if self.enumerationElement is None:
180            assert value is not None
181            self.enumerationElement = facet_instance.elementForValue(value)
182        assert isinstance(self.enumerationElement, facets._EnumerationElement)
183        assert self.enumerationElement.tag() is not None
184
185        # If no type definition was provided, use the value datatype
186        # for the facet.
187        kw.setdefault('type_definition', facet_instance.valueDatatype())
188
189        super(ReferenceEnumerationMember, self).__init__(**kw)
190
191        self.setLiteral(self._addTypePrefix(self.enumerationElement.tag(), **kw))
192
193def pythonLiteral (value, **kw):
194    # For dictionaries, apply translation to all values (not keys)
195    if isinstance(value, six.dictionary_type):
196        return ', '.join([ '%s=%s' % (k, pythonLiteral(v, **kw)) for (k, v) in six.iteritems(value) ])
197
198    # For lists, apply translation to all members
199    if isinstance(value, six.list_type):
200        return [ pythonLiteral(_v, **kw) for _v in value ]
201
202    # ExpandedName is a tuple, but not here
203    if isinstance(value, pyxb.namespace.ExpandedName):
204        return pythonLiteral(ReferenceExpandedName(expanded_name=value, **kw))
205
206    # For other collection types, do what you do for list
207    if isinstance(value, (six.tuple_type, set)):
208        return type(value)(pythonLiteral(list(value), **kw))
209
210    # Value is a binding value for which there should be an
211    # enumeration constant.  Return that constant.
212    if isinstance(value, facets._Enumeration_mixin):
213        return pythonLiteral(ReferenceEnumerationMember(enum_value=value, **kw))
214
215    # Value is an instance of a Python binding, e.g. one of the
216    # XMLSchema datatypes.  Use its value, applying the proper prefix
217    # for the module.
218    if isinstance(value, basis.simpleTypeDefinition):
219        return PrefixModule(value, value.pythonLiteral())
220
221    if isinstance(value, pyxb.namespace.Namespace):
222        return pythonLiteral(ReferenceNamespace(namespace=value, **kw))
223
224    if isinstance(value, type):
225        if issubclass(value, basis.simpleTypeDefinition):
226            return PrefixModule(value)
227        if issubclass(value, facets.Facet):
228            return PrefixModule(value)
229
230    if isinstance(value, facets.Facet):
231        return pythonLiteral(ReferenceFacet(facet=value, **kw))
232
233    # Treat pattern elements as their value
234    if isinstance(value, facets._PatternElement):
235        return pythonLiteral(value.pattern)
236
237    # Treat enumeration elements as their value
238    if isinstance(value, facets._EnumerationElement):
239        return pythonLiteral(value.value())
240
241    # Wildcards expand to a pyxb.binding.content.Wildcard instance
242    if isinstance(value, xs.structures.Wildcard):
243        return pythonLiteral(ReferenceWildcard(value, **kw))
244
245    # Schema components have a single name through their lifespan
246    if isinstance(value, xs.structures._SchemaComponent_mixin):
247        return pythonLiteral(ReferenceSchemaComponent(value, **kw))
248
249    # Other special cases
250    if isinstance(value, ReferenceLiteral):
251        return value.asLiteral()
252
253    # Represent namespaces by their URI
254    if isinstance(value, pyxb.namespace.Namespace):
255        return repr2to3(value.uri())
256
257    # Standard Python types, including string types
258    if isinstance(value, (six.none_type, six.boolean_type, six.float_type, six.integer_types, six.string_types)):
259        return pyxb.utils.utility.repr2to3(value)
260
261    raise Exception('Unexpected literal type %s' % (type(value),))
262
263def _GenerateAutomaton (automaton, template_map, containing_state, lines, **kw):
264    binding_module = kw['binding_module']
265    name = utility.PrepareIdentifier('BuildAutomaton', binding_module.uniqueInModule(), protected=True)
266    au_src = []
267    au_src.append(templates.replaceInText('''
268def %{name} ():
269    # Remove this helper function from the namespace after it is invoked
270    global %{name}
271    del %{name}
272    import pyxb.utils.fac as fac
273''', name=name))
274
275    def stateSortKey (st):
276        if isinstance(st.symbol, xs.structures.ModelGroup):
277            return st.symbol.facStateSortKey()
278        return st.symbol[0].facStateSortKey()
279
280    def counterConditionSortKey (cc):
281        return cc.metadata.facStateSortKey()
282
283    def updateInstructionSortKey (ui):
284        return counterConditionSortKey(ui.counterCondition)
285
286    def transitionSortKey (xit):
287        # The destination of a transition is not unique; need to
288        # differentiate using the update instructions.  Which
289        # themselves should be sorted.
290        st = xit.consumingState()
291
292        # Transitions into/out-of subautomata might not include a
293        # consuming state.  Give those a sort value -1, which python3
294        # considers comparable with the non-negative integer sort key
295        # used for states.
296        ssk = -1
297        if st is not None:
298            ssk = stateSortKey(st)
299        keys = [ ssk ]
300        keys.extend(map(updateInstructionSortKey, sorted(xit.updateInstructions, key=updateInstructionSortKey)))
301        return tuple(keys)
302
303    au_src.append('    counters = set()')
304    counter_map = {}
305    sorted_counter_conditions = sorted(automaton.counterConditions, key=counterConditionSortKey)
306    for cc in sorted_counter_conditions:
307        cc_id = 'cc_%u' % (len(counter_map),)
308        counter_map[cc] = cc_id
309        au_src.append('    %s = fac.CounterCondition(min=%s, max=%s, metadata=%r)' % (cc_id, repr2to3(cc.min), repr2to3(cc.max), cc.metadata._location()))
310        au_src.append('    counters.add(%s)' % (cc_id,))
311    state_map = {}
312    au_src.append('    states = []')
313    sorted_states = sorted(automaton.states, key=stateSortKey)
314    for st in sorted_states:
315        st_id = 'st_%u' % (len(state_map),)
316        state_map[st] = st_id
317        if st.subAutomata is not None:
318            au_src.append('    sub_automata = []')
319            for sa in st.subAutomata:
320                au_src.append('    sub_automata.append(%s)' % (_GenerateAutomaton(sa, template_map, st_id, lines, **kw),))
321        if st.finalUpdate is None:
322            au_src.append('    final_update = None')
323        else:
324            au_src.append('    final_update = set()')
325            for ui in sorted(st.finalUpdate, key=updateInstructionSortKey):
326                au_src.append('    final_update.add(fac.UpdateInstruction(%s, %r))' % (counter_map[ui.counterCondition], ui.doIncrement))
327        if isinstance(st.symbol, xs.structures.ModelGroup):
328            au_src.append('    symbol = %r' % (st.symbol._location(),))
329        else:
330            (particle, symbol) = st.symbol
331            if isinstance(symbol, xs.structures.Wildcard):
332                au_src.append(templates.replaceInText('    symbol = pyxb.binding.content.WildcardUse(%{wildcard}, %{location})', wildcard=binding_module.literal(symbol, **kw), location=repr2to3(particle._location())))
333            elif isinstance(symbol, xs.structures.ElementDeclaration):
334                binding_module.importForDeclaration(symbol)
335                au_src.append(templates.replaceInText('    symbol = pyxb.binding.content.ElementUse(%{ctd}._UseForTag(%{field_tag}), %{location})', field_tag=binding_module.literal(symbol.expandedName(), **kw), location=repr2to3(particle._location()), **template_map))
336        au_src.append('    %s = fac.State(symbol, is_initial=%r, final_update=final_update, is_unordered_catenation=%r)' % (st_id, st.isInitial, st.isUnorderedCatenation))
337        if st.subAutomata is not None:
338            au_src.append('    %s._set_subAutomata(*sub_automata)' % (st_id,))
339        au_src.append('    states.append(%s)' % (st_id,))
340    for st in sorted_states:
341        au_src.append('    transitions = []')
342        for xit in sorted(st.transitionSet, key=transitionSortKey):
343            au_src.append('    transitions.append(fac.Transition(%s, [' % (state_map[xit.destination],))
344            sorted_ui = sorted(xit.updateInstructions, key=updateInstructionSortKey)
345            au_src.append('        %s ]))' % (',\n        '.join(map(lambda _ui: 'fac.UpdateInstruction(%s, %r)' % (counter_map[_ui.counterCondition], _ui.doIncrement), sorted_ui))))
346        au_src.append('    %s._set_transitionSet(transitions)' % (state_map[st],))
347    au_src.append('    return fac.Automaton(states, counters, %r, containing_state=%s)' % (automaton.nullable, containing_state))
348    lines.extend(au_src)
349    return '%s()' % (name,)
350
351def GenerateAutomaton (ctd, **kw):
352    aux = _CTDAuxData.Get(ctd)
353    binding_module = kw['binding_module']
354    template_map = { 'ctd' : binding_module.literal(ctd, **kw) }
355    automaton = aux.automaton
356    if automaton is None:
357        return None
358    lines = []
359    name = _GenerateAutomaton(automaton, template_map, 'None', lines, **kw)
360    return (name, lines)
361
362def _useEnumerationTags (td):
363    if td is None:
364        return False
365    assert isinstance(td, xs.structures.SimpleTypeDefinition)
366    ptd = td.baseTypeDefinition()
367    python_support = None
368    # Atomic types that use strings as their representation
369    if (ptd.VARIETY_atomic == ptd.variety()):
370        python_support = ptd.primitiveTypeDefinition().pythonSupport()
371        return issubclass(python_support, six.string_types)
372    # Derivations from anySimpleType use strings too
373    if (ptd.VARIETY_absent == ptd.variety()):
374        return True
375    # Union types?  Yeah, I suppose so.  Though this only applies to
376    # members lifted up into the union.
377    if (ptd.VARIETY_union == ptd.variety()):
378        return True
379    # List types have spaces so no tags.
380    return False
381
382def GenerateFacets (td, generator, **kw):
383    binding_module = kw['binding_module']
384    outf = binding_module.bindingIO()
385    facet_instances = []
386    gen_enum_tag = _useEnumerationTags(td)
387    for (fc, fi) in six.iteritems(td.facets()):
388        #if (fi is None) or (fi.ownerTypeDefinition() != td):
389        #    continue
390        if (fi is None) and (fc in td.baseTypeDefinition().facets()):
391            # Nothing new here
392            continue
393        if (fi is not None) and (fi.ownerTypeDefinition() != td):
394            # Did this one in an ancestor
395            continue
396        argset = { }
397        is_collection = issubclass(fc, facets._CollectionFacet_mixin)
398        if issubclass(fc, facets._LateDatatype_mixin):
399            vdt = td
400            if fc.LateDatatypeBindsSuperclass():
401                vdt = vdt.baseTypeDefinition()
402            argset['value_datatype'] = vdt
403        if fi is not None:
404            if not is_collection:
405                argset['value'] = fi.value()
406            if isinstance(fi, facets.CF_enumeration):
407                argset['enum_prefix'] = fi.enumPrefix()
408        facet_var = ReferenceFacetMember(type_definition=td, facet_class=fc, **kw)
409        outf.write("%s = %s(%s)\n" % binding_module.literal( (facet_var, fc, argset ), **kw))
410        facet_instances.append(binding_module.literal(facet_var, **kw))
411        if (fi is not None) and is_collection:
412            for i in six.iteritems(fi):
413                if isinstance(i, facets._EnumerationElement):
414                    if isinstance(i.value(), pyxb.namespace.ExpandedName):
415                        enum_config = '%s.addEnumeration(value=%s, tag=%s)' % binding_module.literal( ( facet_var, i.value(), i.tag() ), **kw)
416                    else:
417                        enum_config = '%s.addEnumeration(unicode_value=%s, tag=%s)' % binding_module.literal( ( facet_var, i.unicodeValue(), i.tag() ), **kw)
418                    if gen_enum_tag and (i.tag() is not None):
419                        enum_member = ReferenceEnumerationMember(type_definition=td, facet_instance=fi, enumeration_element=i, **kw)
420                        outf.write("%s = %s\n" % (binding_module.literal(enum_member, **kw), enum_config))
421                        if fi.enumPrefix() is not None:
422                            outf.write("%s_%s = %s\n" % (fi.enumPrefix(), i.tag(), binding_module.literal(enum_member, **kw)))
423                    else:
424                        outf.write("%s\n" % (enum_config,))
425                if isinstance(i, facets._PatternElement):
426                    outf.write("%s.addPattern(pattern=%s)\n" % binding_module.literal( (facet_var, i.pattern ), **kw))
427    if gen_enum_tag and (xs.structures.SimpleTypeDefinition.VARIETY_union == td.variety()):
428        # If the union has enumerations of its own, there's no need to
429        # inherit anything, because they supersede anything implicitly
430        # inherited.
431        fi = td.facets().get(facets.CF_enumeration)
432        if fi is None:
433            # Need to expose any enumerations in members up in this class
434            for mtd in td.memberTypeDefinitions():
435                if not _useEnumerationTags(mtd):
436                    continue
437                fi = mtd.facets().get(facets.CF_enumeration)
438                if fi is None:
439                    continue
440                for i in six.iteritems(fi):
441                    assert isinstance(i, facets._EnumerationElement)
442                    etd = i.enumeration().ownerTypeDefinition()
443                    enum_member = ReferenceEnumerationMember(type_definition=td, facet_instance=fi, enumeration_element=i, **kw)
444                    outf.write("%-50s%s\n" % ('%s = %s' % binding_module.literal( (enum_member, i.unicodeValue()) ),
445                                              '# originally %s.%s' % (binding_module.literal(etd), i.tag())))
446    if 2 <= len(facet_instances):
447        map_args = ",\n   ".join(facet_instances)
448    else:
449        map_args = ','.join(facet_instances)
450    outf.write("%s._InitializeFacetMap(%s)\n" % (binding_module.literal(td, **kw), map_args))
451
452def _VCAppendAuxInit (vc_source, aux_init, binding_module, kw):
453    if vc_source.fixed() is not None:
454        aux_init.append('fixed=True')
455        aux_init.append('unicode_default=%s' % (binding_module.literal(vc_source.fixed(), **kw),))
456    elif vc_source.default() is not None:
457        aux_init.append('unicode_default=%s' % (binding_module.literal(vc_source.default(), **kw),))
458
459# If std is a simple type that requires an enumeration mixin, return the
460# corresponding facet; otherwise return None.
461def simpleTypeOwnedEnumerationFacet (std):
462    if not isinstance(std, xs.structures.SimpleTypeDefinition):
463        return None
464    enum_facet = std.facets().get(facets.CF_enumeration)
465    if (enum_facet is not None) and (enum_facet.ownerTypeDefinition() == std):
466        return enum_facet
467    return None
468
469def GenerateSTD (std, generator):
470
471    binding_module = generator.moduleForComponent(std)
472    outf = binding_module.bindingIO()
473
474    class_keywords = frozenset(basis.simpleTypeDefinition._ReservedSymbols)
475    class_unique = set()
476
477    kw = { }
478    kw['binding_module'] = binding_module
479    kw['class_keywords'] = class_keywords
480    kw['class_unique'] = class_unique
481
482    parent_classes = [ binding_module.literal(std.baseTypeDefinition(), **kw) ]
483    if simpleTypeOwnedEnumerationFacet(std) is not None:
484        parent_classes.append('pyxb.binding.basis.enumeration_mixin')
485
486    template_map = { }
487    binding_name = template_map['std'] = binding_module.literal(std, **kw)
488    if (std.expandedName() is not None) and (std.expandedName().localName() != binding_name):
489        _log.warning('Simple type %s renamed to %s', std.expandedName(), binding_name)
490
491    template_map['superclasses'] = ''
492    if 0 < len(parent_classes):
493        template_map['superclasses'] = ', '.join(parent_classes)
494    template_map['expanded_name'] = binding_module.literal(std.expandedName(), **kw)
495    if std.expandedName() is not None:
496        template_map['qname'] = six.text_type(std.expandedName())
497    else:
498        template_map['qname'] = '[anonymous]'
499    template_map['namespaceReference'] = binding_module.literal(std.bindingNamespace(), **kw)
500    template_map['xsd_location'] = repr2to3(std._location())
501    if std.annotation() is not None:
502        template_map['documentation'] = std.annotation().asDocString()
503        template_map['documentation_expr'] = binding_module.literal(std.annotation().text())
504    else:
505        template_map['documentation'] = ''
506        template_map['documentation_expr'] = binding_module.literal(None)
507
508    # @todo: Extensions of LIST will be wrong in below
509
510    common_template = '''
511    """%{documentation}"""
512
513    _ExpandedName = %{expanded_name}
514    _XSDLocation = %{xsd_location}
515    _Documentation = %{documentation_expr}
516'''
517    if xs.structures.SimpleTypeDefinition.VARIETY_absent == std.variety():
518        template = '''
519# The ur simple type: %{qname}
520class %{std} (%{superclasses}):
521''' + common_template
522        if not template_map['documentation']:
523            template_map['documentation'] = 'The ur simple type.'
524    elif xs.structures.SimpleTypeDefinition.VARIETY_atomic == std.variety():
525        template = '''
526# Atomic simple type: %{qname}
527class %{std} (%{superclasses}):
528''' + common_template
529        if not template_map['documentation']:
530            template_map['documentation'] = 'An atomic simple type.'
531    elif xs.structures.SimpleTypeDefinition.VARIETY_list == std.variety():
532        template = '''
533# List simple type: %{qname}
534# superclasses %{superclasses}
535class %{std} (pyxb.binding.basis.STD_list):
536''' + common_template + '''
537    _ItemType = %{itemtype}
538'''
539        template_map['itemtype'] = binding_module.literal(std.itemTypeDefinition(), **kw)
540        if not template_map['documentation']:
541            template_map['documentation'] = templates.replaceInText('Simple type that is a list of %{itemtype}.', **template_map)
542    elif xs.structures.SimpleTypeDefinition.VARIETY_union == std.variety():
543        template = '''
544# Union simple type: %{qname}
545# superclasses %{superclasses}
546class %{std} (pyxb.binding.basis.STD_union):
547''' + common_template + '''
548    _MemberTypes = ( %{membertypes}, )
549'''
550        template_map['membertypes'] = ", ".join( [ binding_module.literal(_mt, **kw) for _mt in std.memberTypeDefinitions() ])
551        if not template_map['documentation']:
552            template_map['documentation'] = templates.replaceInText('Simple type that is a union of %{membertypes}.', **template_map)
553    else:
554        raise pyxb.LogicError("Unhandled STD variety")
555
556    outf.write(templates.replaceInText(template, **template_map))
557
558    GenerateFacets(std, generator, **kw)
559
560    if std.name() is not None:
561        outf.write(templates.replaceInText("%{namespaceReference}.addCategoryObject('typeBinding', %{localName}, %{std})\n",
562                                           localName=binding_module.literal(std.name(), **kw), **template_map))
563    outf.write(templates.replaceInText('_module_typeBindings.%{std} = %{std}\n', **template_map))
564
565def elementDeclarationMap (ed, binding_module, **kw):
566    template_map = { }
567    template_map['qname'] = six.text_type(ed.expandedName())
568    template_map['decl_location'] = repr2to3(ed._location())
569    template_map['namespaceReference'] = binding_module.literal(ed.bindingNamespace(), **kw)
570    if (ed.SCOPE_global == ed.scope()):
571        binding_name = template_map['class'] = binding_module.literal(ed, **kw)
572        if ed.expandedName().localName() != binding_name:
573            _log.warning('Element %s renamed to %s', ed.expandedName(), binding_name)
574        template_map['localName'] = binding_module.literal(ed.name(), **kw)
575        template_map['map_update'] = templates.replaceInText("%{namespaceReference}.addCategoryObject('elementBinding', %{localName}, %{class})", **template_map)
576    else:
577        template_map['scope'] = binding_module.literal(ed.scope(), **kw)
578    if ed.annotation() is not None:
579        template_map['documentation'] = binding_module.literal(six.text_type(ed.annotation()))
580    if ed.abstract():
581        template_map['abstract'] = binding_module.literal(ed.abstract(), **kw)
582    if ed.nillable():
583        template_map['nillable'] = binding_module.literal(ed.nillable(), **kw)
584    if ed.default():
585        template_map['defaultValue'] = binding_module.literal(ed.default(), **kw)
586    template_map['typeDefinition'] = binding_module.literal(ed.typeDefinition(), **kw)
587    if ed.substitutionGroupAffiliation():
588        template_map['substitution_group'] = binding_module.literal(ed.substitutionGroupAffiliation(), **kw)
589    aux_init = []
590    for k in ( 'nillable', 'abstract', 'scope', 'documentation' ):
591        if k in template_map:
592            aux_init.append('%s=%s' % (k, template_map[k]))
593    aux_init.append('location=%s' % (template_map['decl_location'],))
594    _VCAppendAuxInit(ed, aux_init, binding_module, kw)
595    template_map['element_aux_init'] = ''
596    if 0 < len(aux_init):
597        template_map['element_aux_init'] = ', ' + ', '.join(aux_init)
598
599    return template_map
600
601import pyxb.utils.fac
602import operator
603import functools
604
605# A Symbol in the term tree is a pair consisting of the containing
606# particle (for location information) and one of an
607# ElementDeclaration, Wildcard, or tuple of sub-term-trees for All
608# model groups.
609
610def BuildTermTree (node):
611    """Construct a L{FAC term tree<pyxb.utils.fac.Node>} for a L{particle<xs.structures.Particle>}.
612
613    This translates the XML schema content model of particles, model
614    groups, element declarations, and wildcards into a tree expressing
615    the corresponding content as a regular expression with numerical
616    constraints.
617
618    @param node: An instance of L{xs.structures.Particle}
619
620    @return: An instance of L{pyxb.utils.fac.Node}
621    """
622
623    def _generateTermTree_visitor (node, entered, arg):
624        """Helper for constructing a L{FAC term tree<pyxb.utils.fac.Node>}.
625
626        This is passed to L{xs.structures.Particle.walkParticleTree}.
627
628        @param node: An instance of L{xs.structures._ParticleTree_mixin}
629
630        @param entered: C{True} entering an interior tree node, C{False}
631        leaving an interior tree node, C{None} at a leaf node.
632
633        @param arg: A list of pairs C{(particle, terms)} where C{particle}
634        is the L{xs.structures.Particle} instance containing a list of
635        L{term trees<pyxb.utils.fac.Node>}.
636        """
637
638        if entered is None:
639            (parent_particle, terms) = arg.peekNodeTermPair()
640            assert isinstance(parent_particle, xs.structures.Particle)
641            assert isinstance(node, (xs.structures.ElementDeclaration, xs.structures.Wildcard))
642            node._setFacStateSortKey(arg.nextSequenceNumber())
643            terms.append(pyxb.utils.fac.Symbol((parent_particle, node)))
644        elif entered:
645            node._setFacStateSortKey(arg.nextSequenceNumber())
646            arg.addNode(node)
647        else:
648            (xnode, terms) = arg.popNodeTermPair()
649            assert xnode == node
650            (parent_particle, siblings) = arg.peekNodeTermPair()
651            if 1 == len(terms):
652                term = terms[0]
653                # Either node is a Particle, or it's a single-member model
654                # group.  If it's a non-trivial particle we need a
655                # numerical constraint; if it's a single-member model
656                # group or a trivial particle we can use the term
657                # directly.
658                if isinstance(node, xs.structures.Particle) and ((1 != node.minOccurs()) or (1 != node.maxOccurs())):
659                    term = pyxb.utils.fac.NumericalConstraint(term, node.minOccurs(), node.maxOccurs(), metadata=node)
660            else:
661                assert isinstance(parent_particle, xs.structures.Particle), 'unexpected %s' % (parent_particle,)
662                assert isinstance(node, xs.structures.ModelGroup)
663                if node.C_CHOICE == node.compositor():
664                    term = pyxb.utils.fac.Choice(*terms, metadata=node)
665                elif node.C_SEQUENCE == node.compositor():
666                    term = pyxb.utils.fac.Sequence(*terms, metadata=node)
667                else:
668                    # The quadratic state explosion and need to clone
669                    # terms that results from a naive transformation of
670                    # unordered catenation to choices among sequences of
671                    # nodes and recursively-defined catenation expressions
672                    # is not worth the pain.  Create a "symbol" for the
673                    # state and hold the alternatives in it.
674                    assert node.C_ALL == node.compositor()
675                    assert functools.reduce(operator.and_, map(lambda _s: isinstance(_s, pyxb.utils.fac.Node), terms), True)
676                    term = pyxb.utils.fac.All(*terms, metadata=node)
677            siblings.append(term)
678
679    class TermTreeArg (object):
680        __sequenceNumber = None
681        __termTreeList = None
682        __nodeTermPairs = None
683        def __init__ (self, node):
684            self.__sequenceNumber = 0
685            self.__termTreeList = []
686            self.__nodeTermPairs = [ (node, self.__termTreeList) ]
687
688        def termTree (self):
689            assert 1 == len(self.__nodeTermPairs)
690            assert 1 == len(self.__termTreeList)
691            return self.__termTreeList[0]
692
693        def peekNodeTermPair (self):
694            return self.__nodeTermPairs[-1]
695
696        def popNodeTermPair (self):
697            return self.__nodeTermPairs.pop()
698
699        def addNode (self, node):
700            self.__nodeTermPairs.append((node, []))
701
702        def nextSequenceNumber (self):
703            rv = self.__sequenceNumber
704            self.__sequenceNumber += 1
705            return rv
706
707    assert isinstance(node, xs.structures.Particle)
708    ttarg = TermTreeArg(node)
709    node.walkParticleTree(_generateTermTree_visitor, ttarg)
710    term_tree = ttarg.termTree()
711    return term_tree
712
713def BuildPluralityData (term_tree):
714    """Walk a term tree to determine which element declarations may
715    appear multiple times.
716
717    The bindings need to use a list for any Python attribute
718    corresponding to an element declaration that can occur multiple
719    times in the content model.  The number of occurrences is
720    determined by the occurrence constraints on parent particles and
721    the compositors of containing model groups.  All this information
722    is available in the term tree used for the content model
723    automaton.
724
725    @param term_tree: A L{FAC term tree<pyxb.utils.fac.Node>}
726    representing the content model for a complex data type.
727
728    @return: Plurality data, as a pair C{(singles, multiples)} where
729    C{singles} is a set of base L{element
730    declarations<xs.structures.ElementDeclaration>} that are known to
731    occur at least once and at most once in a region of the content,
732    and C{multiples} is a similar set of declarations that are known
733    to potentially occur more than once."""
734
735    def _ttMergeSets (parent, child):
736        (p1, pm) = parent
737        (c1, cm) = child
738
739        # Anything multiple in the child becomes multiple in the parent.
740        pm.update(cm)
741
742        # Anything independently occuring once in both parent and child
743        # becomes multiple in the parent.
744        pm.update(c1.intersection(p1))
745
746        # Anything that was single in the parent (child) but is now
747        # multiple is no longer single.
748        p1.difference_update(pm)
749        c1.difference_update(pm)
750
751        # Anything that was single in the parent and also single in the
752        # child is no longer single in the parent.
753        p1.symmetric_difference_update(c1)
754
755    def _ttPrePluralityWalk (node, pos, arg):
756        # If there are multiple children, create a new list on which they
757        # will be placed.
758        if isinstance(node, pyxb.utils.fac.MultiTermNode):
759            arg.append([])
760
761    def _ttPostPluralityWalk (node, pos, arg):
762        # Initialize a fresh result for this node
763        singles = set()
764        multiples = set()
765        combined = (singles, multiples)
766        if isinstance(node, pyxb.utils.fac.MultiTermNode):
767            # Get the list of children, and examine
768            term_list = arg.pop()
769            if isinstance(node, pyxb.utils.fac.Choice):
770                # For choice we aggregate the singles and multiples
771                # separately.
772                for (t1, tm) in term_list:
773                    multiples.update(tm)
774                    singles.update(t1)
775            else:
776                # For sequence (ordered or not) we merge the children
777                assert isinstance(node, (pyxb.utils.fac.Sequence, pyxb.utils.fac.All))
778                for tt in term_list:
779                    _ttMergeSets(combined, tt)
780        elif isinstance(node, pyxb.utils.fac.Symbol):
781            (particle, term) = node.metadata
782            if isinstance(term, xs.structures.ElementDeclaration):
783                # One instance of the base declaration for the element
784                singles.add(term.baseDeclaration())
785            elif isinstance(term, xs.structures.Wildcard):
786                pass
787            else:
788                assert isinstance(term, list)
789                # Unordered catenation is the same as ordered catenation.
790                for tt in term:
791                    _ttMergeSets(combined, BuildPluralityData(tt))
792        else:
793            assert isinstance(node, pyxb.utils.fac.NumericalConstraint)
794            # Grab the data for the topmost tree and adjust it based on
795            # occurrence data.
796            combined = arg[-1].pop()
797            (singles, multiples) = combined
798            if 0 == node.max:
799                # If the node can't match at all, there are no occurrences
800                # at all
801                multiples.clear()
802                singles.clear()
803            elif 1 == node.max:
804                # If the node can only match once, what we've got is right
805                pass
806            else:
807                # If the node can match multiple times, there are no
808                # singles.
809                multiples.update(singles)
810                singles.clear()
811        arg[-1].append(combined)
812
813    # Initialize state with an implied parent that currently has no
814    # children
815    arg = [[]]
816    term_tree.walkTermTree(_ttPrePluralityWalk, _ttPostPluralityWalk, arg)
817
818    # The result term tree is the single child of that implied parent
819    assert 1 == len(arg)
820    arg = arg[0]
821    assert 1 == len(arg)
822    return arg[0]
823
824class _CTDAuxData (object):
825    """Helper class holding information need in both preparation and generation."""
826
827    contentBasis = None
828    termTree = None
829    edSingles = None
830    edMultiples = None
831    automaton = None
832    ctd = None
833
834    def __init__ (self, ctd):
835        self.ctd = ctd
836        ctd.__auxData = self
837        self.contentBasis = ctd.contentType()[1]
838        if isinstance(self.contentBasis, xs.structures.Particle):
839            self.termTree = BuildTermTree(self.contentBasis)
840            self.automaton = self.termTree.buildAutomaton()
841            (self.edSingles, self.edMultiples) = BuildPluralityData(self.termTree)
842        else:
843            self.edSingles = set()
844            self.edMultiples = set()
845
846    @classmethod
847    def Create (cls, ctd):
848        return cls(ctd)
849
850    @classmethod
851    def Get (cls, ctd):
852        return ctd.__auxData
853
854def GenerateCTD (ctd, generator, **kw):
855    binding_module = generator.moduleForComponent(ctd)
856    outf = binding_module.bindingIO()
857
858    prolog_template = None
859    template_map = { }
860    binding_name = template_map['ctd'] = binding_module.literal(ctd, **kw)
861    if (ctd.expandedName() is not None) and (ctd.expandedName().localName() != binding_name):
862        _log.warning('Complex type %s renamed to %s', ctd.expandedName(), binding_name)
863
864    base_type = ctd.baseTypeDefinition()
865    content_type_tag = ctd._contentTypeTag()
866
867    template_map['base_type'] = binding_module.literal(base_type, **kw)
868    template_map['namespaceReference'] = binding_module.literal(ctd.bindingNamespace(), **kw)
869    template_map['expanded_name'] = binding_module.literal(ctd.expandedName(), **kw)
870    if ctd.expandedName() is not None:
871        template_map['qname'] = six.text_type(ctd.expandedName())
872    else:
873        template_map['qname'] = '[anonymous]'
874    template_map['xsd_location'] = repr2to3(ctd._location())
875    template_map['simple_base_type'] = binding_module.literal(None, **kw)
876    template_map['contentTypeTag'] = content_type_tag
877    template_map['is_abstract'] = repr2to3(not not ctd.abstract())
878
879    content_basis = None
880    if (ctd.CT_SIMPLE == content_type_tag):
881        content_basis = ctd.contentType()[1]
882        template_map['simple_base_type'] = binding_module.literal(content_basis, **kw)
883    elif (ctd.CT_MIXED == content_type_tag):
884        content_basis = ctd.contentType()[1]
885    elif (ctd.CT_ELEMENT_ONLY == content_type_tag):
886        content_basis = ctd.contentType()[1]
887
888    if ctd.annotation() is not None:
889        template_map['documentation'] = ctd.annotation().asDocString()
890    elif isinstance(ctd.owner(), xs.structures.ElementDeclaration) \
891      and ctd.owner().annotation() is not None:
892        template_map['documentation'] = ctd.owner().annotation().asDocString()
893    else:
894        template_map['documentation'] = templates.replaceInText("Complex type %{qname} with content type %{contentTypeTag}", **template_map)
895
896    prolog_template = '''
897# Complex type %{qname} with content type %{contentTypeTag}
898class %{ctd} (%{superclass}):
899    """%{documentation}"""
900    _TypeDefinition = %{simple_base_type}
901    _ContentTypeTag = pyxb.binding.basis.complexTypeDefinition._CT_%{contentTypeTag}
902    _Abstract = %{is_abstract}
903    _ExpandedName = %{expanded_name}
904    _XSDLocation = %{xsd_location}
905'''
906
907    # Complex types that inherit from non-ur-type complex types should
908    # have their base type as their Python superclass, so pre-existing
909    # elements and attributes can be re-used.
910    inherits_from_base = True
911    template_map['superclass'] = binding_module.literal(base_type, **kw)
912    if ctd._isHierarchyRoot():
913        inherits_from_base = False
914        template_map['superclass'] = 'pyxb.binding.basis.complexTypeDefinition'
915        assert base_type.nameInBinding() is not None
916
917    if inherits_from_base:
918        prolog_template += '''    _ElementMap = %{superclass}._ElementMap.copy()
919    _AttributeMap = %{superclass}._AttributeMap.copy()
920'''
921    else:
922        prolog_template += '''    _ElementMap = {}
923    _AttributeMap = {}
924'''
925
926    # Support for deconflicting attributes, elements, and reserved symbols
927    class_keywords = frozenset(basis.complexTypeDefinition._ReservedSymbols)
928    class_unique = set()
929
930    # Deconflict elements first, attributes are lower priority.
931    # Expectation is that all elements that have the same tag in the
932    # XML are combined into the same instance member, even if they
933    # have different types.  Determine what name that should be, and
934    # whether there might be multiple instances of elements of that
935    # name.
936    element_uses = []
937
938    definitions = []
939
940    definitions.append('# Base type is %{base_type}')
941
942    # Retain in the ctd the information about the element
943    # infrastructure, so it can be inherited where appropriate in
944    # subclasses.
945
946    if isinstance(content_basis, xs.structures.Particle):
947        aux = _CTDAuxData.Get(ctd)
948        elements = aux.edSingles.union(aux.edMultiples)
949
950        outf.postscript().append("\n\n")
951        for ed in sorted(elements, key=lambda _c: _c.schemaOrderSortKey()):
952            is_plural = ed in aux.edMultiples
953            # @todo Detect and account for plurality change between this and base
954            ef_map = ed._templateMap()
955            if ed.scope() == ctd:
956                ef_map.update(elementDeclarationMap(ed, binding_module, **kw))
957                aux_init = []
958                ef_map['is_plural'] = repr2to3(is_plural)
959                element_uses.append(templates.replaceInText('%{use}.name() : %{use}', **ef_map))
960                if 0 == len(aux_init):
961                    ef_map['aux_init'] = ''
962                else:
963                    ef_map['aux_init'] = ', ' + ', '.join(aux_init)
964                ef_map['element_binding'] = utility.PrepareIdentifier('%s_elt' % (ef_map['id'],), class_unique, class_keywords, private=True)
965                if ed.annotation() is not None:
966                    ef_map['documentation'] = binding_module.literal(six.text_type(ed.annotation()))
967                else:
968                    ef_map['documentation'] = binding_module.literal(None)
969            if ed.scope() != ctd:
970                definitions.append(templates.replaceInText('''
971    # Element %{id} (%{qname}) inherited from %{decl_type_en}''', decl_type_en=six.text_type(ed.scope().expandedName()), **ef_map))
972                continue
973
974            binding_module.importForDeclaration(ed)
975            if ed.expandedName().localName() != ef_map['id']:
976                _log.warning('Element use %s.%s renamed to %s', ctd.expandedName(), ed.expandedName(), ef_map['id'])
977            definitions.append(templates.replaceInText('''
978    # Element %{qname} uses Python identifier %{id}
979    %{use} = pyxb.binding.content.ElementDeclaration(%{name_expr}, '%{id}', '%{key}', %{is_plural}, %{decl_location}, %{aux_init})
980''', name_expr=binding_module.literal(ed.expandedName(), **kw), **ef_map))
981
982            definitions.append(templates.replaceInText('''
983    %{inspector} = property(%{use}.value, %{use}.set, None, %{documentation})
984''', **ef_map))
985            outf.postscript().append(templates.replaceInText('''
986%{ctd}._AddElement(pyxb.binding.basis.element(%{name_expr}, %{typeDefinition}%{element_aux_init}))
987''', name_expr=binding_module.literal(ed.expandedName(), **kw), ctd=template_map['ctd'], **ef_map))
988
989        auto_defn = GenerateAutomaton(ctd, binding_module=binding_module, **kw)
990        if auto_defn is not None:
991            (automaton_ctor, lines) = auto_defn
992            if lines:
993                outf.postscript().append("\n".join(lines))
994                outf.postscript().append("\n")
995            outf.postscript().append(templates.replaceInText('%{ctd}._Automaton = %{automaton_ctor}\n', ctd=template_map['ctd'], automaton_ctor=automaton_ctor))
996            outf.postscript().append("\n")
997
998    # Create definitions for all attributes.
999    attribute_uses = []
1000
1001    # name - String value of expanded name of the attribute (attr_tag, attr_ns)
1002    # name_expr - Python expression for an expanded name identifying the attribute (attr_tag)
1003    # use - Binding variable name holding AttributeUse instance (attr_name)
1004    # id - Python identifier for attribute (python_attr_name)
1005    # key - String used as dictionary key holding instance value of attribute (value_attr_name)
1006    # inspector - Name of the method used for inspection (attr_inspector)
1007    # mutator - Name of the method use for mutation (attr_mutator)
1008    for au in sorted(ctd.attributeUses(), key=lambda _au: _au.attributeDeclaration().schemaOrderSortKey()):
1009        ad = au.attributeDeclaration()
1010        assert isinstance(ad.scope(), xs.structures.ComplexTypeDefinition), 'unexpected scope %s' % (ad.scope(),)
1011        au_map = ad._templateMap()
1012        if ad.scope() != ctd:
1013            definitions.append(templates.replaceInText('''
1014    # Attribute %{id} inherited from %{decl_type_en}''', decl_type_en=six.text_type(ad.scope().expandedName()), **au_map))
1015            continue
1016        assert isinstance(au_map, dict)
1017        aur = au
1018        while aur.restrictionOf() is not None:
1019            aur = aur.restrictionOf()
1020        if au != aur:
1021            au_map = aur.attributeDeclaration()._templateMap().copy()
1022            definitions.append(templates.replaceInText('''
1023    # Attribute %{id} is restricted from parent''', **au_map))
1024
1025        assert ad.typeDefinition() is not None
1026        au_map['attr_type'] = binding_module.literal(ad.typeDefinition(), in_class=True, **kw)
1027        au_map['decl_location'] = repr2to3(ad._location())
1028        au_map['use_location'] = repr2to3(au._location())
1029
1030        vc_source = ad
1031        if au.valueConstraint() is not None:
1032            vc_source = au
1033        aux_init = []
1034        _VCAppendAuxInit(vc_source, aux_init, binding_module, kw)
1035        if au.required():
1036            aux_init.append('required=True')
1037        if au.prohibited():
1038            aux_init.append('prohibited=True')
1039        if 0 == len(aux_init):
1040            au_map['aux_init'] = ''
1041        else:
1042            aux_init.insert(0, '')
1043            au_map['aux_init'] = ', '.join(aux_init)
1044        if ad.annotation() is not None:
1045            au_map['documentation'] = binding_module.literal(six.text_type(ad.annotation()))
1046        else:
1047            au_map['documentation'] = binding_module.literal(None)
1048
1049        binding_module.importForDeclaration(ad)
1050        attribute_uses.append(templates.replaceInText('%{use}.name() : %{use}', **au_map))
1051        if ad.expandedName().localName() != au_map['id']:
1052            _log.warning('Attribute %s.%s renamed to %s', ctd.expandedName(), ad.expandedName(), au_map['id'])
1053        definitions.append(templates.replaceInText('''
1054    # Attribute %{qname} uses Python identifier %{id}
1055    %{use} = pyxb.binding.content.AttributeUse(%{name_expr}, '%{id}', '%{key}', %{attr_type}%{aux_init})
1056    %{use}._DeclarationLocation = %{decl_location}
1057    %{use}._UseLocation = %{use_location}''', name_expr=binding_module.literal(ad.expandedName(), **kw), **au_map))
1058        definitions.append(templates.replaceInText('''
1059    %{inspector} = property(%{use}.value, %{use}.set, None, %{documentation})
1060''', ctd=template_map['ctd'], **au_map))
1061
1062    if ctd.attributeWildcard() is not None:
1063        definitions.append('_AttributeWildcard = %s' % (binding_module.literal(ctd.attributeWildcard(), **kw),))
1064    if ctd.hasWildcardElement():
1065        definitions.append('_HasWildcardElement = True')
1066    template_map['attribute_uses'] = ",\n        ".join(attribute_uses)
1067    template_map['element_uses'] = ",\n        ".join(element_uses)
1068
1069    template_map['registration'] = templates.replaceInText('_module_typeBindings.%{ctd} = %{ctd}', **template_map)
1070    if ctd.name() is not None:
1071        template_map['registration'] += templates.replaceInText("\n%{namespaceReference}.addCategoryObject('typeBinding', %{localName}, %{ctd})",
1072                                                                localName=binding_module.literal(ctd.name(), **kw), **template_map)
1073
1074    template = ''.join([prolog_template,
1075               "    ", "\n    ".join(definitions), "\n",
1076               '''    _ElementMap.update({
1077        %{element_uses}
1078    })
1079    _AttributeMap.update({
1080        %{attribute_uses}
1081    })
1082%{registration}
1083
1084'''])
1085
1086    outf.write(template, **template_map)
1087
1088def GenerateED (ed, generator, **kw):
1089    # Unscoped declarations should never be referenced in the binding.
1090    assert ed._scopeIsGlobal()
1091
1092    binding_module = generator.moduleForComponent(ed)
1093    outf = binding_module.bindingIO()
1094
1095    template_map = elementDeclarationMap(ed, binding_module, **kw)
1096    template_map.setdefault('scope', binding_module.literal(None, **kw))
1097    template_map.setdefault('map_update', '')
1098
1099    binding_module.importForDeclaration(ed)
1100    outf.write(templates.replaceInText('''
1101%{class} = pyxb.binding.basis.element(%{name_expr}, %{typeDefinition}%{element_aux_init})
1102%{namespaceReference}.addCategoryObject('elementBinding', %{class}.name().localName(), %{class})
1103''', name_expr=binding_module.literal(ed.expandedName(), **kw), **template_map))
1104
1105    if ed.substitutionGroupAffiliation() is not None:
1106        outf.postscript().append(templates.replaceInText('''
1107%{class}._setSubstitutionGroup(%{substitution_group})
1108''', **template_map))
1109
1110def _PrepareSimpleTypeDefinition (std, generator, nsm, module_context):
1111    std._templateMap()['_unique'] = nsm.uniqueInClass(std)
1112    if _useEnumerationTags(std):
1113        enum_facet = simpleTypeOwnedEnumerationFacet(std)
1114        if enum_facet is not None:
1115            for ei in six.iteritems(enum_facet):
1116                assert ei.tag() is None, '%s already has a tag' % (ei,)
1117                ei._setTag(utility.PrepareIdentifier(ei.unicodeValue(), nsm.uniqueInClass(std)))
1118
1119def _PrepareComplexTypeDefinition (ctd, generator, nsm, module_context):
1120    kw = { 'binding_module' : module_context }
1121    ctd._templateMap()['_unique'] = nsm.uniqueInClass(ctd)
1122    aux = _CTDAuxData.Create(ctd)
1123    multiples = aux.edMultiples
1124    for cd in ctd.localScopedDeclarations():
1125        _SetNameWithAccessors(cd, ctd, cd in multiples, module_context, nsm, kw)
1126
1127def _SetNameWithAccessors (component, container, is_plural, binding_module, nsm, kw):
1128    use_map = component._templateMap()
1129    class_unique = nsm.uniqueInClass(container)
1130    assert isinstance(component, xs.structures._ScopedDeclaration_mixin)
1131    unique_name = component.expandedName().localName()
1132    if component.overridesParentScope():
1133        class_unique.discard(component.overriddenDeclaration().uniqueNameInBinding())
1134    unique_name = utility.PrepareIdentifier(unique_name, class_unique)
1135    use_map['id'] = unique_name
1136    use_map['inspector'] = unique_name
1137    use_map['mutator'] = utility.PrepareIdentifier('set' + unique_name[0].upper() + unique_name[1:], class_unique)
1138    use_map['use'] = utility.MakeUnique('__' + unique_name.strip('_'), class_unique)
1139    assert component._scope() == container
1140    assert component.nameInBinding() is None, 'Use %s but binding name %s for %s' % (use_map['use'], component.nameInBinding(), component.expandedName())
1141    component.setNameInBinding(use_map['use'])
1142    component.setUniqueNameInBinding(use_map['id'])
1143    key_name = six.u('%s_%s_%s') % (six.text_type(nsm.namespace()), container.nameInBinding(), component.expandedName())
1144    use_map['key'] = utility.PrepareIdentifier(key_name, class_unique, private=True)
1145    use_map['qname'] = six.text_type(component.expandedName())
1146    if isinstance(component, xs.structures.ElementDeclaration) and is_plural:
1147        use_map['appender'] = utility.PrepareIdentifier('add' + unique_name[0].upper() + unique_name[1:], class_unique)
1148    return use_map
1149
1150class BindingIO (object):
1151    __prolog = None
1152    __postscript = None
1153    __templateMap = None
1154    __stringIO = None
1155    __bindingFilePath = None
1156    __bindingFile = None
1157
1158    def __init__ (self, binding_module, **kw):
1159        super(BindingIO, self).__init__()
1160        self.__bindingModule = binding_module
1161        self.__bindingFilePath = kw['binding_file_path']
1162        self.__bindingFile = kw['binding_file']
1163        self.__prolog = []
1164        self.__postscript = []
1165        self.__templateMap = kw.copy()
1166        encoding = kw.get('encoding', pyxb._OutputEncoding)
1167        self.__templateMap.update({ 'date' : str(datetime.datetime.now()),
1168                                    'filePath' : self.__bindingFilePath,
1169                                    'coding' : encoding,
1170                                    'binding_module' : binding_module,
1171                                    'binding_tag' : binding_module.bindingTag(),
1172                                    'pyxbVersion' : pyxb.__version__,
1173                                    'pyxb_version' : repr2to3(pyxb.__version__),
1174                                    'pythonVersion' : '.'.join(map(str, sys.version_info))})
1175        self.__stringIO = io.StringIO()
1176        if self.__bindingFile:
1177            prefacet = self.expand('''# %{filePath}
1178# -*- coding: %{coding} -*-
1179# PyXB bindings for %{binding_tag}
1180# Generated %{date} by PyXB version %{pyxbVersion} using Python %{pythonVersion}
1181%{binding_preface}''', binding_preface=binding_module.bindingPreface())
1182            self.__bindingFile.write(prefacet.encode(encoding))
1183            self.__bindingFile.flush()
1184
1185    def bindingFile (self):
1186        return self.__bindingFile
1187
1188    def expand (self, template, **kw):
1189        tm = self.__templateMap.copy()
1190        tm.update(kw)
1191        return templates.replaceInText(template, **tm)
1192
1193    def appendPrologBoilerplate (self, tm):
1194        self.prolog().append(self.expand('''# Unique identifier for bindings created at the same time
1195_GenerationUID = %{generation_uid_expr}
1196
1197# Version of PyXB used to generate the bindings
1198_PyXBVersion = %{pyxb_version}
1199# Generated bindings are not compatible across PyXB versions
1200if pyxb.__version__ != _PyXBVersion:
1201    raise pyxb.PyXBVersionError(_PyXBVersion)
1202
1203# A holder for module-level binding classes so we can access them from
1204# inside class definitions where property names may conflict.
1205_module_typeBindings = pyxb.utils.utility.Object()
1206
1207# Import bindings for namespaces imported into schema
1208%{aux_imports}
1209
1210# NOTE: All namespace declarations are reserved within the binding
1211%{namespace_decls}
1212''', **tm))
1213
1214    def write (self, template, **kw):
1215        txt = self.expand(template, **kw)
1216        self.__stringIO.write(txt)
1217
1218    def bindingModule (self):
1219        return self.__bindingModule
1220    __bindingModule = None
1221
1222    def prolog (self):
1223        return self.__prolog
1224    def postscript (self):
1225        return self.__postscript
1226
1227    def literal (self, *args, **kw):
1228        kw.update(self.__templateMap)
1229        return pythonLiteral(*args, **kw)
1230
1231    def contents (self):
1232        rv = self.__prolog
1233        rv.append(self.__stringIO.getvalue())
1234        rv.extend(self.__postscript)
1235        return ''.join(rv)
1236
1237class _ModuleNaming_mixin (object):
1238    __anonSTDIndex = None
1239    __anonCTDIndex = None
1240    __uniqueInModule = None
1241    __uniqueInClass = None
1242    __referencedFromClass = None
1243
1244    _UniqueInModule = set([ 'pyxb', 'sys', '_module_typeBindings' ])
1245    """Identifiers that are reserved within a module.
1246
1247    Subclasses extend this with the identifiers they add to the
1248    module.  Module-level schema-derived identifiers (such as type
1249    definition and element names) are deconflicted from this set and
1250    from each other."""
1251
1252    _ReferencedFromClass = set([ 'pyxb', 'sys', '_module_typeBindings' ])
1253    """Identifiers defined in module that are accessed unqualified from class.
1254
1255    These include standard import module names and globals such as
1256    references to namespaces."""
1257
1258    __ComponentBindingModuleMap = {}
1259
1260    def generator (self):
1261        return self.__generator
1262    __generator = None
1263
1264    def __init__ (self, generator, *args, **kw):
1265        super(_ModuleNaming_mixin, self).__init__(*args, **kw)
1266        self.__generator = generator
1267        assert isinstance(self.__generator, Generator)
1268        self.__anonSTDIndex = 1
1269        self.__anonCTDIndex = 1
1270        self.__components = []
1271        self.__componentNameMap = {}
1272        self.__uniqueInModule = set()
1273        self.__referencedFromClass = self._ReferencedFromClass.copy()
1274        self.__bindingIO = None
1275        self.__importModulePathMap = {}
1276        self.__namespaceDeclarations = []
1277        self.__referencedNamespaces = {}
1278        self.__uniqueInClass = {}
1279
1280    def _importModule (self, module):
1281        assert not isinstance(module, pyxb.namespace.Namespace)
1282        assert isinstance(module, (_ModuleNaming_mixin, pyxb.namespace.archive.ModuleRecord)), 'Unexpected type %s' % (type(module),)
1283        if isinstance(module, NamespaceModule):
1284            if pyxb.namespace.XMLSchema == module.namespace():
1285                return
1286            module = module.moduleRecord()
1287        assert isinstance(module, (pyxb.namespace.archive.ModuleRecord, NamespaceGroupModule))
1288        if not (module in self.__importModulePathMap):
1289            module_path = module.modulePath()
1290            if 'pyxb' == module_path.split('.', 2)[0]:
1291                assert 'pyxb' in self.uniqueInModule()
1292                assert 'pyxb' in self.__referencedFromClass
1293                module_path = None
1294            else:
1295                module_path = utility.PrepareIdentifier('ImportedBinding_' + module_path.replace('.', '_'),
1296                                                        self.uniqueInModule(), protected=True)
1297                self.__referencedFromClass.add(module_path)
1298            self.__importModulePathMap[module] = module_path
1299
1300    def uniqueInClass (self, component):
1301        rv = self.__uniqueInClass.get(component)
1302        if rv is None:
1303            rv = set()
1304            rv.update(self.__referencedFromClass)
1305            if isinstance(component, xs.structures.SimpleTypeDefinition):
1306                rv.update(basis.simpleTypeDefinition._ReservedSymbols)
1307                if simpleTypeOwnedEnumerationFacet(component) is not None:
1308                    rv.update(basis.enumeration_mixin._ReservedSymbols)
1309            else:
1310                assert isinstance(component, xs.structures.ComplexTypeDefinition)
1311                if component._isHierarchyRoot():
1312                    rv.update(basis.complexTypeDefinition._ReservedSymbols)
1313                else:
1314                    base_td = component.baseTypeDefinition()
1315                    base_unique = base_td._templateMap().get('_unique')
1316                    assert base_unique is not None, 'Base %s of %s has no unique' % (base_td.expandedName(), component.expandedName())
1317                    rv.update(base_unique)
1318            self.__uniqueInClass[component] = rv
1319        return rv
1320
1321    __referencedNamespaces = None
1322
1323    def bindingIO (self):
1324        return self.__bindingIO
1325
1326    __moduleUID = None
1327    def moduleUID (self):
1328        if self.__moduleUID is None:
1329            self.__moduleUID = pyxb.utils.utility.HashForText(self._moduleUID_vx())
1330        return self.__moduleUID
1331
1332    def _moduleUID_vx (self):
1333        return str(id(self))
1334
1335    def bindingTag (self):
1336        """Return a distinct string recorded in the first 4096 bytes of the binding file.
1337
1338        This is used to ensure uniqueness and avoid overwriting data
1339        belonging to a different binding.  The return value comprises
1340        the class-specialized L{_bindingTagPrefix_vx} with the
1341        L{moduleUID}.
1342        """
1343        return '%s:%s' % (self._bindingTagPrefix_vx(), self.moduleUID())
1344
1345    def _bindingTagPrefix_vx (self):
1346        raise pyxb.LogicError('Subclass %s does not define _bindingTagPrefix_vx' % (type(self),))
1347
1348    def bindingPreface (self):
1349        """Return a block of binding text (comment or code) serving as a preface.
1350
1351        Normally this should describe the module contents."""
1352        return self._bindingPreface_vx()
1353    def _bindingPreface_vx (self):
1354        return ''
1355
1356    def moduleContents (self):
1357        template_map = {}
1358        aux_imports = []
1359        for (mr, as_path) in six.iteritems(self.__importModulePathMap):
1360            assert self != mr
1361            if as_path is not None:
1362                aux_imports.append('import %s as %s' % (mr.modulePath(), as_path))
1363            else:
1364                aux_imports.append('import %s' % (mr.modulePath(),))
1365        template_map['aux_imports'] = "\n".join(aux_imports)
1366        template_map['namespace_decls'] = "\n".join(self.__namespaceDeclarations)
1367        template_map['module_uid'] = self.moduleUID()
1368        template_map['generation_uid_expr'] = repr2to3(self.generator().generationUID())
1369        self._finalizeModuleContents_vx(template_map)
1370        return self.__bindingIO.contents()
1371
1372    def modulePath (self):
1373        return self.__modulePath
1374    def _setModulePath (self, path_data):
1375        (binding_file_path, binding_file, module_path) = path_data
1376        self.__bindingFilePath = binding_file_path
1377        self.__bindingFile = binding_file
1378        if module_path is None:
1379            module_path = self.moduleRecord().modulePath()
1380        if module_path is not None:
1381            self.__modulePath = module_path
1382        kw = self._initialBindingTemplateMap()
1383        self.__bindingIO = BindingIO(self, binding_file=binding_file, binding_file_path=binding_file_path, **kw)
1384    __modulePath = None
1385
1386    def pathFromImport (self, module, name):
1387        """Python code reference to an object in an imported module"""
1388        if isinstance(module, NamespaceModule):
1389            module = module.moduleRecord()
1390        as_path = self.__importModulePathMap[module]
1391        if as_path is None:
1392            as_path = module.modulePath()
1393        return '%s.%s' % (as_path, name)
1394
1395    def bindingFile (self):
1396        return self.__bindingFile
1397    __bindingFile = None
1398    __bindingFilePath = None
1399
1400    def _initializeUniqueInModule (self, unique_in_module):
1401        self.__uniqueInModule = set(unique_in_module)
1402
1403    def uniqueInModule (self):
1404        return self.__uniqueInModule
1405
1406    @classmethod
1407    def BindComponentInModule (cls, component, module):
1408        cls.__ComponentBindingModuleMap[component] = module
1409        return module
1410
1411    @classmethod
1412    def ComponentBindingModule (cls, component):
1413        return cls.__ComponentBindingModuleMap.get(component)
1414
1415    @classmethod
1416    def _RecordModule (cls, module):
1417        cls.__RecordModuleMap[module.moduleRecord()] = module
1418        return module
1419    @classmethod
1420    def _ForRecord (cls, module_record):
1421        return cls.__RecordModuleMap.get(module_record)
1422    __RecordModuleMap = { }
1423
1424    def _bindComponent (self, component):
1425        kw = {}
1426        rv = component.bestNCName()
1427        if rv is None:
1428            if isinstance(component, xs.structures.ComplexTypeDefinition):
1429                rv = utility.PrepareIdentifier('CTD_ANON', self.uniqueInClass(component), protected=True)
1430            elif isinstance(component, xs.structures.SimpleTypeDefinition):
1431                rv = utility.PrepareIdentifier('STD_ANON', self.uniqueInClass(component), protected=True)
1432            else:
1433                assert False
1434            kw['protected'] = True
1435        rv = utility.PrepareIdentifier(rv, self.__uniqueInModule, kw)
1436        assert not component in self.__componentNameMap
1437        self.__components.append(component)
1438        self.__componentNameMap[component] = rv
1439        return rv
1440    def nameInModule (self, component):
1441        return self.__componentNameMap.get(component)
1442
1443    def referenceSchemaComponent (self, component, in_class=False):
1444        origin = component._objectOrigin()
1445        assert origin is not None
1446        module_record = origin.moduleRecord()
1447        assert module_record is not None
1448        if self.generator().generationUID() != module_record.generationUID():
1449            self._importModule(module_record)
1450            return self.pathFromImport(module_record, component.nameInBinding())
1451        component_module = _ModuleNaming_mixin.ComponentBindingModule(component)
1452        assert component_module is not None, 'No binding module for %s from %s in %s as %s' % (component, module_record, self.moduleRecord(), component.nameInBinding())
1453        name = component_module.__componentNameMap.get(component)
1454        if name is None:
1455            assert isinstance(self, NamespaceModule) and (self.namespace() == component.bindingNamespace())
1456            name = component.nameInBinding()
1457        if self != component_module:
1458            self._importModule(component_module)
1459            name = self.pathFromImport(component_module, name)
1460        elif in_class:
1461            name = '_module_typeBindings.%s' %(name,)
1462        return name
1463
1464    def _referencedNamespaces (self): return self.__referencedNamespaces
1465
1466    def defineNamespace (self, namespace, name, definition=None, **kw):
1467        rv = self.__referencedNamespaces.get(namespace)
1468        assert rv is None, 'Module %s already has reference to %s' % (self, namespace)
1469        # All module-level namespace declarations are reserved.
1470        # Some may have a protected name.  The unprotected name
1471        # shall always begin with 'Namespace'.  These names may
1472        # be referenced from class implementations as well.
1473        assert name.startswith('Namespace'), 'unexpected %s naming %s' % (name, namespace)
1474        name = utility.PrepareIdentifier(name, self.__uniqueInModule, **kw)
1475        self.__referencedFromClass.add(name)
1476        if definition is None:
1477            if namespace.isAbsentNamespace():
1478                definition = 'pyxb.namespace.CreateAbsentNamespace()'
1479            else:
1480                definition = 'pyxb.namespace.NamespaceForURI(%s, create_if_missing=True)' % (repr2to3(namespace.uri()),)
1481        self.__namespaceDeclarations.append('%s = %s' % (name, definition))
1482        self.__namespaceDeclarations.append("%s.configureCategories(['typeBinding', 'elementBinding'])" % (name,))
1483        self.__referencedNamespaces[namespace] = name
1484        return name
1485
1486    def referenceNamespace (self, namespace):
1487        rv = self.__referencedNamespaces.get(namespace)
1488        if rv is None:
1489            assert not (isinstance(self, NamespaceModule) and (self.namespace() == namespace))
1490            assert namespace.isBuiltinNamespace() or not namespace.isUndeclaredNamespace()
1491            if namespace.isBuiltinNamespace():
1492                rv = namespace.builtinNamespaceRepresentation()
1493            if rv is None:
1494                # Not the local namespace or a built-in.  Give it a
1495                # local name, potentially derived from its prefix.
1496                # Then try to find an existing import that defines the
1497                # namespace.  Then define the local name within the
1498                # binding, either as a reference to a namespace
1499                # reachable from an import or by doing a runtime
1500                # lookup from the namespace URI if somehow no provider
1501                # has been imported.  (This last case should apply
1502                # only to namespace group modules.)
1503                if namespace.prefix():
1504                    nsn = 'Namespace_%s' % (namespace.prefix(),)
1505                else:
1506                    nsn = 'Namespace'
1507                nsdef = None
1508                for im in six.iterkeys(self.__importModulePathMap):
1509                    if isinstance(im, pyxb.namespace.archive.ModuleRecord):
1510                        if im.namespace() == namespace:
1511                            nsdef = self.pathFromImport(im, 'Namespace')
1512                            break
1513                    elif isinstance(im, NamespaceGroupModule):
1514                        pass
1515                    else:
1516                        assert False, 'unexpected import from type %s %s' % (type(im), im,)
1517                # If we failed to identify the namespace in an existing import,
1518                # and this module is not a namespace group which includes the
1519                # namespace as part of its content, something went wrong.
1520                if (nsdef is None) and not (isinstance(self, NamespaceGroupModule) and self.moduleForNamespace(namespace) is not None):
1521                    # This can happen if we've got a QName for some namespace for which
1522                    # we don't have any information.  That's actually OK, so just go
1523                    # ahead and define a namespace we can reference.
1524                    pass
1525
1526                rv =  self.defineNamespace(namespace, nsn, nsdef, protected=True)
1527                assert 0 < len(self.__namespaceDeclarations)
1528            self.__referencedNamespaces[namespace] = rv
1529        return rv
1530
1531    def importForDeclaration (self, decl):
1532        """Import the binding from which the declaration came.
1533
1534        Figure out where the declaration came from.  If it's not part
1535        of this binding, make sure we import the binding associated
1536        with the schema from which it came.  We need that, if not for
1537        something in the declaration itself, at least to be able to
1538        get the Namespace for the declaration's name.  None of this is
1539        relevant if the declaration has no namespace."""
1540        sdecl = decl
1541        while sdecl._cloneSource() is not None:
1542            sdecl = sdecl._cloneSource()
1543        assert decl.expandedName() == sdecl.expandedName()
1544        ns = decl.expandedName().namespace()
1545        if ns is None:
1546            return
1547        mr = sdecl._objectOrigin().moduleRecord()
1548        if isinstance(self, NamespaceModule):
1549            need_import = self.moduleRecord().modulePath() != mr.modulePath()
1550        elif isinstance(self, NamespaceGroupModule):
1551            need_import = True
1552            for nm in self.namespaceModules():
1553                if nm.moduleRecord().modulePath() == mr.modulePath():
1554                    need_import = False
1555                    break
1556        else:
1557            raise pyxb.LogicError('Unhandled module naming', self)
1558        if need_import:
1559            self._importModule(mr)
1560
1561    def literal (self, *args, **kw):
1562        return self.__bindingIO.literal(*args, **kw)
1563
1564    def addImportsFrom (self, module):
1565        _log.info('Importing to %s from %s', self, module)
1566        self._importModule(module)
1567        for c in self.__components:
1568            local_name = self.nameInModule(c)
1569            assert local_name is not None
1570            rem_name = module.nameInModule(c)
1571            if rem_name is None:
1572                continue
1573            aux = ''
1574            if local_name != rem_name:
1575                aux = ' as %s' % (local_name,)
1576            self.__bindingIO.write("from %s import %s%s # %s\n" % (module.modulePath(), rem_name, aux, c.expandedName()))
1577
1578    def writeToModuleFile (self):
1579        if self.bindingFile():
1580            self.bindingFile().write(self.moduleContents().encode(pyxb._OutputEncoding))
1581            self.bindingFile().close()
1582            _log.info('Saved binding source to %s', self.__bindingFilePath)
1583        else:
1584            _log.info('No binding file for %s', self)
1585
1586class NamespaceModule (_ModuleNaming_mixin):
1587    """This class represents a Python module that holds all the
1588    declarations belonging to a specific namespace."""
1589
1590    def namespace (self):
1591        return self.__namespace
1592    __namespace = None
1593
1594    def moduleRecord (self):
1595        return self.__moduleRecord
1596    __moduleRecord = None
1597
1598    def namespaceGroupModule (self):
1599        return self.__namespaceGroupModule
1600    def setNamespaceGroupModule (self, namespace_group_module):
1601        self.__namespaceGroupModule = namespace_group_module
1602    __namespaceGroupModule = None
1603
1604    _UniqueInModule = _ModuleNaming_mixin._UniqueInModule.copy()
1605    _UniqueInModule.update([ 'CreateFromDOM', 'CreateFromDocument' ])
1606
1607    def namespaceGroupHead (self):
1608        return self.__namespaceGroupHead
1609    __namespaceGroupHead = None
1610    __namespaceGroup = None
1611
1612    def componentsInNamespace (self):
1613        return self.__components
1614    __components = None
1615
1616    @classmethod
1617    def ForComponent (cls, component):
1618        return cls.__ComponentModuleMap.get(component)
1619    __ComponentModuleMap = { }
1620
1621    def _bindingTagPrefix_vx (self):
1622        return 'NM'
1623
1624    def _bindingPreface_vx (self):
1625        ns = self.namespace()
1626        rvl = ['# Namespace %s' % (ns,)]
1627        if ns.prefix() is not None:
1628            rvl.append(' [xmlns:%s]' % (ns.prefix(),))
1629        rvl.append('\n')
1630        return ''.join(rvl)
1631
1632    def _moduleUID_vx (self):
1633        if self.namespace().isAbsentNamespace():
1634            return 'Absent'
1635        return six.text_type(self.namespace())
1636
1637    def namespaceGroupMulti (self):
1638        return 1 < len(self.__namespaceGroup)
1639
1640    def __init__ (self, generator, module_record, mr_scc, components=None, **kw):
1641        super(NamespaceModule, self).__init__(generator, **kw)
1642        self._initializeUniqueInModule(self._UniqueInModule)
1643        self.__moduleRecord = module_record
1644        self.__namespace = self.__moduleRecord.namespace()
1645        self.defineNamespace(self.__namespace, 'Namespace')
1646        self._RecordModule(self)
1647        self.__components = components
1648        # wow! fromkeys actually IS useful!
1649        if self.__components is not None:
1650            self.__ComponentModuleMap.update(dict.fromkeys(self.__components, self))
1651        self.__namespaceBindingNames = {}
1652        self.__componentBindingName = {}
1653        self._setModulePath(generator.modulePathData(self))
1654
1655    def _initialBindingTemplateMap (self):
1656        kw = { 'moduleType' : 'namespace'
1657             , 'targetNamespace' : repr2to3(self.__namespace.uri())
1658             , 'namespaceURI' : self.__namespace.uri()
1659             , 'namespaceReference' : self.referenceNamespace(self.__namespace)
1660             }
1661        return kw
1662
1663    def _finalizeModuleContents_vx (self, template_map):
1664        template_map['_TextType'] = '_six.text_type'
1665        self.bindingIO().prolog().append('''
1666from __future__ import unicode_literals
1667import pyxb
1668import pyxb.binding
1669import pyxb.binding.saxer
1670import io
1671import pyxb.utils.utility
1672import pyxb.utils.domutils
1673import sys
1674import pyxb.utils.six as _six
1675''')
1676        self.bindingIO().appendPrologBoilerplate(template_map)
1677        self.bindingIO().prolog().append(self.bindingIO().expand('''
1678def CreateFromDocument (xml_text, default_namespace=None, location_base=None):
1679    """Parse the given XML and use the document element to create a
1680    Python instance.
1681
1682    @param xml_text An XML document.  This should be data (Python 2
1683    str or Python 3 bytes), or a text (Python 2 unicode or Python 3
1684    str) in the L{pyxb._InputEncoding} encoding.
1685
1686    @keyword default_namespace The L{pyxb.Namespace} instance to use as the
1687    default namespace where there is no default namespace in scope.
1688    If unspecified or C{None}, the namespace of the module containing
1689    this function will be used.
1690
1691    @keyword location_base: An object to be recorded as the base of all
1692    L{pyxb.utils.utility.Location} instances associated with events and
1693    objects handled by the parser.  You might pass the URI from which
1694    the document was obtained.
1695    """
1696
1697    if pyxb.XMLStyle_saxer != pyxb._XMLStyle:
1698        dom = pyxb.utils.domutils.StringToDOM(xml_text)
1699        return CreateFromDOM(dom.documentElement, default_namespace=default_namespace)
1700    if default_namespace is None:
1701        default_namespace = Namespace.fallbackNamespace()
1702    saxer = pyxb.binding.saxer.make_parser(fallback_namespace=default_namespace, location_base=location_base)
1703    handler = saxer.getContentHandler()
1704    xmld = xml_text
1705    if isinstance(xmld, %{_TextType}):
1706        xmld = xmld.encode(pyxb._InputEncoding)
1707    saxer.parse(io.BytesIO(xmld))
1708    instance = handler.rootObject()
1709    return instance
1710
1711def CreateFromDOM (node, default_namespace=None):
1712    """Create a Python instance from the given DOM node.
1713    The node tag must correspond to an element declaration in this module.
1714
1715    @deprecated: Forcing use of DOM interface is unnecessary; use L{CreateFromDocument}."""
1716    if default_namespace is None:
1717        default_namespace = Namespace.fallbackNamespace()
1718    return pyxb.binding.basis.element.AnyCreateFromDOM(node, default_namespace)
1719
1720''', **template_map))
1721
1722    __components = None
1723    __componentBindingName = None
1724
1725    def bindComponent (self, component):
1726        ns_name = self._bindComponent(component)
1727        component.setNameInBinding(ns_name)
1728        binding_module = self
1729        if self.__namespaceGroupModule:
1730            self.__namespaceGroupModule._bindComponent(component)
1731            binding_module = self.__namespaceGroupModule
1732        return _ModuleNaming_mixin.BindComponentInModule(component, binding_module)
1733
1734    def __str__ (self):
1735        return 'NM:%s@%s' % (self.namespace(), self.modulePath())
1736
1737class NamespaceGroupModule (_ModuleNaming_mixin):
1738    """This class represents a Python module that holds all the
1739    declarations belonging to a set of namespaces which have
1740    interdependencies."""
1741
1742    def namespaceModules (self):
1743        return self.__namespaceModules
1744    __namespaceModules = None
1745
1746    def moduleForNamespace (self, namespace):
1747        for nm in self.__namespaceModules:
1748            if nm.namespace() == namespace:
1749                return nm
1750        return None
1751
1752    __components = None
1753    __componentBindingName = None
1754    __uniqueInModule = None
1755
1756    __UniqueInGroups = set()
1757
1758    _GroupPrefix = '_group'
1759
1760    def __init__ (self, generator, namespace_modules, **kw):
1761        super(NamespaceGroupModule, self).__init__(generator, **kw)
1762        assert 1 < len(namespace_modules)
1763        self.__namespaceModules = namespace_modules
1764        self.__namespaceGroupHead = namespace_modules[0].namespaceGroupHead()
1765        self._initializeUniqueInModule(self._UniqueInModule)
1766        self._setModulePath(generator.modulePathData(self))
1767
1768    def _initialBindingTemplateMap (self):
1769        kw = { 'moduleType' : 'namespaceGroup' }
1770        return kw
1771
1772    def _bindingTagPrefix_vx (self):
1773        return 'NGM'
1774
1775    def _bindingPreface_vx (self):
1776        rvl = ['# Group contents:\n' ]
1777        for nsm in self.namespaceModules():
1778            rvl.append(nsm.bindingPreface())
1779        rvl.append('\n')
1780        return ''.join(rvl)
1781
1782    def _finalizeModuleContents_vx (self, template_map):
1783        self.bindingIO().prolog().append('''
1784from __future__ import unicode_literals
1785import pyxb
1786import pyxb.binding
1787import pyxb.utils.utility
1788import pyxb.utils.six as _six
1789''')
1790        self.bindingIO().appendPrologBoilerplate(template_map)
1791
1792    def _moduleUID_vx (self):
1793        nss = []
1794        for nsm in self.namespaceModules():
1795            ns = nsm.namespace()
1796            if ns.isAbsentNamespace():
1797                nss.append('Absent')
1798            else:
1799                nss.append(six.text_type(ns))
1800        nss.sort()
1801        return six.u(';').join(nss)
1802
1803    def __str__ (self):
1804        return 'NGM:%s' % (self.modulePath(),)
1805
1806
1807def GeneratePython (schema_location=None,
1808                    schema_text=None,
1809                    namespace=None,
1810                    module_prefix_elts=[],
1811                    **kw):
1812
1813    generator = Generator(allow_absent_module=True, generate_to_files=False, **kw)
1814    if schema_location is not None:
1815        generator.addSchemaLocation(schema_location)
1816    elif schema_text is not None:
1817        generator.addSchema(schema_text)
1818    modules = generator.bindingModules()
1819
1820    assert 1 == len(modules), '%s produced %d modules: %s' % (namespace, len(modules), six.u(' ').join([ six.text_type(_m) for _m in modules]))
1821    return modules.pop().moduleContents()
1822
1823import optparse
1824import re
1825
1826class Generator (object):
1827    """Configuration and data for a single binding-generation action."""
1828
1829    _DEFAULT_bindingRoot = '.'
1830    def bindingRoot (self):
1831        """The directory path into which generated bindings will be written.
1832        @rtype: C{str}"""
1833        return self.__bindingRoot
1834    def setBindingRoot (self, binding_root):
1835        self.__bindingRoot = binding_root
1836        return self
1837    __bindingRoot = None
1838
1839    def __moduleFilePath (self, module_elts, inhibit_extension=False):
1840        if isinstance(module_elts, six.string_types):
1841            module_elts = module_elts.split('.')
1842        else:
1843            module_elts = module_elts[:]
1844        assert 0 < len(module_elts)
1845        if not inhibit_extension:
1846            assert not module_elts[-1].endswith('.py')
1847            module_elts[-1] = '%s.py' % (module_elts[-1],)
1848        return os.path.join(self.bindingRoot(), *module_elts)
1849
1850    def generateToFiles (self):
1851        return self.__generateToFiles
1852    __generateToFiles = None
1853
1854    def modulePathData (self, module):
1855        # file system path to where the bindings are written
1856        # module path from which the bindings are normally imported
1857        # file object into which bindings are written
1858
1859        module_path = None
1860        if isinstance(module, NamespaceModule):
1861            mr = module.moduleRecord()
1862            if mr is None:
1863                return ('/dev/null', None, None)
1864            if self.generationUID() != mr.generationUID():
1865                return ('/dev/null', None, None)
1866            if not self.generateToFiles():
1867                return ('/dev/null', None, None)
1868            if mr.namespace().isBuiltinNamespace() and (not self.allowBuiltinGeneration()):
1869                return ('/dev/null', None, None)
1870            module_path = mr.modulePath()
1871            assert module_path is not None, 'No path specified for module %s' % (mr,)
1872            #if pyxb.namespace.XMLSchema != ns:
1873            #    return ('/dev/null', None, None)
1874            #module_path="bogus.xsd"
1875            module_elts = module_path.split('.')
1876            if self.writeForCustomization():
1877                import_file_path = self.__moduleFilePath(module_elts)
1878                module_elts.insert(-1, 'raw')
1879                if not os.path.exists(import_file_path):
1880                    raw_module_path = '.'.join(module_elts)
1881                    fd = pyxb.utils.utility.OpenOrCreate(import_file_path)
1882                    impt = '''# -*- coding: utf-8 -*-
1883from %s import *
1884'''  % (raw_module_path,)
1885                    impd = impt.encode('utf-8')
1886                    fd.write(impd)
1887                    fd.close()
1888            binding_file_path = self.__moduleFilePath(module_elts)
1889            try:
1890                binding_file = pyxb.utils.utility.OpenOrCreate(binding_file_path, tag=module.moduleUID())
1891            except OSError as e:
1892                if errno.EEXIST == e.errno:
1893                    raise pyxb.BindingGenerationError('Target file %s for module %s bindings exists with other content' % (binding_file_path, mr))
1894                raise
1895        elif isinstance(module, NamespaceGroupModule):
1896            if not self.generateToFiles():
1897                raise pyxb.BindingGenerationError('Generation of namespace groups requires generate-to-files')
1898            module_elts = []
1899            if self.modulePrefix():
1900                module_elts.extend(map(utility.MakeModuleElement, self.modulePrefix().split('.')))
1901            if self.writeForCustomization():
1902                module_elts.append('raw')
1903            in_use = set()
1904            while True:
1905                module_elts.append(pyxb.utils.utility.PrepareIdentifier('nsgroup', in_use, protected=True))
1906                try:
1907                    binding_file_path = self.__moduleFilePath(module_elts)
1908                    _log.info('Attempting group %s uid %s at %s', module, module.moduleUID(), binding_file_path)
1909                    binding_file = pyxb.utils.utility.OpenOrCreate(binding_file_path, tag=module.moduleUID())
1910                    break
1911                except OSError as e:
1912                    if errno.EEXIST != e.errno:
1913                        raise
1914                module_elts.pop()
1915            module_path = '.'.join(module_elts)
1916        else:
1917            assert False
1918        if self.generateToFiles():
1919            for n in range(len(module_elts)-1):
1920                sub_path = self.__moduleFilePath(module_elts[:1+n], inhibit_extension=True)
1921                init_path = os.path.join(sub_path, '__init__.py')
1922                if not os.path.exists(init_path):
1923                    open(init_path, 'w')
1924        return (binding_file_path, binding_file, module_path)
1925
1926    def schemaRoot (self):
1927        """The directory from which entrypoint schemas specified as
1928        relative file paths will be read."""
1929        return self.__schemaRoot
1930    def setSchemaRoot (self, schema_root):
1931        if not schema_root.endswith(os.sep):
1932            schema_root = schema_root + os.sep
1933        self.__schemaRoot = schema_root
1934        return self
1935    __schemaRoot = None
1936
1937    def schemaStrippedPrefix (self):
1938        """Optional string that is stripped from the beginning of
1939        schemaLocation values before loading from them. This now
1940        applies only to URIs specified on the command line so is
1941        unlikely to be useful.
1942        """
1943        return self.__schemaStrippedPrefix
1944    def setSchemaStrippedPrefix (self, schema_stripped_prefix):
1945        self.__schemaStrippedPrefix = schema_stripped_prefix
1946        return self
1947    __schemaStrippedPrefix = None
1948
1949    def locationPrefixRewriteMap (self):
1950        """Optional map to rewrite schema locations.
1951
1952        This applies only to the values of schemaLocation attributes
1953        in C{import} and C{include} elements.  Its purpose is to
1954        convert remote or absolute schema locations into local or
1955        relative ones to allow offline processing when all schema are
1956        available in a local directory.  See C{schemaRoot}.
1957        """
1958        return self.__locationPrefixRewriteMap
1959    def setLocationPrefixRewriteMap (self, location_prefix_rewrite_map):
1960        self.__locationPrefixMap.clear()
1961        self.__locationPrefixMap.update(location_prefix_rewrite_map)
1962        return self
1963    def addLocationPrefixRewrite (self, prefix, substituent):
1964        """Add a rewrite entry for schema locations.
1965
1966        @param prefix : A text prefix that should be removed from
1967        schema location URIs.
1968
1969        @param substituent : The text prefix that should replace
1970        C{prefix} as a prefix in a schema location URI.
1971        """
1972
1973        self.__locationPrefixRewriteMap[prefix] = substituent
1974        return self
1975    def argAddLocationPrefixRewrite (self, prefix_rewrite):
1976        """Add a rewrite entry for schema locations.
1977
1978        Parameter values are strings of the form C{pfx=sub}.  The
1979        effect is that a schema location that begins with C{pfx} is
1980        rewritten so that it instead begins with C{sub}.
1981
1982        This applies to schemaLocation attributes in C{import} and
1983        C{include} elements.  It may be used to convert absolute
1984        schema locations into relative ones to allow offline
1985        processing when all schema are available in a local
1986        directory.  See C{schemaRoot}.
1987        """
1988
1989        try:
1990            (prefix, substituent) = prefix_rewrite.split('=', 1)
1991        except:
1992            raise
1993        self.addLocationPrefixRewrite(prefix, substituent)
1994    __locationPrefixMap = {}
1995
1996    def schemaLocationList (self):
1997        """A list of locations from which entrypoint schemas are to be
1998        read.
1999
2000        The values in the list are either URIs, or tuples consisting
2001        of a value and a callable which, when passed the generator
2002        object and the value, will return a
2003        L{pyxb.xmlschema.structures.Schema} instance.  See
2004        L{addSchemaLocation}.
2005
2006        See also L{addSchemaLocation} and L{schemas}.
2007        """
2008        return self.__schemaLocationList
2009    def setSchemaLocationList (self, schema_location_list):
2010        self.__schemaLocationList[:] = []
2011        self.__schemaLocationList.extend(schema_location_list)
2012        return self
2013    def addSchemaLocation (self, schema_location, converter=None):
2014        """Add the location of an entrypoint schema.
2015
2016        @param schema_location: The location of the schema.  This
2017        should be a URL; if the schema location does not have a URL
2018        scheme (e.g., C{http:}), it is assumed to be a file, and if it
2019        is not an absolute path is located relative to the
2020        C{schemaRoot}.
2021
2022        @keyword converter: Optional callable that will be invoked
2023        with the generator instance and the schema location, and is
2024        expected to return a L{pyxb.xmlschema.structures.Schema}
2025        instance.  If absent, the contents of the location are
2026        converted directly.
2027
2028        @note: The C{converter} argument derives from WSDL support: we
2029        need to add to the sequence of schema locations a URI of
2030        something that will not parse as a schema, but does have inner
2031        material that can if treated properly.  "Treated properly" may
2032        include having the archive path and other namespace
2033        manipulations configured before anything is done to it.
2034        """
2035        self.__schemaLocationList.append( (schema_location, converter) )
2036        return self
2037    def argAddSchemaLocation (self, schema_location):
2038        """Add the location of an entrypoint schema.  The provided
2039        value should be a URL; if it does not have a URL scheme (e.g.,
2040        C{http:}), it is assumed to be a file, and if it is not an
2041        absolute path is located relative to the C{schemaRoot}."""
2042        self.addSchemaLocation(schema_location)
2043    __schemaLocationList = None
2044
2045    def schemas (self):
2046        """Schema for which bindings should be generated.
2047
2048        These may be L{Schema<pyxb.xmlschema.structures.Schema>}
2049        instances, or strings; the latter is preferred, and is parsed
2050        into a Schema instance when required.
2051
2052        This is the list of entrypoint schemas for binding generation.
2053        Values in L{schemaLocationList} are read and converted into
2054        schema, then appended to this list.  Values from L{moduleList}
2055        are applied starting with the first schema in this list.
2056        """
2057        return self.__schemas[:]
2058    def setSchemas (self, schemas):
2059        self.__schemas[:] = []
2060        self.__schemas.extend(schemas)
2061        return self
2062    def addSchema (self, schema):
2063        self.__schemas.append(schema)
2064        return self
2065    __schemas = None
2066
2067    def namespaces (self):
2068        """The set of L{namespaces<pyxb.namespace.Namespace>} for
2069        which bindings will be generated.
2070
2071        This is the set of namespaces read from entrypoint schema,
2072        closed under reference to namespaces defined by schema import.
2073
2074        @rtype: C{set}
2075        """
2076        return self.__namespaces.copy()
2077    def setNamespaces (self, namespace_set):
2078        self.__namespaces.clear()
2079        self.__namespaces.update(namespace_set)
2080        return self
2081    def addNamespace (self, namespace):
2082        self.__namespaces.add(namespace)
2083        return self
2084    __namespaces = None
2085
2086    def moduleList (self):
2087        """A list of module names to be applied in order to the namespaces of entrypoint schemas"""
2088        return self.__moduleList[:]
2089    def _setModuleList (self, module_list):
2090        self.__moduleList[:] = []
2091        self.__moduleList.extend(module_list)
2092        return self
2093
2094    def addModuleName (self, module_name):
2095        """Add a module name corresponding to an entrypoint schema.
2096
2097        The namespace defined by the corresponding schema will be
2098        written to a binding using the given module name, adjusted by
2099        L{modulePrefix}."""
2100        self.__moduleList.append(module_name)
2101        return self
2102    __moduleList = None
2103
2104    def modulePrefix (self):
2105        """The prefix for binding modules.
2106
2107        The base name for the module holding a binding is taken from
2108        the moduleList, moduleMap, or an XMLNS prefix associated with
2109        the namespace in a containing schema.  This value, if present,
2110        is used as a prefix to allow a deeper module hierarchy."""
2111        return self.__modulePrefix
2112    def setModulePrefix (self, module_prefix):
2113        self.__modulePrefix = module_prefix
2114        return self
2115    __modulePrefix = None
2116
2117    def namespaceModuleMap (self):
2118        """A map from namespace URIs to the module to be used for the
2119        corresponding generated binding.
2120
2121        Module values are adjusted by L{modulePrefix} if that has been
2122        specified.
2123
2124        An entry in this map for a namespace supersedes the module
2125        specified in moduleList if the namespace is defined by an
2126        entrypoint schema.
2127
2128        @return: A reference to the namespace module map.
2129        """
2130        return self.__namespaceModuleMap
2131    __namespaceModuleMap = None
2132
2133    def archivePath (self):
2134        """A colon-separated list of paths from which namespace
2135        archives can be read.
2136
2137        The default path is the contents of the C{PYXB_ARCHIVE_PATH}
2138        environment variable, or the standard path configured at
2139        installation time.  Any file with the extension C{.wxs} found
2140        in one of these directories is examined to see whether it is a
2141        namespace archive.
2142        """
2143        return self.__archivePath
2144    def setArchivePath (self, archive_path):
2145        self.__archivePath = archive_path
2146        return self
2147    __archivePath = None
2148
2149    def noLoadNamespaces (self):
2150        """A frozenset of namespaces that must not be loaded from an archive."""
2151        return frozenset(self.__noLoadNamespaces)
2152    def _setNoLoadNamespaces (self, namespace_set):
2153        """Record the set of namespaces that should not be loaded from an archive.
2154
2155        The expectation is that any required entities in the namespace
2156        will be defined by loading schema."""
2157        self.__noLoadNamespaces.clear()
2158        self.__noLoadNamespaces.update([ pyxb.namespace.NamespaceInstance(_ns) for _ns in namespace_set ])
2159    def addNoLoadNamespace (self, namespace):
2160        """Mark that the specified namespace should not be loaded from an archive.
2161
2162        Use this when you are generating bindings for an application
2163        that has a restricted profile of a namespace that would
2164        otherwise be read from an archive.  Be aware that this removes
2165        any knowledge of any archive in which this namespace is
2166        present as a non-private member."""
2167        self.__noLoadNamespaces.add(pyxb.namespace.NamespaceInstance(namespace))
2168    __noloadNamespaces = None
2169
2170    def importAugmentableNamespaces (self):
2171        """A list of namespaces for which new bindings are allowd."""
2172        return frozenset(self.__importAugmentableNamespaces)
2173    def _setImportAugmentableNamespaces (self, namespace_set):
2174        """Return the set of namespaces that may be augmented by import directives."""
2175        self.__importAugmentableNamespaces.clear()
2176        self.__importAugmentableNamespaces.update([ pyxb.namespace.NamespaceInstance(_ns) for _ns in namespace_set ])
2177    def addImportAugmentableNamespace (self, namespace):
2178        """Mark that the specified namespace may be imported by new bindings.
2179
2180        Normally namespaces that are available from archives are
2181        considered to be complete, and schema locations in import
2182        directives are ignored.  Use this to indicate that the
2183        bindings being generated import new bindings.
2184
2185        Note that attempts to import schema that contributed to the
2186        archive will only be detected if the archive was generated
2187        from the same schemaLocation URI; if the archive was generated
2188        from a different source component definitions might
2189        conflict."""
2190        self.__importAugmentableNamespaces.add(pyxb.namespace.NamespaceInstance(namespace))
2191    __importAugmentableNamespaces = None
2192
2193    def archiveToFile (self):
2194        """Optional file into which the archive of namespaces will be written.
2195
2196        Subsequent generation actions can read pre-parsed namespaces
2197        from this file, and therefore reference the bindings that were
2198        built earlier rather than re-generating them.
2199
2200        The file name should normally end with C{.wxs}."""
2201        return self.__archiveToFile
2202    def setArchiveToFile (self, archive_to_file):
2203        self.__archiveToFile = archive_to_file
2204        return self
2205    __archiveToFile = None
2206
2207    def setNamespaceVisibility (self, namespace, visibility):
2208        namespace = pyxb.namespace.NamespaceInstance(namespace)
2209        self.__namespaceVisibilityMap[namespace] = visibility
2210        pass
2211    def _setNamespaceVisibilities (self, public, private):
2212        if public is None:
2213            public = set()
2214        if private is None:
2215            private = set()
2216        self.__namespaceVisibilityMap.clear()
2217        self.__namespaceVisibilityMap.update(dict.fromkeys(public, True))
2218        self.__namespaceVisibilityMap.update(dict.fromkeys(private, False))
2219    def namespaceVisibilityMap (self):
2220        """Indicates, for specific namespaces, whether their
2221        visibility in the archive should be public or private."""
2222        return self.__namespaceVisibilityMap.copy()
2223    __namespaceVisibilityMap = None
2224
2225    def defaultNamespacePublic (self):
2226        """Indicates whether unmentioned namespaces will be public or private (default) in the archive.
2227
2228        A namespace is I{mentioned} if it is the target namespace of
2229        an entrypoint schema, or appears in a namespace visibility
2230        specification.  I.e., this default applies only to namespaces
2231        that are modified as a result of including some schema, which
2232        is generally a local customization of something.
2233        """
2234        return self.__defaultNamespacePublic
2235    def setDefaultNamespacePublic (self, default_namespace_public):
2236        self.__defaultNamespacePublic = default_namespace_public
2237    __defaultNamespacePublic = None
2238
2239    def validateChanges (self):
2240        """Indicates whether the bindings should validate mutations
2241        against the content model."""
2242        return self.__validateChanges
2243    def setValidateChanges (self, validate_changes):
2244        self.__validateChanges = validate_changes
2245        return self
2246    __validateChanges = None
2247
2248    def writeForCustomization (self):
2249        """Indicates whether the binding Python code should be written into a sub-module for customization.
2250
2251        If enabled, a module C{path.to.namespace} will be written to
2252        the file C{path/to/raw/namespace.py}, so that the file
2253        C{path/to/namespace.py} can import it and override behavior."""
2254        return self.__writeForCustomization
2255    def setWriteForCustomization (self, write_for_customization):
2256        self.__writeForCustomization = write_for_customization
2257        return self
2258    __writeForCustomization = None
2259
2260    def allowAbsentModule (self):
2261        """Indicates whether the code generator is permitted to
2262        process namespace for which no module path can be determined.
2263
2264        Use this only when generating bindings that will not be
2265        referenced by other bindings."""
2266        return self.__allowAbsentModule
2267    def setAllowAbsentModule (self, allow_absent_module):
2268        self.__allowAbsentModule = allow_absent_module
2269        return self
2270    __allowAbsentModule = None
2271
2272    def allowBuiltinGeneration (self):
2273        """Indicates whether bindings will be written for namespaces that are built-in to PyXB.
2274
2275        This must be enabled when building bindings for the XML,
2276        XMLSchema instance, and other built-in namespaces.  Normally
2277        generation of these namespaces is inhibited lest it produce
2278        inconsistencies."""
2279        return self.__allowBuiltinGeneration
2280    def setAllowBuiltinGeneration (self, allow_builtin_generation):
2281        self.__allowBuiltinGeneration = allow_builtin_generation
2282        return self
2283    __allowBuiltinGeneration = None
2284
2285    def uriContentArchiveDirectory (self):
2286        """The directory path into which any content retrieved by URI will be written.
2287
2288        This serves as a local cache, and to give you an opportunity
2289        to inspect material retrieved from some other system.
2290        @rtype: C{str}"""
2291        return self.__uriContentArchiveDirectory
2292    def setUriContentArchiveDirectory (self, ucad):
2293        self.__uriContentArchiveDirectory = ucad
2294    __uriContentArchiveDirectory = None
2295
2296    def loggingConfigFile (self):
2297        """A file provided to L{logging.config.fileConfig} to control log messages.
2298
2299        In the absence of other configuration the Python standard logging infrastructure is used in its
2300        default configuration.
2301
2302        @rtype: C{str}"""
2303        return self.__loggingConfigFile
2304    def setLoggingConfigFile (self, logging_config_file):
2305        self.__loggingConfigFile = logging_config_file
2306    __loggingConfigFile = None
2307
2308    def __init__ (self, *args, **kw):
2309        """Create a configuration to be used for generating bindings.
2310
2311        Arguments are treated as additions to the schema location list
2312        after all keywords have been processed.
2313
2314        @keyword binding_root: Invokes L{setBindingRoot}
2315        @keyword schema_root: Invokes L{setSchemaRoot}
2316        @keyword schema_stripped_prefix: Invokes L{setSchemaStrippedPrefix}
2317        @keyword location_prefix_rewrite_map: Invokes L{setLocationPrefixRewriteMap}
2318        @keyword schema_location_list: Invokes L{setSchemaLocationList}
2319        @keyword module_list: Invokes L{_setModuleList}
2320        @keyword module_prefix: Invokes L{setModulePrefix}
2321        @keyword archive_path: Invokes L{setArchivePath}
2322        @keyword no_load_namespaces: Invokes L{_setNoLoadNamespaces}
2323        @keyword import_augmentable_namespaces: Invokes L{_setImportAugmentableNamespaces}
2324        @keyword archive_to_file: Invokes L{setArchiveToFile}
2325        @keyword public_namespace: Invokes L{setNamespaceVisibility}
2326        @keyword private_namespace: Invokes L{setNamespaceVisibility}
2327        @keyword default_namespace_public: Invokes L{setDefaultNamespacePublic}
2328        @keyword validate_changes: Invokes L{setValidateChanges}
2329        @keyword namespace_module_map: Initializes L{namespaceModuleMap}
2330        @keyword schemas: Invokes L{setSchemas}
2331        @keyword namespaces: Invokes L{setNamespaces}
2332        @keyword write_for_customization: Invokes L{setWriteForCustomization}
2333        @keyword allow_builtin_generation: Invokes L{setAllowBuiltinGeneration}
2334        @keyword allow_absent_module: Invokes L{setAllowAbsentModule}
2335        @keyword generate_to_files: Sets L{generateToFiles}
2336        @keyword uri_content_archive_directory: Invokes L{setUriContentArchiveDirectory}
2337        @keyword logging_config_file: Invokes L{setLoggingConfigFile}
2338        """
2339        argv = kw.get('argv')
2340        if argv is not None:
2341            kw = {}
2342        self.__bindingRoot = kw.get('binding_root', self._DEFAULT_bindingRoot)
2343        self.__schemaRoot = kw.get('schema_root', os.getcwd() + os.path.sep)
2344        self.__schemaStrippedPrefix = kw.get('schema_stripped_prefix')
2345        self.__locationPrefixRewriteMap = kw.get('location_prefix_rewrite_map', {})
2346        self.__schemas = []
2347        self.__schemaLocationList = kw.get('schema_location_list', [])[:]
2348        self.__moduleList = kw.get('module_list', [])[:]
2349        self.__modulePrefix = kw.get('module_prefix')
2350        self.__archivePath = kw.get('archive_path', pyxb.namespace.archive.GetArchivePath())
2351        self.__noLoadNamespaces = kw.get('no_load_namespaces', set()).copy()
2352        self.__importAugmentableNamespaces = kw.get('import_augmentable_namespaces', set()).copy()
2353        self.__archiveToFile = kw.get('archive_to_file')
2354        self.__namespaceVisibilityMap = {}
2355        self._setNamespaceVisibilities(kw.get('public_namespaces', set()), kw.get('private_namespaces', set()))
2356        self.__defaultNamespacePublic = kw.get('default_namespace_public', False)
2357        self.__validateChanges = kw.get('validate_changes', True)
2358        self.__namespaceModuleMap = kw.get('namespace_module_map', {}).copy()
2359        self.__schemas = kw.get('schemas', [])[:]
2360        self.__namespaces = set(kw.get('namespaces', []))
2361        self.__writeForCustomization = kw.get('write_for_customization', False)
2362        self.__allowBuiltinGeneration = kw.get('allow_builtin_generation', False)
2363        self.__allowAbsentModule = kw.get('allow_absent_module', False)
2364        self.__generateToFiles = kw.get('generate_to_files', True)
2365        self.__uriContentArchiveDirectory = kw.get('uri_content_archive_directory')
2366        self.__loggingConfigFile = kw.get('logging_config_file')
2367        self.__unnamedModulePaths = set()
2368
2369        if argv is not None:
2370            self.applyOptionValues(*self.optionParser().parse_args(argv))
2371        [ self.addSchemaLocation(_a) for _a in args ]
2372
2373        self.__generationUID = pyxb.utils.utility.UniqueIdentifier()
2374
2375        pyxb.namespace.XML.validateComponentModel()
2376
2377    __stripSpaces_re = re.compile('\s\s\s+')
2378    def __stripSpaces (self, string):
2379        return self.__stripSpaces_re.sub(' ', string)
2380
2381    __OptionSetters = (
2382        ('binding_root', setBindingRoot),
2383        ('schema_root', setSchemaRoot),
2384        ('schema_stripped_prefix', setSchemaStrippedPrefix),
2385        ('location_prefix_rewrite', argAddLocationPrefixRewrite),
2386        ('schema_location', setSchemaLocationList),
2387        ('module', _setModuleList),
2388        ('module_prefix', setModulePrefix),
2389        ('archive_path', setArchivePath),
2390        ('no_load_namespace', _setNoLoadNamespaces),
2391        ('import_augmentable_namespace', _setImportAugmentableNamespaces),
2392        ('archive_to_file', setArchiveToFile),
2393        ('default_namespace_public', setDefaultNamespacePublic),
2394        ('validate_changes', setValidateChanges),
2395        ('write_for_customization', setWriteForCustomization),
2396        ('allow_builtin_generation', setAllowBuiltinGeneration),
2397        ('allow_absent_module', setAllowAbsentModule),
2398        ('uri_content_archive_directory', setUriContentArchiveDirectory),
2399        ('logging_config_file', setLoggingConfigFile)
2400        )
2401    def applyOptionValues (self, options, args=None):
2402        for (tag, method) in self.__OptionSetters:
2403            v = getattr(options, tag)
2404            if v is not None:
2405                method(self, v)
2406        public_namespaces = getattr(options, 'public_namespace')
2407        private_namespaces = getattr(options, 'private_namespace')
2408        self._setNamespaceVisibilities(public_namespaces, private_namespaces)
2409        if args is not None:
2410            self.__schemaLocationList.extend(args)
2411        pyxb.utils.utility.SetLocationPrefixRewriteMap(self.locationPrefixRewriteMap())
2412        if self.__loggingConfigFile is not None:
2413            logging.config.fileConfig(self.__loggingConfigFile)
2414
2415    def setFromCommandLine (self, argv=None):
2416        if argv is None:
2417            argv = sys.argv[1:]
2418        (options, args) = self.optionParser().parse_args(argv)
2419        self.applyOptionValues(options, args)
2420        return self
2421
2422    def generationUID (self):
2423        """A unique identifier associated with this Generator instance.
2424
2425        This is an instance of L{pyxb.utils.utility.UniqueIdentifier}.
2426        Its associated objects are
2427        L{pyxb.namespace.archive._SchemaOrigin} instances, which
2428        identify schema that contribute to the definition of a
2429        namespace."""
2430        return self.__generationUID
2431    __generationUID = None
2432
2433    def optionParser (self, reset=False):
2434        """Return an C{optparse.OptionParser} instance tied to this configuration.
2435
2436        @param reset: If C{False} (default), a parser created in a
2437        previous invocation will be returned.  If C{True}, any
2438        previous option parser is discarded and a new one created.
2439        @type reset: C{bool}
2440        """
2441        if reset or (self.__optionParser is None):
2442            parser = optparse.OptionParser(usage="%prog [options] [more schema locations...]",
2443                                           version='%%prog from PyXB %s' % (pyxb.__version__,),
2444                                           description='Generate bindings from a set of XML schemas')
2445
2446            group = optparse.OptionGroup(parser, 'Identifying Schema', 'Specify and locate schema for which bindings should be generated.')
2447            group.add_option('--schema-location', '-u', metavar="FILE_or_URL",
2448                             action='append',
2449                             help=self.__stripSpaces(self.argAddSchemaLocation.__doc__))
2450            group.add_option('--schema-root', metavar="DIRECTORY",
2451                             help=self.__stripSpaces(self.schemaRoot.__doc__))
2452            group.add_option('--schema-stripped-prefix', metavar="TEXT", type='string',
2453                             help=self.__stripSpaces(self.schemaStrippedPrefix.__doc__))
2454            group.add_option('--location-prefix-rewrite', metavar="TEXT", type='string',
2455                             help=self.__stripSpaces(self.argAddLocationPrefixRewrite.__doc__))
2456            group.add_option('--uri-content-archive-directory', metavar="DIRECTORY",
2457                             help=self.__stripSpaces(self.uriContentArchiveDirectory.__doc__))
2458            parser.add_option_group(group)
2459
2460            group = optparse.OptionGroup(parser, 'Configuring Bindings', 'Specify where generated bindings should be written, and how they will be accessed from Python.')
2461            group.add_option('--module', '-m', metavar="MODULE",
2462                             action='append',
2463                             help=self.__stripSpaces(self.addModuleName.__doc__))
2464            group.add_option('--module-prefix', metavar="MODULE",
2465                             help=self.__stripSpaces(self.modulePrefix.__doc__))
2466            group.add_option('--binding-root', metavar="DIRECTORY",
2467                             help=self.__stripSpaces(self.bindingRoot.__doc__))
2468            group.add_option('-r', '--write-for-customization',
2469                             action='store_true', dest='write_for_customization',
2470                             help=self.__stripSpaces(self.writeForCustomization.__doc__ + ' This option turns on the feature.'))
2471            group.add_option('--no-write-for-customization',
2472                             action='store_false', dest='write_for_customization',
2473                             help=self.__stripSpaces(self.writeForCustomization.__doc__ + ' This option turns off the feature (I{default}).'))
2474            parser.add_option_group(group)
2475
2476            group = optparse.OptionGroup(parser, 'Reading Namespace Archives', 'Locating and loading (or inhibiting load of) namespace archives.')
2477            group.add_option('--archive-path', metavar="PATH",
2478                             help=self.__stripSpaces(self.archivePath.__doc__))
2479            group.add_option('--import-augmentable-namespace', metavar="URI",
2480                             action='append',
2481                             help=self.__stripSpaces(self.addImportAugmentableNamespace.__doc__))
2482            group.add_option('--no-load-namespace', metavar="URI",
2483                             action='append',
2484                             help=self.__stripSpaces(self.addNoLoadNamespace.__doc__))
2485            parser.add_option_group(group)
2486
2487            group = optparse.OptionGroup(parser, 'Writing Namespace Archives', 'Control the location and content of a namespace archive corresponding to a binding generation.')
2488            group.add_option('--archive-to-file', metavar="FILE",
2489                             help=self.__stripSpaces(self.archiveToFile.__doc__))
2490            group.add_option('--public-namespace', metavar="URI",
2491                             action='append',
2492                             help=self.__stripSpaces(self.namespaceVisibilityMap.__doc__ + ' This option adds the namespace as a public archive member.'))
2493            group.add_option('--private-namespace', metavar="URI",
2494                             action='append',
2495                             help=self.__stripSpaces(self.namespaceVisibilityMap.__doc__ + ' This option adds the namespace as a private archive member.'))
2496            group.add_option('--default-namespace-public',
2497                             action="store_true", dest='default_namespace_public',
2498                             help=self.__stripSpaces(self.defaultNamespacePublic.__doc__ + ' This option makes the default C{public} (I{default}).'))
2499            group.add_option('--default-namespace-private',
2500                             action="store_false", dest='default_namespace_public',
2501                             help=self.__stripSpaces(self.defaultNamespacePublic.__doc__ + ' This option makes the default C{private}.'))
2502            parser.add_option_group(group)
2503
2504            group = optparse.OptionGroup(parser, 'Configuring Binding Code Generation', "Control the style and content of the generated bindings.  This is not well-supported, and you are advised to pretend these options don't exist.")
2505            group.add_option('--validate-changes',
2506                              action='store_true', dest='validate_changes',
2507                              help=self.__stripSpaces(self.validateChanges.__doc__ + ' This option turns on validation (default).'))
2508            group.add_option('--no-validate-changes',
2509                              action='store_false', dest='validate_changes',
2510                              help=self.__stripSpaces(self.validateChanges.__doc__ + ' This option turns off validation.'))
2511            parser.add_option_group(group)
2512
2513            group = optparse.OptionGroup(parser, 'Miscellaneous Options', "Anything else.")
2514            group.add_option('--logging-config-file', metavar="FILE",
2515                             help=self.__stripSpaces(self.loggingConfigFile.__doc__))
2516            parser.add_option_group(group)
2517
2518            group = optparse.OptionGroup(parser, 'Maintainer Options', "Don't use these.  They don't exist.  If they did, they'd do different things at different times, and if you used them you'd probably be sorry.")
2519
2520            group.add_option('--allow-absent-module',
2521                              action='store_true', dest='allow_absent_module',
2522                              help=self.__stripSpaces(self.allowAbsentModule.__doc__ + ' This option turns on the feature.'))
2523            group.add_option('--no-allow-absent-module',
2524                             action='store_false', dest='allow_absent_module',
2525                             help=self.__stripSpaces(self.allowAbsentModule.__doc__ + ' This option turns off the feature (default).'))
2526            group.add_option('--allow-builtin-generation',
2527                             action='store_true', dest='allow_builtin_generation',
2528                             help=self.__stripSpaces(self.allowBuiltinGeneration.__doc__ + ' This option turns on the feature.'))
2529            group.add_option('--no-allow-builtin-generation',
2530                             action='store_false', dest='allow_builtin_generation',
2531                             help=self.__stripSpaces(self.allowBuiltinGeneration.__doc__ + ' This option turns off the feature (default).'))
2532            parser.add_option_group(group)
2533
2534            self.__optionParser = parser
2535        return self.__optionParser
2536    __optionParser = None
2537
2538    def getCommandLineArgs (self):
2539        """Return a command line option sequence that could be used to
2540        construct an equivalent configuration.
2541
2542        @note: If you extend the option parser, as is done by
2543        C{pyxbgen}, this may not be able to reconstruct the correct
2544        command line."""
2545        opts = []
2546        module_list = self.moduleList()
2547        schema_list = self.schemaLocationList()
2548        while module_list and schema_list:
2549            ml = module_list.pop(0)
2550            sl = schema_list.pop(0)
2551            if isinstance(sl, tuple):
2552                sl = sl[0]
2553            opts.extend(['--schema-location=' + sl, '--module=' + ml])
2554        for sl in schema_list:
2555            opts.append('--schema-location=' + sl)
2556        if self.schemaRoot() is not None:
2557            opts.append('--schema-root=' + self.schemaRoot())
2558        if self.schemaStrippedPrefix() is not None:
2559            opts.append('--schema-stripped-prefix=%s' + self.schemaStrippedPrefix())
2560        for (pfx, sub) in self.locationPrefixRewriteMap():
2561            opts.append('--location-prefix-rewrite=%s=%s' % (pfx, sub))
2562        if self.modulePrefix() is not None:
2563            opts.append('--module-prefix=' + self.modulePrefix())
2564        opts.append('--binding-root=' + self.bindingRoot())
2565        if self.archivePath() is not None:
2566            opts.append('--archive-path=' + self.archivePath())
2567        for ns in self.noLoadNamespaces():
2568            opts.append('--no-load-namespace=' + ns.uri())
2569        for ns in self.importAugmentableNamespaces():
2570            opts.append('--import-augmentable-namespace=' + ns.uri())
2571        if self.archiveToFile() is not None:
2572            opts.append('--archive-to-file=' + self.archiveToFile())
2573        for (ns, visibility) in self.namespaceVisibilityMap():
2574            if visibility:
2575                opts.append('--public-namespace=' + ns.uri())
2576            else:
2577                opts.append('--private-namespace=' + ns.uri())
2578        if self.defaultNamespacePublic():
2579            opts.append('--default-namespace-public')
2580        else:
2581            opts.append('--default-namespace-private')
2582        for (val, opt) in ( (self.validateChanges(), 'validate-changes'),
2583                            (self.writeForCustomization(), 'write-for-customization'),
2584                            (self.allowAbsentModule(), 'allow-absent-module'),
2585                            (self.allowBuiltinGeneration(), 'allow-builtin-generation') ):
2586            if val:
2587                opts.append('--' + opt)
2588            else:
2589                opts.append('--no-' + opt)
2590        if self.uriContentArchiveDirectory() is not None:
2591            opts.append('--uri-content-archive-directory=%s' + self.uriContentArchiveDirectory())
2592        return opts
2593
2594    def normalizeSchemaLocation (self, sl):
2595        ssp = self.schemaStrippedPrefix()
2596        if ssp and sl.startswith(ssp):
2597            sl = sl[len(ssp):]
2598        return pyxb.utils.utility.NormalizeLocation(sl, self.schemaRoot())
2599
2600    def assignModulePath (self, module_record, module_path=None):
2601        """Provide a Python module path for the module record.
2602
2603        This is the path by which the module bindings associated with
2604        C{module_record} will be imported.
2605
2606        If a path had already been assigned to the module, it is left
2607        in place.
2608
2609        @param module_record: Information about a collection of related bindings
2610        @type module_record: L{pyxb.namespace.archive.ModuleRecord}
2611
2612        @param module_path: Default path to use
2613        @type module_path: C{str}
2614
2615        @return: C{module_record}
2616        """
2617        if module_record.modulePath() is not None:
2618            return module_record
2619        namespace = module_record.namespace()
2620        if not namespace.isAbsentNamespace():
2621            # Use the namespace prefix from a referencing schema if no other clue was given
2622            if (module_path is None) and not (namespace.prefix() is None):
2623                module_path = namespace.prefix()
2624            # Prefer an existing assignment over a new one
2625            module_path = self.namespaceModuleMap().get(namespace.uri(), module_path)
2626        if (module_path is None) and self.generateToFiles():
2627            module_path = pyxb.utils.utility.MakeUnique('binding', self.__unnamedModulePaths)
2628        if (module_path is not None) and self.modulePrefix(): # non-empty value
2629            # Prepend a configured module prefix
2630            module_path = '.'.join([self.modulePrefix(), module_path])
2631        module_record.setModulePath(module_path)
2632        return module_record
2633
2634    __didResolveExternalSchema = False
2635    def resolveExternalSchema (self):
2636        if self.__didResolveExternalSchema:
2637            return
2638
2639        # Locate all relevant archives and the namespaces they
2640        # provide.
2641        pyxb.namespace.archive.NamespaceArchive.PreLoadArchives(self.archivePath())
2642
2643        # Mark the namespaces we were told not to load.  These may be
2644        # namespaces for which we already have bindings in the search
2645        # path, but we want to generate completely new ones.
2646        for ns in self.noLoadNamespaces():
2647            assert isinstance(ns, pyxb.namespace.Namespace)
2648            _log.info("Namespace %s marked not loadable" % (ns,))
2649            ns.markNotLoadable()
2650
2651        # Mark the namespaces that we permit to be extended by import
2652        # statements.
2653        for ns in self.importAugmentableNamespaces():
2654            assert isinstance(ns, pyxb.namespace.Namespace)
2655            _log.info("Namespace %s marked import-augmentable" % (ns,))
2656            ns.setImportAugmentable(True)
2657
2658        # Read all the schema we were told about.
2659        while self.__schemaLocationList:
2660            sl = self.__schemaLocationList.pop(0)
2661            if isinstance(sl, tuple):
2662                (sl, converter) = sl
2663            else:
2664                converter = None
2665            try:
2666                if converter is None:
2667                    schema = xs.schema.CreateFromLocation(absolute_schema_location=self.normalizeSchemaLocation(sl),
2668                                                          generation_uid=self.generationUID(),
2669                                                          uri_content_archive_directory=self.uriContentArchiveDirectory())
2670                else:
2671                    schema = converter(self, sl)
2672                self.addSchema(schema)
2673            except pyxb.SchemaUniquenessError as e:
2674                _log.info('Skipped redundant translation of %s defining %s', e.schemaLocation(), e.namespace())
2675                self.addSchema(e.existingSchema())
2676
2677        # Assign Python modules to hold bindings for the schema we're
2678        # processing.
2679        for schema in self.__schemas:
2680            if isinstance(schema, six.string_types):
2681                schema = xs.schema.CreateFromDocument(schema, generation_uid=self.generationUID())
2682            origin = schema.originRecord()
2683            assert origin is not None
2684            module_path = None
2685            if self.__moduleList:
2686                module_path = self.__moduleList.pop(0)
2687            self.assignModulePath(origin.moduleRecord(), module_path)
2688            assert schema.targetNamespace() == origin.moduleRecord().namespace()
2689            self.addNamespace(schema.targetNamespace())
2690        self.__didResolveExternalSchema = True
2691
2692        # Discard any existing component information
2693        self.__componentGraph = None
2694        self.__componentOrder = None
2695
2696    def __graphFromComponents (self, components, include_lax):
2697        components = components.copy()
2698        component_graph = pyxb.utils.utility.Graph()
2699        need_visit = components.copy()
2700        bindable_fn = lambda _c: isinstance(_c, xs.structures.ElementDeclaration) or _c.isTypeDefinition()
2701        while 0 < len(need_visit):
2702            c = need_visit.pop()
2703            assert c is not None
2704            assert bindable_fn(c) or include_lax
2705            assert c._objectOrigin() is not None, '%s %s has no origin' % (type(c), c)
2706            component_graph.addNode(c)
2707            br = c.bindingRequires(reset=True, include_lax=include_lax)
2708            for cd in br:
2709                assert bindable_fn(cd) or include_lax, '%s produced %s in requires' % (type(c), type(cd))
2710                if cd._objectOrigin() is None:
2711                    assert isinstance(cd, (pyxb.xmlschema.structures.Annotation, pyxb.xmlschema.structures.Wildcard))
2712                    continue
2713                if (cd._objectOrigin().moduleRecord() in self.__moduleRecords) and not (cd in components):
2714                    components.add(cd)
2715                    need_visit.add(cd)
2716                if cd in components:
2717                    component_graph.addEdge(c, cd)
2718        return component_graph
2719
2720    def __resolveComponentDependencies (self):
2721        self.resolveExternalSchema()
2722
2723        bindable_fn = lambda _c: isinstance(_c, xs.structures.ElementDeclaration) or _c.isTypeDefinition()
2724
2725        self.__moduleRecords = set()
2726        all_components = set()
2727        for origin in self.generationUID().associatedObjects():
2728            mr = origin.moduleRecord()
2729            if not (mr in self.__moduleRecords):
2730                self.__moduleRecords.add(mr)
2731                mr.completeGenerationAssociations()
2732            all_components.update(origin.originatedObjects())
2733
2734        namespaces = set()
2735        for mr in self.__moduleRecords:
2736            if mr.namespace().isBuiltinNamespace() and not self.allowBuiltinGeneration():
2737                continue
2738            namespaces.add(mr.namespace())
2739        pyxb.namespace.resolution.ResolveSiblingNamespaces(namespaces)
2740
2741        # Mark module visibility.  Entry-point namespaces default to
2742        # public.
2743        for ns in self.namespaces():
2744            self.__namespaceVisibilityMap.setdefault(ns, True)
2745
2746        # Generate the graph from all components and descend into lax
2747        # requirements; otherwise we might miss anonymous types hidden
2748        # inside attribute declarations and the like.
2749        component_graph = self.__graphFromComponents(all_components, True)
2750
2751        binding_components = set(filter(bindable_fn, component_graph.nodes()))
2752        for c in binding_components:
2753            assert bindable_fn(c), 'Unexpected %s in binding components' % (type(c),)
2754            c._setBindingNamespace(c._objectOrigin().moduleRecord().namespace())
2755
2756        component_order = []
2757        root_sets = self.__graphFromComponents(binding_components, False).rootSetOrder()
2758        if root_sets is None:
2759            raise pyxb.BindingGenerationError('Unable to partial-order named components')
2760        for rs in root_sets:
2761            component_order.extend(sorted(rs, key=lambda _c: _c.schemaOrderSortKey()))
2762
2763        self.__componentGraph = component_graph
2764        self.__componentOrder = component_order
2765
2766    __moduleRecords = None
2767    __componentGraph = None
2768    __componentOrder = None
2769
2770    def moduleRecords (self):
2771        """The set of L{pyxb.namespace.archive.ModuleRecord} instances
2772        associated with schema processed in this generation
2773        instance.
2774
2775        These should be in one-to-one correspondence with the
2776        namespaces for which bindings are being generated.  Multiple
2777        input schemas may contribute to a single module record; all
2778        material in that record is placed in a single binding file.
2779        """
2780        if self.__moduleRecords is None:
2781            self.__resolveComponentDependencies()
2782        return self.__moduleRecords
2783
2784    def componentGraph (self):
2785        if self.__componentGraph is None:
2786            self.__resolveComponentDependencies()
2787        return self.__componentGraph
2788
2789    def componentOrder (self):
2790        if self.__componentOrder is None:
2791            self.__resolveComponentDependencies()
2792        return self.__componentOrder
2793
2794    def __generateBindings (self):
2795
2796        # Note that module graph may have fewer nodes than
2797        # self.moduleRecords(), if a module has no components that
2798        # require binding generation.
2799
2800        module_graph = pyxb.utils.utility.Graph()
2801        [ module_graph.addRoot(_mr) for _mr in self.moduleRecords() ]
2802        for (s, t) in self.componentGraph().edges():
2803            module_graph.addEdge(s._objectOrigin().moduleRecord(), t._objectOrigin().moduleRecord())
2804        module_scc_order = module_graph.sccOrder()
2805
2806        record_binding_map = {}
2807        modules = []
2808        nsvm = self.namespaceVisibilityMap()
2809        for mr_scc in module_scc_order:
2810            scc_modules = [ ]
2811            for mr in mr_scc:
2812                mr._setIsPublic(nsvm.get(mr.namespace(), self.defaultNamespacePublic()))
2813                self.assignModulePath(mr)
2814                if (mr.modulePath() is None) and self.generateToFiles():
2815                    raise pyxb.BindingGenerationError('No prefix or module name available for %s' % (mr,))
2816                if (not mr.isPublic()) and (mr.modulePath() is not None):
2817                    elts = mr.modulePath().split('.')
2818                    elts[-1] = '_%s' % (elts[-1],)
2819                    mr.setModulePath('.'.join(elts))
2820                nsm = NamespaceModule(self, mr, mr_scc)
2821                record_binding_map[mr] = nsm
2822                scc_modules.append(nsm)
2823
2824            scc_modules.sort(key=lambda _nm: _nm.namespace().uri())
2825            modules.extend(scc_modules)
2826            if 1 < len(mr_scc):
2827                ngm = NamespaceGroupModule(self, scc_modules)
2828                modules.append(ngm)
2829                for nsm in scc_modules:
2830                    nsm.setNamespaceGroupModule(ngm)
2831
2832        element_declarations = []
2833        type_definitions = []
2834        for c in self.componentOrder():
2835            if isinstance(c, xs.structures.ElementDeclaration) and c._scopeIsGlobal():
2836                # Only bind elements this pass, so their names get priority in deconfliction
2837                nsm = record_binding_map[c._objectOrigin().moduleRecord()]
2838                nsm.bindComponent(c)
2839                element_declarations.append(c)
2840            elif c.isTypeDefinition():
2841                type_definitions.append(c)
2842            else:
2843                # No binding generation required
2844                pass
2845
2846        simple_type_definitions = []
2847        complex_type_definitions = []
2848        for td in type_definitions:
2849            nsm = record_binding_map[td._objectOrigin().moduleRecord()]
2850            assert nsm is not None, 'No namespace module for %s type %s scope %s namespace %s' % (td.expandedName(), type(td), td._scope(), td.bindingNamespace)
2851            module_context = nsm.bindComponent(td)
2852            assert isinstance(module_context, _ModuleNaming_mixin), 'Unexpected type %s' % (type(module_context),)
2853            if isinstance(td, xs.structures.SimpleTypeDefinition):
2854                _PrepareSimpleTypeDefinition(td, self, nsm, module_context)
2855                simple_type_definitions.append(td)
2856            elif isinstance(td, xs.structures.ComplexTypeDefinition):
2857                _PrepareComplexTypeDefinition(td, self, nsm, module_context)
2858                complex_type_definitions.append(td)
2859            else:
2860                assert False, 'Unexpected component type %s' % (type(td),)
2861
2862        for ngm in modules:
2863            if isinstance(ngm, NamespaceGroupModule):
2864                for m in ngm.namespaceModules():
2865                    m.addImportsFrom(ngm)
2866
2867        for std in simple_type_definitions:
2868            GenerateSTD(std, self)
2869        for ctd in complex_type_definitions:
2870            GenerateCTD(ctd, self)
2871        for ed in element_declarations:
2872            GenerateED(ed, self)
2873
2874        self.__bindingModules = modules
2875
2876    __bindingModules = None
2877    def bindingModules (self):
2878        if self.__componentGraph is None:
2879            self.__resolveComponentDependencies()
2880        if self.__bindingModules is None:
2881            self.__generateBindings()
2882        return self.__bindingModules
2883
2884    def writeNamespaceArchive (self):
2885        archive_file = self.archiveToFile()
2886        if archive_file is not None:
2887            ns_archive = pyxb.namespace.archive.NamespaceArchive(generation_uid=self.generationUID())
2888            try:
2889                ns_archive.writeNamespaces(pyxb.utils.utility.OpenOrCreate(archive_file))
2890                _log.info('Saved parsed schema to %s URI', archive_file)
2891            except Exception as e:
2892                _log.exception('Failure saving preprocessed schema to %s', archive_file)
2893                #try:
2894                #    os.unlink(component_model_file)
2895                #except (OSError, IOError), e:
2896                #    pass
2897                if isinstance(e, (AssertionError, AttributeError, TypeError)):
2898                    raise
2899
2900    def moduleForComponent (self, component):
2901        return _ModuleNaming_mixin.ComponentBindingModule(component)
2902