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