| Home | Trees | Indices | Help |
|
|---|
|
|
1 # -*- coding: utf-8 -*-
2 # Copyright 2009-2013, Peter A. Bigot
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 # not use this file except in compliance with the License. You may obtain a
6 # copy of the License at:
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations
14 # under the License.
15
16 """Classes and global objects related to archiving U{XML
17 Namespaces<http://www.w3.org/TR/2006/REC-xml-names-20060816/index.html>}."""
18
19 import logging
20 import os
21 import os.path
22 import pyxb
23 import pyxb.utils.utility
24 from pyxb.utils import six
25
26 _log = logging.getLogger(__name__)
27
28 PathEnvironmentVariable = 'PYXB_ARCHIVE_PATH'
29 """Environment variable from which default path to pre-loaded namespaces is
30 read. The value should be a colon-separated list of absolute paths. The
31 character C{&} at the start of a member of the list is replaced by the path to
32 the directory where the %{pyxb} modules are found, including a trailing C{/}.
33 For example, use C{&pyxb/bundles//} to enable search of any archive bundled
34 with PyXB.
35
36 @note: If you put a path separator between C{&} and the following path, this
37 will cause the substitution to be ignored."""
38
39 DefaultArchivePrefix = os.path.realpath(os.path.join(os.path.dirname( __file__), '../..'))
40 """The default archive prefix, substituted for C{&} in C{PYXB_ARCHIVE_PATH}."""
43 """Return the archive path as defined by the L{PathEnvironmentVariable},
44 or C{None} if that variable is not defined."""
45 return os.environ.get(PathEnvironmentVariable)
46
47 # Stuff required for pickling
48 from pyxb.utils.six.moves import cPickle as pickle
49 import re
52 """Represent a file from which one or more namespaces can be read, or to
53 which they will be written."""
54
55 # A code used to identify the format of the archive, so we don't
56 # mis-interpret its contents.
57 # YYYYMMDDHHMM
58 __PickleFormat = '200907190858'
59
60 @classmethod
62 """The category name to use when storing references to anonymous type
63 definitions. For example, attribute definitions defined within an
64 attribute use in a model group definition.that can be referenced frojm
65 ax different namespace."""
66 return cls.__AnonymousCategory
67 __AnonymousCategory = '_anonymousTypeDefinition'
68
69 @classmethod
71 """Return a reference to a set specifying the namespace instances that
72 are being archived.
73
74 This is needed to determine whether a component must be serialized as
75 aa reference."""
76 # NB: Use root class explicitly. If we use cls, when this is invoked
77 # by subclasses it gets mangled using the subclass name so the one
78 # defined in this class is not found
79 return NamespaceArchive.__PicklingArchive
80 # Class variable recording the namespace that is currently being
81 # pickled. Used to prevent storing components that belong to
82 # other namespaces. Should be None unless within an invocation of
83 # SaveToFile.
84 __PicklingArchive = None
85
86 __NamespaceArchives = None
87 """A mapping from generation UID to NamespaceArchive instances."""
88
90 """Remove this archive from the set of available archives.
91
92 This is invoked when an archive contains a namespace that the user has
93 specified should not be loaded."""
94 del self.__NamespaceArchives[self.generationUID()]
95 for ns in self.__namespaces:
96 ns._removeArchive(self)
97
98 @classmethod
100 """Return a L{NamespaceArchive} instance associated with the given file.
101
102 To the extent possible, the same file accessed through different paths
103 returns the same L{NamespaceArchive} instance.
104 """
105
106 nsa = NamespaceArchive(archive_path=archive_file, stage=cls._STAGE_uid)
107 rv = cls.__NamespaceArchives.get(nsa.generationUID(), nsa)
108 if rv == nsa:
109 cls.__NamespaceArchives[rv.generationUID()] = rv
110 rv._readToStage(stage)
111 return rv
112
113 __ArchivePattern_re = re.compile('\.wxs$')
114
115 @classmethod
117 """Scan for available archives, associating them with namespaces.
118
119 This only validates potential archive contents; it does not load
120 namespace data from the archives.
121
122 @keyword archive_path: A list of files or directories in which
123 namespace archives can be found. The entries are separated by
124 os.pathsep, which is a colon on POSIX platforms and a semi-colon on
125 Windows. See L{PathEnvironmentVariable}. Defaults to
126 L{GetArchivePath()}. If not defaulted, C{reset} will be forced to
127 C{True}. For any directory in the path, all files ending with
128 C{.wxs} are examined.
129
130 @keyword reset: If C{False} (default), the most recently read set of
131 archives is returned; if C{True}, the archive path is re-scanned and the
132 namespace associations validated.
133 """
134
135 from pyxb.namespace import builtin
136
137 reset = reset or (archive_path is not None) or (cls.__NamespaceArchives is None)
138 if reset:
139 # Get a list of pre-existing archives, initializing the map if
140 # this is the first time through.
141 if cls.__NamespaceArchives is None:
142 cls.__NamespaceArchives = { }
143 existing_archives = set(six.itervalues(cls.__NamespaceArchives))
144 archive_set = set()
145
146 # Ensure we have an archive path. If not, don't do anything.
147 if archive_path is None:
148 archive_path = GetArchivePath()
149 if archive_path is not None:
150
151 # Get archive instances for everything in the archive path
152 candidate_files = pyxb.utils.utility.GetMatchingFiles(archive_path, cls.__ArchivePattern_re,
153 default_path_wildcard='+', default_path=GetArchivePath(),
154 prefix_pattern='&', prefix_substituend=DefaultArchivePrefix)
155 for afn in candidate_files:
156 try:
157 nsa = cls.__GetArchiveInstance(afn, stage=cls._STAGE_readModules)
158 archive_set.add(nsa)
159 except pickle.UnpicklingError:
160 _log.exception('Cannot unpickle archive %s', afn)
161 except pyxb.NamespaceArchiveError:
162 _log.exception('Cannot process archive %s', afn)
163
164 # Do this for two reasons: first, to get an iterable that won't
165 # cause problems when we remove unresolvable archives from
166 # archive_set; and second to aid with forced dependency inversion
167 # testing
168 ordered_archives = sorted(list(archive_set), key=lambda _a: _a.archivePath())
169 ordered_archives.reverse()
170
171 # Create a graph that identifies dependencies between the archives
172 archive_map = { }
173 for a in archive_set:
174 archive_map[a.generationUID()] = a
175 archive_graph = pyxb.utils.utility.Graph()
176 for a in ordered_archives:
177 prereqs = a._unsatisfiedModulePrerequisites()
178 if 0 < len(prereqs):
179 for p in prereqs:
180 if builtin.BuiltInObjectUID == p:
181 continue
182 da = archive_map.get(p)
183 if da is None:
184 _log.warning('%s depends on unavailable archive %s', a, p)
185 archive_set.remove(a)
186 else:
187 archive_graph.addEdge(a, da)
188 else:
189 archive_graph.addRoot(a)
190
191 # Verify that there are no dependency loops.
192 archive_scc = archive_graph.sccOrder()
193 for scc in archive_scc:
194 if 1 < len(scc):
195 raise pyxb.LogicError("Cycle in archive dependencies. How'd you do that?\n " + "\n ".join([ _a.archivePath() for _a in scc ]))
196 archive = scc[0]
197 if not (archive in archive_set):
198 archive.discard()
199 existing_archives.remove(archive)
200 continue
201 #archive._readToStage(cls._STAGE_COMPLETE)
202
203 # Discard any archives that we used to know about but now aren't
204 # supposed to. @todo make this friendlier in the case of archives
205 # we've already incorporated.
206 for archive in existing_archives.difference(archive_set):
207 _log.info('Discarding excluded archive %s', archive)
208 archive.discard()
209
213 __archivePath = None
214
216 """The unique identifier for the generation that produced this archive."""
217 return self.__generationUID
218 __generationUID = None
219
221 """Return C{True} iff it is permissible to load the archive.
222 Archives created for output cannot be loaded."""
223 return self.__isLoadable
224 __isLoadable = None
225
227 self.__moduleRecords = set()
228 namespaces = set()
229 for ns in pyxb.namespace.utility.AvailableNamespaces():
230 # @todo allow these; right now it's usually the XML
231 # namespace and we're not prepared to reconcile
232 # redefinitions of those components.
233 if ns.isUndeclaredNamespace():
234 continue
235 mr = ns.lookupModuleRecordByUID(self.generationUID())
236 if mr is not None:
237 namespaces.add(ns)
238 mr.prepareForArchive(self)
239 self.__moduleRecords.add(mr)
240 self.__namespaces.update(namespaces)
242 """Return the set of L{module records <ModuleRecord>} stored in this
243 archive.
244
245 Each module record represents"""
246 return self.__moduleRecords
247 __moduleRecords = None
248
249 @classmethod
251 """Return the L{NamespaceArchive} instance that can be found at the
252 given path."""
253 return cls.__GetArchiveInstance(archive_file)
254
255 # States in the finite automaton that is used to read archive contents.
256 _STAGE_UNOPENED = 0 # Haven't even checked for existence
257 _STAGE_uid = 1 # Verified archive exists, obtained generation UID from it
258 _STAGE_readModules = 2 # Read module records from archive, which includes UID dependences
259 _STAGE_validateModules = 3 # Verified pre-requisites for module loading
260 _STAGE_readComponents = 4 # Extracted components from archive and integrated into namespaces
261 _STAGE_COMPLETE = _STAGE_readComponents
262
264 return self.__stage
265 __stage = None
266
268 """Create a new namespace archive.
269
270 If C{namespaces} is given, this is an output archive.
271
272 If C{namespaces} is absent, this is an input archive.
273
274 @raise IOError: error attempting to read the archive file
275 @raise pickle.UnpicklingError: something is wrong with the format of the library
276 """
277 self.__namespaces = set()
278 if generation_uid is not None:
279 if archive_path:
280 raise pyxb.LogicError('NamespaceArchive: cannot define both namespaces and archive_path')
281 self.__generationUID = generation_uid
282 self.__locateModuleRecords()
283 elif archive_path is not None:
284 if generation_uid is not None:
285 raise pyxb.LogicError('NamespaceArchive: cannot provide generation_uid with archive_path')
286 self.__archivePath = archive_path
287 self.__stage = self._STAGE_UNOPENED
288 self.__isLoadable = loadable
289 if self.__isLoadable:
290 if stage is None:
291 stage = self._STAGE_moduleRecords
292 self._readToStage(stage)
293 else:
294 pass
295
297 """Add the given namespace to the set that is to be stored in this archive."""
298 if namespace.isAbsentNamespace():
299 raise pyxb.NamespaceArchiveError('Cannot archive absent namespace')
300 self.__namespaces.add(namespace)
301
303 """Add the given namespaces to the set that is to be stored in this archive."""
304 [ self.add(_ns) for _ns in namespace_set ]
305
309 __namespaces = None
310
312 if isinstance(output, six.string_types):
313 output = open(output, 'wb')
314 pickler = pickle.Pickler(output, -1)
315
316 # The format of the archive
317 pickler.dump(NamespaceArchive.__PickleFormat)
318
319 # The UID for the set
320 assert self.generationUID() is not None
321 pickler.dump(self.generationUID())
322
323 return pickler
324
326 unpickler = pickle.Unpickler(open(self.__archivePath, 'rb'))
327
328 fmt = unpickler.load()
329 if self.__PickleFormat != fmt:
330 raise pyxb.NamespaceArchiveError('Archive format is %s, require %s' % (fmt, self.__PickleFormat))
331
332 self.__generationUID = unpickler.load()
333
334 return unpickler
335
337 mrs = unpickler.load()
338 assert isinstance(mrs, set), 'Expected set got %s from %s' % (type(mrs), self.archivePath())
339 if self.__moduleRecords is None:
340 for mr in mrs.copy():
341 mr2 = mr.namespace().lookupModuleRecordByUID(mr.generationUID())
342 if mr2 is not None:
343 mr2._setFromOther(mr, self)
344 mrs.remove(mr)
345 self.__moduleRecords = set()
346 assert 0 == len(self.__namespaces)
347 for mr in mrs:
348 mr._setArchive(self)
349 ns = mr.namespace()
350 ns.addModuleRecord(mr)
351 self.__namespaces.add(ns)
352 self.__moduleRecords.add(mr)
353 else:
354 # Verify the archive still has what was in it when we created this.
355 for mr in mrs:
356 mr2 = mr.namespace().lookupModuleRecordByUID(mr.generationUID())
357 if not (mr2 in self.__moduleRecords):
358 raise pyxb.NamespaceArchiveError('Lost module record %s %s from %s' % (mr.namespace(), mr.generationUID(), self.archivePath()))
359
361 prereq_uids = set()
362 for mr in self.__moduleRecords:
363 prereq_uids.update(mr.dependsOnExternal())
364 return prereq_uids
365
367 from pyxb.namespace import builtin
368 prereq_uids = self._unsatisfiedModulePrerequisites()
369 for uid in prereq_uids:
370 if builtin.BuiltInObjectUID == uid:
371 continue
372 depends_on = self.__NamespaceArchives.get(uid)
373 if depends_on is None:
374 raise pyxb.NamespaceArchiveError('%s: archive depends on unavailable archive %s' % (self.archivePath(), uid))
375 depends_on._readToStage(stage)
376
378 self.__validatePrerequisites(self._STAGE_validateModules)
379 for mr in self.__moduleRecords:
380 ns = mr.namespace()
381 for base_uid in mr.dependsOnExternal():
382 xmr = ns.lookupModuleRecordByUID(base_uid)
383 if xmr is None:
384 raise pyxb.NamespaceArchiveError('Module %s depends on external module %s, not available in archive path' % (mr.generationUID(), base_uid))
385 if not xmr.isIncorporated():
386 _log.info('Need to incorporate data from %s', xmr)
387 else:
388 _log.info('Have required base data %s', xmr)
389
390 for origin in mr.origins():
391 for (cat, names) in six.iteritems(origin.categoryMembers()):
392 if not (cat in ns.categories()):
393 continue
394 cross_objects = names.intersection(six.iterkeys(ns.categoryMap(cat)))
395 if 0 < len(cross_objects):
396 raise pyxb.NamespaceArchiveError('Archive %s namespace %s module %s origin %s archive/active conflict on category %s: %s' % (self.__archivePath, ns, mr, origin, cat, " ".join(cross_objects)))
397 _log.info('%s no conflicts on %d names', cat, len(names))
398
400 self.__validatePrerequisites(self._STAGE_readComponents)
401 for n in range(len(self.__moduleRecords)):
402 ns = unpickler.load()
403 mr = ns.lookupModuleRecordByUID(self.generationUID())
404 assert mr in self.__moduleRecords
405 assert not mr.isIncorporated()
406 objects = unpickler.load()
407 mr._loadCategoryObjects(objects)
408
409 __unpickler = None
411 if self.__stage is None:
412 raise pyxb.NamespaceArchiveError('Attempt to read from invalid archive %s' % (self,))
413 try:
414 while self.__stage < stage:
415 if self.__stage < self._STAGE_uid:
416 self.__unpickler = self.__createUnpickler()
417 self.__stage = self._STAGE_uid
418 continue
419 if self.__stage < self._STAGE_readModules:
420 assert self.__unpickler is not None
421 self.__readModules(self.__unpickler)
422 self.__stage = self._STAGE_readModules
423 continue
424 if self.__stage < self._STAGE_validateModules:
425 self.__validateModules()
426 self.__stage = self._STAGE_validateModules
427 continue
428 if self.__stage < self._STAGE_readComponents:
429 assert self.__unpickler is not None
430 self.__stage = self._STAGE_readComponents
431 self.__readComponentSet(self.__unpickler)
432 self.__unpickler = None
433 continue
434 raise pyxb.LogicError('Too many stages (at %s, want %s)' % (self.__stage, stage))
435 except:
436 self.__stage = None
437 self.__unpickler = None
438 raise
439
441 """Read all the components from this archive, integrating them into
442 their respective namespaces."""
443 self._readToStage(self._STAGE_COMPLETE)
444
446 """Store the namespaces into the archive.
447
448 @param output: An instance substitutable for a writable file, or the
449 name of a file to write to.
450 """
451 import sys
452
453 assert NamespaceArchive.__PicklingArchive is None
454 NamespaceArchive.__PicklingArchive = self
455 assert self.__moduleRecords is not None
456
457 # Recalculate the record/object associations: we didn't assign
458 # anonymous names to the indeterminate scope objects because they
459 # weren't needed for bindings, but they are needed in the archive.
460 for mr in self.__moduleRecords:
461 mr.namespace()._associateOrigins(mr)
462
463 try:
464 # See http://bugs.python.org/issue3338
465 recursion_limit = sys.getrecursionlimit()
466 sys.setrecursionlimit(10 * recursion_limit)
467
468 pickler = self.__createPickler(output)
469
470 assert isinstance(self.__moduleRecords, set)
471 pickler.dump(self.__moduleRecords)
472
473 for mr in self.__moduleRecords:
474 pickler.dump(mr.namespace())
475 pickler.dump(mr.categoryObjects())
476 finally:
477 sys.setrecursionlimit(recursion_limit)
478 NamespaceArchive.__PicklingArchive = None
479
481 archive_path = self.__archivePath
482 if archive_path is None:
483 archive_path = '??'
484 return 'NSArchive@%s' % (archive_path,)
485
487 """Mix-in to any object that can be stored in a namespace within an archive."""
488
489 # Need to set this per category item
490 __objectOrigin = None
492 return self.__objectOrigin
494 if (self.__objectOrigin is not None) and (not override):
495 if self.__objectOrigin != object_origin:
496 raise pyxb.LogicError('Inconsistent origins for object %s: %s %s' % (self, self.__objectOrigin, object_origin))
497 else:
498 self.__objectOrigin = object_origin
499
501 #assert self.__objectOrigin is not None
502 if self._objectOrigin() is not None:
503 return getattr(super(_ArchivableObject_mixin, self), '_prepareForArchive_csc', lambda *_args,**_kw: self)(self._objectOrigin().moduleRecord())
504 assert not isinstance(self, pyxb.xmlschema.structures._NamedComponent_mixin)
505
507 return getattr(super(_ArchivableObject_mixin, self), '_updateFromOther_csc', lambda *_args,**_kw: self)(other)
508
510 """Update this instance with additional information provided by the other instance.
511
512 This is used, for example, when a built-in type is already registered
513 in the namespace, but we've processed the corresponding schema and
514 have obtained more details."""
515 assert self != other
516 return self._updateFromOther_csc(other)
517
519 from pyxb.namespace import builtin
520 assert self._objectOrigin()
521 return builtin.BuiltInObjectUID == self._objectOrigin().generationUID()
522
524 """Encapsulate the operations and data relevant to archiving namespaces.
525
526 This class mixes-in to L{pyxb.namespace.Namespace}"""
527
529 """CSC extension to reset fields of a Namespace.
530
531 This one handles category-related data."""
532 getattr(super(_NamespaceArchivable_mixin, self), '_reset', lambda *args, **kw: None)()
533 self.__loadedFromArchive = None
534 self.__wroteToArchive = None
535 self.__active = False
536 self.__moduleRecordMap = {}
537
539 return self.__loadedFromArchive
540
541 __wroteToArchive = None
542 __loadedFromArchive = None
543
545 if self.__isActive and empty_inactive:
546 for (ct, cm) in six.iteritems(self._categoryMap()):
547 if 0 < len(cm):
548 return True
549 return False
550 return self.__isActive
551
553 self.__isActive = True
554 __isActive = None
555
558
564
566 # Yes, I do want this to raise KeyError if the archive is not present
567 mr = self.__moduleRecordMap[archive.generationUID()]
568 assert not mr.isIncorporated(), 'Removing archive %s after incorporation' % (archive.archivePath(),)
569 del self.__moduleRecordMap[archive.generationUID()]
570
572 """Return C{True} iff the component model for this namespace can be
573 loaded from a namespace archive."""
574 for mr in self.moduleRecords():
575 if mr.isLoadable():
576 return True
577 return False
578
580 """Return C{True} iff the component model for this namespace may be
581 extended by import directives.
582
583 This is the case if the namespace has been marked with
584 L{setImportAugmentable}, or if there is no archive or built-in that
585 provides a component model for the namespace."""
586 if self.__isImportAugmentable:
587 return True
588 for mr in self.moduleRecords():
589 if mr.isLoadable() or mr.isIncorporated():
590 return False
591 return True
594 __isImportAugmentable = False
595
597 """Return the list of archives from which components for this
598 namespace can be loaded."""
599 rv = []
600 for mr in self.moduleRecords():
601 if mr.isLoadable():
602 rv.append(mr.archive())
603 return rv
604
607 __moduleRecordMap = None
608
610 assert isinstance(module_record, ModuleRecord)
611 assert not (module_record.generationUID() in self.__moduleRecordMap)
612 self.__moduleRecordMap[module_record.generationUID()] = module_record
613 return module_record
615 rv = self.__moduleRecordMap.get(generation_uid)
616 if (rv is None) and create_if_missing:
617 rv = self.addModuleRecord(ModuleRecord(self, generation_uid, *args, **kw))
618 return rv
619
621 #assert not self.__isActive, 'ERROR: State set for active namespace %s' % (self,)
622 return getattr(super(_NamespaceArchivable_mixin, self), '_getState_csc', lambda _kw: _kw)(kw)
623
625 """Prevent loading this namespace from an archive.
626
627 This marks all archives in which the namespace appears, whether
628 publically or privately, as not loadable."""
629 if self._loadedFromArchive():
630 raise pyxb.NamespaceError(self, 'cannot mark not loadable when already loaded')
631 for mr in self.moduleRecords():
632 mr._setIsLoadable(False)
633
635 __PrivateTransient = set()
636
638 return self.__namespace
639 __namespace = None
640
642 return self.__archive
646 __archive = None
647 __PrivateTransient.add('archive')
648
650 return self.__isPublic
654 __isPublic = None
655
659 assert self.__isLoadable
660 self.__isIncorporated = True
661 self.__isLoadable = False
662 return self
663 __isIncorporated = None
664 __PrivateTransient.add('isIncorporated')
665
671 __isLoadable = None
672
674 return self.__generationUID
675 __generationUID = None
676
680 assert isinstance(origin, _ObjectOrigin)
681 assert not (origin.signature() in self.__originMap)
682 self.__originMap[origin.signature()] = origin
683 return origin
687 if self.__originMap is None:
688 self.__originMap = {}
689 else:
690 self.__originMap.clear()
691 [ self.addOrigin(_o) for _o in origins ]
692 return self
693 __originMap = None
694
700
702 return self.__modulePath
704 assert (module_path is None) or isinstance(module_path, six.string_types)
705 self.__modulePath = module_path
706 return self
707 __modulePath = None
708
710 return self.__referencedNamespaces
717 __referencedNamespaces = None
718
719 __constructedLocally = False
720 __PrivateTransient.add('constructedLocally')
721
723 from pyxb.namespace import builtin
724
725 super(ModuleRecord, self).__init__()
726 self.__namespace = namespace
727 assert (generation_uid != builtin.BuiltInObjectUID) or namespace.isBuiltinNamespace()
728 self.__isPublic = kw.get('is_public', False)
729 self.__isIncorporated = kw.get('is_incorporated', False)
730 self.__isLoadable = kw.get('is_loadable', True)
731 assert isinstance(generation_uid, pyxb.utils.utility.UniqueIdentifier)
732 self.__generationUID = generation_uid
733 self.__modulePath = kw.get('module_path')
734 self.__originMap = {}
735 self.__referencedNamespaces = set()
736 self.__categoryObjects = { }
737 self.__constructedLocally = True
738 self.__dependsOnExternal = set()
739
741 if (not self.__constructedLocally) or other.__constructedLocally:
742 raise pyxb.ImplementationError('Module record update requires local to be updated from archive')
743 assert self.__generationUID == other.__generationUID
744 assert self.__archive is None
745 self.__isPublic = other.__isPublic
746 assert not self.__isIncorporated
747 self.__isLoadable = other.__isLoadable
748 self.__modulePath = other.__modulePath
749 self.__originMap.update(other.__originMap)
750 self.__referencedNamespaces.update(other.__referencedNamespaces)
751 if not (other.__categoryObjects is None):
752 self.__categoryObjects.update(other.__categoryObjects)
753 self.__dependsOnExternal.update(other.__dependsOnExternal)
754 self._setArchive(archive)
755
757 return self.__categoryObjects
759 self.__categoryObjects.clear()
760 for origin in self.origins():
761 origin.resetCategoryMembers()
766 assert self.__categoryObjects is None
767 assert not self.__constructedLocally
768 ns = self.namespace()
769 ns.configureCategories(six.iterkeys(category_objects))
770 for (cat, obj_map) in six.iteritems(category_objects):
771 current_map = ns.categoryMap(cat)
772 for (local_name, component) in six.iteritems(obj_map):
773 existing_component = current_map.get(local_name)
774 if existing_component is None:
775 current_map[local_name] = component
776 elif existing_component._allowUpdateFromOther(component):
777 existing_component._updateFromOther(component)
778 else:
779 raise pyxb.NamespaceError(self, 'Load attempted to override %s %s in %s' % (cat, local_name, self.namespace()))
780 self.markIncorporated()
781 __categoryObjects = None
782 __PrivateTransient.add('categoryObjects')
783
785 return self.__dependsOnExternal
786 __dependsOnExternal = None
787
789 assert self.archive() is None
790 self._setArchive(archive)
791 ns = self.namespace()
792 self.__dependsOnExternal.clear()
793 for mr in ns.moduleRecords():
794 if mr != self:
795 _log.info('This gen depends on %s', mr)
796 self.__dependsOnExternal.add(mr.generationUID())
797 for obj in ns._namedObjects().union(ns.components()):
798 if isinstance(obj, _ArchivableObject_mixin):
799 if obj._objectOrigin():
800 obj._prepareForArchive(self)
801
803 self.namespace()._transferReferencedNamespaces(self)
804 self.namespace()._associateOrigins(self)
805
808
810 """Marker class for objects that can serve as an origin for an object in a
811 namespace."""
812 __PrivateTransient = set()
813
815 return self.__signature
816 __signature = None
817
819 return self.__moduleRecord
820 __moduleRecord = None
821
824
827
829 self.__signature = kw.pop('signature', None)
830 super(_ObjectOrigin, self).__init__(**kw)
831 self.__moduleRecord = namespace.lookupModuleRecordByUID(generation_uid, create_if_missing=True, **kw)
832 self.__moduleRecord.addOrigin(self)
833 self.__categoryMembers = { }
834 self.__categoryObjectMap = { }
835
837 self.__categoryMembers.clear()
838 self.__categoryObjectMap.clear()
839 self.__originatedComponents = None
841 self.__categoryMembers.setdefault(category, set()).add(name)
842 self.__categoryObjectMap.setdefault(category, {})[name] = obj
843 self.__moduleRecord._addCategoryObject(category, name, obj)
845 return self.__categoryMembers
847 if self.__originatedObjects is None:
848 components = set()
849 [ components.update(six.itervalues(_v)) for _v in six.itervalues(self.__categoryObjectMap) ]
850 self.__originatedObjects = frozenset(components)
851 return self.__originatedObjects
852
853 # The set of category names associated with objects. Don't throw this
854 # away and use categoryObjectMap.keys() instead: that's transient, and we
855 # need this to have a value when read from an archive.
856 __categoryMembers = None
857
858 # Map from category name to a map from an object name to the object
859 __categoryObjectMap = None
860 __PrivateTransient.add('categoryObjectMap')
861
862 # The set of objects that originated at this origin
863 __originatedObjects = None
864 __PrivateTransient.add('originatedObjects')
865
867 """Holds the data regarding components derived from a single schema.
868
869 Coupled to a particular namespace through the
870 L{_NamespaceComponentAssociation_mixin}.
871 """
872
873 __PrivateTransient = set()
874
876 schema = kw.get('schema')
877 if schema is not None:
878 assert not ('location' in kw)
879 kw['location'] = schema.location()
880 assert not ('signature' in kw)
881 kw['signature'] = schema.signature()
882 assert not ('generation_uid' in kw)
883 kw['generation_uid'] = schema.generationUID()
884 assert not ('namespace' in kw)
885 kw['namespace'] = schema.targetNamespace()
886 assert not ('version' in kw)
887 kw['version'] = schema.schemaAttribute('version')
888
890 """Determine whether this record matches the parameters.
891
892 @keyword schema: a L{pyxb.xmlschema.structures.Schema} instance from
893 which the other parameters are obtained.
894 @keyword location: a schema location (URI)
895 @keyword signature: a schema signature
896 @return: C{True} iff I{either} C{location} or C{signature} matches."""
897 self.__setDefaultKW(kw)
898 location = kw.get('location')
899 if (location is not None) and (self.location() == location):
900 return True
901 signature = kw.get('signature')
902 if (signature is not None) and (self.signature() == signature):
903 return True
904 return False
905
907 return self.__location
908 __location = None
909
911 return self.__schema
912 __schema = None
913 __PrivateTransient.add('schema')
914
916 return self.__version
917 __version = None
918
920 self.__setDefaultKW(kw)
921 self.__schema = kw.pop('schema', None)
922 self.__location = kw.pop('location', None)
923 self.__version = kw.pop('version', None)
924 super(_SchemaOrigin, self).__init__(kw.pop('namespace'), kw.pop('generation_uid'), **kw)
925
932
934
936 return self.__rootNamespaces
937 __rootNamespaces = None
938
940 if reset or (self.__namespaceGraph is None):
941 self.__namespaceGraph = pyxb.utils.utility.Graph()
942 for ns in self.rootNamespaces():
943 self.__namespaceGraph.addRoot(ns)
944
945 # Make sure all referenced namespaces have valid components
946 need_check = self.__rootNamespaces.copy()
947 done_check = set()
948 while 0 < len(need_check):
949 ns = need_check.pop()
950 ns.validateComponentModel()
951 self.__namespaceGraph.addNode(ns)
952 for rns in ns.referencedNamespaces().union(ns.importedNamespaces()):
953 self.__namespaceGraph.addEdge(ns, rns)
954 if not rns in done_check:
955 need_check.add(rns)
956 if not ns.hasSchemaComponents():
957 _log.warning('Referenced %s has no schema components', ns.uri())
958 done_check.add(ns)
959 assert done_check == self.__namespaceGraph.nodes()
960
961 return self.__namespaceGraph
962 __namespaceGraph = None
963
966
968 siblings = set()
969 ns_graph = self.namespaceGraph(reset)
970 for ns in self.__rootNamespaces:
971 ns_siblings = ns_graph.sccMap().get(ns)
972 if ns_siblings is not None:
973 siblings.update(ns_siblings)
974 else:
975 siblings.add(ns)
976 return siblings
977
979 if self.__siblingNamespaces is None:
980 self.__siblingNamespaces = self.siblingsFromGraph()
981 return self.__siblingNamespaces
982
984 self.__siblingNamespaces = sibling_namespaces
985
986 __siblingNamespaces = None
987
990
992 if reset or (self.__componentGraph is None):
993 self.__componentGraph = pyxb.utils.utility.Graph()
994 all_components = set()
995 for ns in self.siblingNamespaces():
996 [ all_components.add(_c) for _c in ns.components() if _c.hasBinding() ]
997
998 need_visit = all_components.copy()
999 while 0 < len(need_visit):
1000 c = need_visit.pop()
1001 self.__componentGraph.addNode(c)
1002 for cd in c.bindingRequires(include_lax=True):
1003 if cd in all_components:
1004 self.__componentGraph.addEdge(c, cd)
1005 return self.__componentGraph
1006 __componentGraph = None
1007
1010
1012 namespace_set = set(kw.get('namespace_set', []))
1013 namespace = kw.get('namespace')
1014 if namespace is not None:
1015 namespace_set.add(namespace)
1016 if 0 == len(namespace_set):
1017 raise pyxb.LogicError('NamespaceDependencies requires at least one root namespace')
1018 self.__rootNamespaces = namespace_set
1019
1020
1021 ## Local Variables:
1022 ## fill-column:78
1023 ## End:
1024
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Sun Oct 19 06:24:46 2014 | http://epydoc.sourceforge.net |