Package suds :: Package xsd :: Module sxbasic
[hide private]
[frames] | no frames]

Source Code for Module suds.xsd.sxbasic

  1  # This program is free software; you can redistribute it and/or modify 
  2  # it under the terms of the (LGPL) GNU Lesser General Public License as 
  3  # published by the Free Software Foundation; either version 3 of the  
  4  # License, or (at your option) any later version. 
  5  # 
  6  # This program is distributed in the hope that it will be useful, 
  7  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
  8  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  9  # GNU Library Lesser General Public License for more details at 
 10  # ( http://www.gnu.org/licenses/lgpl.html ). 
 11  # 
 12  # You should have received a copy of the GNU Lesser General Public License 
 13  # along with this program; if not, write to the Free Software 
 14  # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
 15  # written by: Jeff Ortel ( jortel@redhat.com ) 
 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__) 
34 35 36 -class RestrictionMatcher:
37 """ 38 For use with L{NodeFinder} to match restriction. 39 """
40 - def match(self, n):
41 return isinstance(n, Restriction)
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
74 - def qref(self):
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
93 94 -class Complex(SchemaObject):
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
101 - def childtags(self):
102 return ( 103 'attribute', 104 'attributeGroup', 105 'sequence', 106 'all', 107 'choice', 108 'complexContent', 109 'simpleContent', 110 'any', 111 'group')
112
113 - def description(self):
114 return ('name',)
115
116 - def extension(self):
117 for c in self.rawchildren: 118 if c.extension(): 119 return True 120 return False
121
122 - def mixed(self):
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
136 - def childtags(self):
137 return ('sequence', 'all', 'choice')
138
139 - def dependencies(self):
140 deps = [] 141 midx = None 142 if self.ref is not None: 143 query = GroupQuery(self.ref) 144 g = query.execute(self.schema) 145 if g is None: 146 log.debug(self.schema) 147 raise TypeNotFound(self.ref) 148 deps.append(g) 149 midx = 0 150 return (midx, deps)
151
152 - def merge(self, other):
153 SchemaObject.merge(self, other) 154 self.rawchildren = other.rawchildren
155
156 - def description(self):
157 return ('name', 'ref',)
158
159 160 -class AttributeGroup(SchemaObject):
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
167 - def childtags(self):
168 return ('attribute', 'attributeGroup')
169
170 - def dependencies(self):
171 deps = [] 172 midx = None 173 if self.ref is not None: 174 query = AttrGroupQuery(self.ref) 175 ag = query.execute(self.schema) 176 if ag is None: 177 log.debug(self.schema) 178 raise TypeNotFound(self.ref) 179 deps.append(ag) 180 midx = 0 181 return (midx, deps)
182
183 - def merge(self, other):
184 SchemaObject.merge(self, other) 185 self.rawchildren = other.rawchildren
186
187 - def description(self):
188 return ('name', 'ref',)
189
190 191 -class Simple(SchemaObject):
192 """ 193 Represents an (xsd) schema <xs:simpleType/> node 194 """ 195
196 - def childtags(self):
197 return ('restriction', 'any', 'list',)
198
199 - def enum(self):
200 for child, ancestry in self.children(): 201 if isinstance(child, Enumeration): 202 return True 203 return False
204
205 - def mixed(self):
206 return len(self)
207
208 - def description(self):
209 return ('name',)
210
211 - def extension(self):
212 for c in self.rawchildren: 213 if c.extension(): 214 return True 215 return False
216
217 - def restriction(self):
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
229 - def childtags(self):
230 return ()
231
232 - def description(self):
233 return ('name',)
234
235 - def xslist(self):
236 return True
237
238 239 -class Restriction(SchemaObject):
240 """ 241 Represents an (xsd) schema <xs:restriction/> node 242 """ 243
244 - def __init__(self, schema, root):
245 SchemaObject.__init__(self, schema, root) 246 self.ref = root.get('base')
247
248 - def childtags(self):
249 return ('enumeration', 'attribute', 'attributeGroup')
250
251 - def dependencies(self):
252 deps = [] 253 midx = None 254 if self.ref is not None: 255 query = TypeQuery(self.ref) 256 super = query.execute(self.schema) 257 if super is None: 258 log.debug(self.schema) 259 raise TypeNotFound(self.ref) 260 if not super.builtin(): 261 deps.append(super) 262 midx = 0 263 return (midx, deps)
264
265 - def restriction(self):
266 return True
267
268 - def merge(self, other):
269 SchemaObject.merge(self, other) 270 filter = Filter(False, self.rawchildren) 271 self.prepend(self.rawchildren, other.rawchildren, filter)
272
273 - def description(self):
274 return ('ref',)
275
276 277 -class Collection(SchemaObject):
278 """ 279 Represents an (xsd) schema collection node: 280 - sequence 281 - choice 282 - all 283 """ 284
285 - def childtags(self):
286 return ('element', 'sequence', 'all', 'choice', 'any', 'group')
287
288 289 -class Sequence(Collection):
290 """ 291 Represents an (xsd) schema <xs:sequence/> node. 292 """
293 - def sequence(self):
294 return True
295
296 297 -class All(Collection):
298 """ 299 Represents an (xsd) schema <xs:all/> node. 300 """
301 - def all(self):
302 return True
303
304 -class Choice(Collection):
305 """ 306 Represents an (xsd) schema <xs:choice/> node. 307 """
308 - def choice(self):
309 return True
310
311 312 -class ComplexContent(SchemaObject):
313 """ 314 Represents an (xsd) schema <xs:complexContent/> node. 315 """ 316
317 - def childtags(self):
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
338 - def childtags(self):
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
353 - def mixed(self):
354 return len(self)
355
356 357 -class Enumeration(Content):
358 """ 359 Represents an (xsd) schema <xs:enumeration/> node 360 """ 361
362 - def __init__(self, schema, root):
363 Content.__init__(self, schema, root) 364 self.name = root.get('value')
365
366 - def enum(self):
367 return True
368
369 370 -class Element(TypedContent):
371 """ 372 Represents an (xsd) schema <xs:element/> node. 373 """ 374
375 - def __init__(self, schema, root):
376 TypedContent.__init__(self, schema, root) 377 a = root.get('form') 378 if a is not None: 379 self.form_qualified = ( a == 'qualified' ) 380 a = self.root.get('nillable') 381 if a is not None: 382 self.nillable = ( a in ('1', 'true') ) 383 self.implany()
384
385 - def implany(self):
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
399 - def childtags(self):
400 return ('attribute', 'simpleType', 'complexType', 'any',)
401
402 - def extension(self):
403 for c in self.rawchildren: 404 if c.extension(): 405 return True 406 return False
407
408 - def restriction(self):
409 for c in self.rawchildren: 410 if c.restriction(): 411 return True 412 return False
413
414 - def dependencies(self):
415 deps = [] 416 midx = None 417 if self.ref is not None: 418 query = ElementQuery(self.ref) 419 e = query.execute(self.schema) 420 if e is None: 421 log.debug(self.schema) 422 raise TypeNotFound(self.ref) 423 deps.append(e) 424 midx = 0 425 return (midx, deps)
426
427 - def merge(self, other):
428 SchemaObject.merge(self, other) 429 self.rawchildren = other.rawchildren
430
431 - def description(self):
432 return ('name', 'ref', 'type')
433
434 - def anytype(self):
435 """ create an xsd:anyType reference """ 436 p,u = Namespace.xsdns 437 mp = self.root.findPrefix(u) 438 if mp is None: 439 mp = p 440 self.root.addPrefix(p, u) 441 return ':'.join((mp, 'anyType'))
442
443 444 -class Extension(SchemaObject):
445 """ 446 Represents an (xsd) schema <xs:extension/> node. 447 """ 448
449 - def __init__(self, schema, root):
450 SchemaObject.__init__(self, schema, root) 451 self.ref = root.get('base')
452
453 - def childtags(self):
454 return ('attribute', 455 'attributeGroup', 456 'sequence', 457 'all', 458 'choice', 459 'group')
460
461 - def dependencies(self):
462 deps = [] 463 midx = None 464 if self.ref is not None: 465 query = TypeQuery(self.ref) 466 super = query.execute(self.schema) 467 if super is None: 468 log.debug(self.schema) 469 raise TypeNotFound(self.ref) 470 if not super.builtin(): 471 deps.append(super) 472 midx = 0 473 return (midx, deps)
474
475 - def merge(self, other):
476 SchemaObject.merge(self, other) 477 filter = Filter(False, self.rawchildren) 478 self.prepend(self.rawchildren, other.rawchildren, filter)
479
480 - def extension(self):
481 return ( self.ref is not None )
482
483 - def description(self):
484 return ('ref',)
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
517 - def __init__(self, schema, root):
518 SchemaObject.__init__(self, schema, root) 519 self.ns = (None, root.get('namespace')) 520 self.location = root.get('schemaLocation') 521 if self.location is None: 522 self.location = self.locations.get(self.ns[1]) 523 self.opened = False
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
546 - def locate(self):
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
553 - def download(self, options):
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
569 - def description(self):
570 return ('ns', 'location')
571
572 573 -class Include(SchemaObject):
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
584 - def __init__(self, schema, root):
585 SchemaObject.__init__(self, schema, root) 586 self.location = root.get('schemaLocation') 587 if self.location is None: 588 self.location = self.locations.get(self.ns[1]) 589 self.opened = False
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
607 - def download(self, options):
608 """ download the schema """ 609 url = self.location 610 try: 611 if '://' not in url: 612 url = urljoin(self.schema.baseurl, url) 613 reader = DocumentReader(options) 614 d = reader.open(url) 615 root = d.root() 616 root.set('url', url) 617 self.__applytns(root) 618 return self.schema.instance(root, url, options) 619 except TransportError: 620 msg = 'include schema at (%s), failed' % url 621 log.error('%s, %s', self.id, msg, exc_info=True) 622 raise Exception(msg)
623
624 - def __applytns(self, root):
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
636 - def description(self):
637 return ('location')
638
639 640 -class Attribute(TypedContent):
641 """ 642 Represents an (xsd) <attribute/> node 643 """ 644
645 - def __init__(self, schema, root):
646 TypedContent.__init__(self, schema, root) 647 self.use = root.get('use', default='')
648
649 - def childtags(self):
650 return ('restriction',)
651
652 - def isattr(self):
653 return True
654
655 - def get_default(self):
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
663 - def optional(self):
664 return ( self.use != 'required' )
665
666 - def dependencies(self):
667 deps = [] 668 midx = None 669 if self.ref is not None: 670 query = AttrQuery(self.ref) 671 a = query.execute(self.schema) 672 if a is None: 673 log.debug(self.schema) 674 raise TypeNotFound(self.ref) 675 deps.append(a) 676 midx = 0 677 return (midx, deps)
678
679 - def description(self):
680 return ('name', 'ref', 'type')
681
682 683 -class Any(Content):
684 """ 685 Represents an (xsd) <any/> node 686 """ 687
688 - def get_child(self, name):
689 root = self.root.clone() 690 root.set('note', 'synthesized (any) child') 691 child = Any(self.schema, root) 692 return (child, [])
693
694 - def get_attribute(self, name):
695 root = self.root.clone() 696 root.set('note', 'synthesized (any) attribute') 697 attribute = Any(self.schema, root) 698 return (attribute, [])
699
700 - def any(self):
701 return True
702
703 704 -class Factory:
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
733 - def maptag(cls, tag, fn):
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=('*',)):
762 """ 763 Build an xsobject representation. 764 @param root: An schema XML root. 765 @type root: L{sax.element.Element} 766 @param filter: A tag filter. 767 @type filter: [str,...] 768 @return: A schema object graph. 769 @rtype: L{sxbase.SchemaObject} 770 """ 771 children = [] 772 for node in root.getChildren(ns=Namespace.xsdns): 773 if '*' in filter or node.name in filter: 774 child = cls.create(node, schema) 775 if child is None: 776 continue 777 children.append(child) 778 c = cls.build(node, schema, child.childtags()) 779 child.rawchildren = c 780 return children
781 782 @classmethod
783 - def collate(cls, children):
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 # Static Import Bindings :-( 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