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