1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 """
18 The I{sxbasic} module provides classes that represent
19 I{basic} schema objects.
20 """
21
22 from logging import getLogger
23 from suds import *
24 from suds.xsd import *
25 from suds.xsd.sxbase import *
26 from suds.xsd.query import *
27 from suds.sax import splitPrefix, Namespace
28 from suds.transport import TransportError
29 from suds.reader import DocumentReader
30 from urlparse import urljoin
31
32
33 log = getLogger(__name__)
37 """
38 For use with L{NodeFinder} to match restriction.
39 """
42
43
44 -class TypedContent(Content):
45 """
46 Represents any I{typed} content.
47 """
48 - def resolve(self, nobuiltin=False):
49 qref = self.qref()
50 if qref is None:
51 return self
52 key = 'resolved:nb=%s' % nobuiltin
53 cached = self.cache.get(key)
54 if cached is not None:
55 return cached
56 result = self
57 query = TypeQuery(qref)
58 query.history = [self]
59 log.debug('%s, resolving: %s\n using:%s', self.id, qref, query)
60 resolved = query.execute(self.schema)
61 if resolved is None:
62 log.debug(self.schema)
63 raise TypeNotFound(qref)
64 self.cache[key] = resolved
65 if resolved.builtin():
66 if nobuiltin:
67 result = self
68 else:
69 result = resolved
70 else:
71 result = resolved.resolve(nobuiltin)
72 return result
73
75 """
76 Get the I{type} qualified reference to the referenced xsd type.
77 This method takes into account simple types defined through
78 restriction with are detected by determining that self is simple
79 (len=0) and by finding a restriction child.
80 @return: The I{type} qualified reference.
81 @rtype: qref
82 """
83 qref = self.type
84 if qref is None and len(self) == 0:
85 ls = []
86 m = RestrictionMatcher()
87 finder = NodeFinder(m, 1)
88 finder.find(self, ls)
89 if len(ls):
90 return ls[0].ref
91 return qref
92
95 """
96 Represents an (xsd) schema <xs:complexType/> node.
97 @cvar childtags: A list of valid child node names
98 @type childtags: (I{str},...)
99 """
100
112
115
117 for c in self.rawchildren:
118 if c.extension():
119 return True
120 return False
121
123 for c in self.rawchildren:
124 if isinstance(c, SimpleContent) and c.mixed():
125 return True
126 return False
127
128
129 -class Group(SchemaObject):
130 """
131 Represents an (xsd) schema <xs:group/> node.
132 @cvar childtags: A list of valid child node names
133 @type childtags: (I{str},...)
134 """
135
138
151
155
157 return ('name', 'ref',)
158
161 """
162 Represents an (xsd) schema <xs:attributeGroup/> node.
163 @cvar childtags: A list of valid child node names
164 @type childtags: (I{str},...)
165 """
166
169
182
186
188 return ('name', 'ref',)
189
190
191 -class Simple(SchemaObject):
192 """
193 Represents an (xsd) schema <xs:simpleType/> node
194 """
195
198
200 for child, ancestry in self.children():
201 if isinstance(child, Enumeration):
202 return True
203 return False
204
207
210
212 for c in self.rawchildren:
213 if c.extension():
214 return True
215 return False
216
218 for c in self.rawchildren:
219 if c.restriction():
220 return True
221 return False
222
223
224 -class List(SchemaObject):
225 """
226 Represents an (xsd) schema <xs:list/> node
227 """
228
231
234
237
240 """
241 Represents an (xsd) schema <xs:restriction/> node
242 """
243
247
250
264
267
272
275
278 """
279 Represents an (xsd) schema collection node:
280 - sequence
281 - choice
282 - all
283 """
284
287
290 """
291 Represents an (xsd) schema <xs:sequence/> node.
292 """
295
296
297 -class All(Collection):
298 """
299 Represents an (xsd) schema <xs:all/> node.
300 """
303
305 """
306 Represents an (xsd) schema <xs:choice/> node.
307 """
310
311
312 -class ComplexContent(SchemaObject):
313 """
314 Represents an (xsd) schema <xs:complexContent/> node.
315 """
316
318 return ('attribute', 'attributeGroup', 'extension', 'restriction')
319
320 - def extension(self):
321 for c in self.rawchildren:
322 if c.extension():
323 return True
324 return False
325
326 - def restriction(self):
327 for c in self.rawchildren:
328 if c.restriction():
329 return True
330 return False
331
332
333 -class SimpleContent(SchemaObject):
334 """
335 Represents an (xsd) schema <xs:simpleContent/> node.
336 """
337
339 return ('extension', 'restriction')
340
341 - def extension(self):
342 for c in self.rawchildren:
343 if c.extension():
344 return True
345 return False
346
347 - def restriction(self):
348 for c in self.rawchildren:
349 if c.restriction():
350 return True
351 return False
352
355
358 """
359 Represents an (xsd) schema <xs:enumeration/> node
360 """
361
365
368
371 """
372 Represents an (xsd) schema <xs:element/> node.
373 """
374
384
386 """
387 Set the type as any when implicit.
388 An implicit <xs:any/> is when an element has not
389 body and no type defined.
390 @return: self
391 @rtype: L{Element}
392 """
393 if self.type is None and \
394 self.ref is None and \
395 self.root.isempty():
396 self.type = self.anytype()
397 return self
398
401
403 for c in self.rawchildren:
404 if c.extension():
405 return True
406 return False
407
409 for c in self.rawchildren:
410 if c.restriction():
411 return True
412 return False
413
426
430
432 return ('name', 'ref', 'type')
433
442
445 """
446 Represents an (xsd) schema <xs:extension/> node.
447 """
448
452
460
474
479
481 return ( self.ref is not None )
482
485
486
487 -class Import(SchemaObject):
488 """
489 Represents an (xsd) schema <xs:import/> node
490 @cvar locations: A dictionary of namespace locations.
491 @type locations: dict
492 @ivar ns: The imported namespace.
493 @type ns: str
494 @ivar location: The (optional) location.
495 @type location: namespace-uri
496 @ivar opened: Opened and I{imported} flag.
497 @type opened: boolean
498 """
499
500 locations = {}
501
502 @classmethod
503 - def bind(cls, ns, location=None):
504 """
505 Bind a namespace to a schema location (URI).
506 This is used for imports that don't specify a schemaLocation.
507 @param ns: A namespace-uri.
508 @type ns: str
509 @param location: The (optional) schema location for the
510 namespace. (default=ns).
511 @type location: str
512 """
513 if location is None:
514 location = ns
515 cls.locations[ns] = location
516
524
525 - def open(self, options):
526 """
527 Open and import the refrenced schema.
528 @param options: An options dictionary.
529 @type options: L{options.Options}
530 @return: The referenced schema.
531 @rtype: L{Schema}
532 """
533 if self.opened:
534 return
535 self.opened = True
536 log.debug('%s, importing ns="%s", location="%s"', self.id, self.ns[1], self.location)
537 result = self.locate()
538 if result is None:
539 if self.location is None:
540 log.debug('imported schema (%s) not-found', self.ns[1])
541 else:
542 result = self.download(options)
543 log.debug('imported:\n%s', result)
544 return result
545
547 """ find the schema locally """
548 if self.ns[1] == self.schema.tns[1]:
549 return None
550 else:
551 return self.schema.locate(self.ns)
552
554 """ download the schema """
555 url = self.location
556 try:
557 if '://' not in url:
558 url = urljoin(self.schema.baseurl, url)
559 reader = DocumentReader(options)
560 d = reader.open(url)
561 root = d.root()
562 root.set('url', url)
563 return self.schema.instance(root, url, options)
564 except TransportError:
565 msg = 'imported schema (%s) at (%s), failed' % (self.ns[1], url)
566 log.error('%s, %s', self.id, msg, exc_info=True)
567 raise Exception(msg)
568
570 return ('ns', 'location')
571
574 """
575 Represents an (xsd) schema <xs:include/> node
576 @ivar location: The (optional) location.
577 @type location: namespace-uri
578 @ivar opened: Opened and I{imported} flag.
579 @type opened: boolean
580 """
581
582 locations = {}
583
590
591 - def open(self, options):
592 """
593 Open and include the refrenced schema.
594 @param options: An options dictionary.
595 @type options: L{options.Options}
596 @return: The referenced schema.
597 @rtype: L{Schema}
598 """
599 if self.opened:
600 return
601 self.opened = True
602 log.debug('%s, including location="%s"', self.id, self.location)
603 result = self.download(options)
604 log.debug('included:\n%s', result)
605 return result
606
623
625 """ make sure included schema has same tns. """
626 TNS = 'targetNamespace'
627 tns = root.get(TNS)
628 if tns is None:
629 tns = self.schema.tns[1]
630 root.set(TNS, tns)
631 else:
632 if self.schema.tns[1] != tns:
633 raise Exception, '%s mismatch' % TNS
634
635
638
641 """
642 Represents an (xsd) <attribute/> node
643 """
644
648
651
654
656 """
657 Gets the <xs:attribute default=""/> attribute value.
658 @return: The default value for the attribute
659 @rtype: str
660 """
661 return self.root.get('default', default='')
662
664 return ( self.use != 'required' )
665
678
680 return ('name', 'ref', 'type')
681
682
683 -class Any(Content):
684 """
685 Represents an (xsd) <any/> node
686 """
687
693
699
702
705 """
706 @cvar tags: A factory to create object objects based on tag.
707 @type tags: {tag:fn,}
708 """
709
710 tags =\
711 {
712 'import' : Import,
713 'include' : Include,
714 'complexType' : Complex,
715 'group' : Group,
716 'attributeGroup' : AttributeGroup,
717 'simpleType' : Simple,
718 'list' : List,
719 'element' : Element,
720 'attribute' : Attribute,
721 'sequence' : Sequence,
722 'all' : All,
723 'choice' : Choice,
724 'complexContent' : ComplexContent,
725 'simpleContent' : SimpleContent,
726 'restriction' : Restriction,
727 'enumeration' : Enumeration,
728 'extension' : Extension,
729 'any' : Any,
730 }
731
732 @classmethod
734 """
735 Map (override) tag => I{class} mapping.
736 @param tag: An xsd tag name.
737 @type tag: str
738 @param fn: A function or class.
739 @type fn: fn|class.
740 """
741 cls.tags[tag] = fn
742
743 @classmethod
744 - def create(cls, root, schema):
745 """
746 Create an object based on the root tag name.
747 @param root: An XML root element.
748 @type root: L{Element}
749 @param schema: A schema object.
750 @type schema: L{schema.Schema}
751 @return: The created object.
752 @rtype: L{SchemaObject}
753 """
754 fn = cls.tags.get(root.name)
755 if fn is not None:
756 return fn(schema, root)
757 else:
758 return None
759
760 @classmethod
761 - def build(cls, root, schema, filter=('*',)):
781
782 @classmethod
784 imports = []
785 elements = {}
786 attributes = {}
787 types = {}
788 groups = {}
789 agrps = {}
790 for c in children:
791 if isinstance(c, (Import, Include)):
792 imports.append(c)
793 continue
794 if isinstance(c, Attribute):
795 attributes[c.qname] = c
796 continue
797 if isinstance(c, Element):
798 elements[c.qname] = c
799 continue
800 if isinstance(c, Group):
801 groups[c.qname] = c
802 continue
803 if isinstance(c, AttributeGroup):
804 agrps[c.qname] = c
805 continue
806 types[c.qname] = c
807 for i in imports:
808 children.remove(i)
809 return (children, imports, attributes, elements, types, groups, agrps)
810
811
812
813
814
815
816
817 Import.bind(
818 'http://schemas.xmlsoap.org/soap/encoding/',
819 'suds://schemas.xmlsoap.org/soap/encoding/')
820 Import.bind(
821 'http://www.w3.org/XML/1998/namespace',
822 'http://www.w3.org/2001/xml.xsd')
823 Import.bind(
824 'http://www.w3.org/2001/XMLSchema',
825 'http://www.w3.org/2001/XMLSchema.xsd')
826