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

Source Code for Module suds.xsd.schema

  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{schema} module provides a intelligent representation of 
 19  an XSD schema.  The I{raw} model is the XML tree and the I{model} 
 20  is the denormalized, objectified and intelligent view of the schema. 
 21  Most of the I{value-add} provided by the model is centered around 
 22  tranparent referenced type resolution and targeted denormalization. 
 23  """ 
 24   
 25   
 26  import suds.metrics 
 27  from suds import * 
 28  from suds.xsd import * 
 29  from suds.xsd.sxbuiltin import * 
 30  from suds.xsd.sxbasic import Factory as BasicFactory 
 31  from suds.xsd.sxbuiltin import Factory as BuiltinFactory 
 32  from suds.xsd.sxbase import SchemaObject 
 33  from suds.xsd.deplist import DepList 
 34  from suds.sax.element import Element 
 35  from suds.sax import splitPrefix, Namespace 
 36  from logging import getLogger 
 37   
 38  log = getLogger(__name__) 
 39   
 40   
41 -class SchemaCollection:
42 """ 43 A collection of schema objects. This class is needed because WSDLs 44 may contain more then one <schema/> node. 45 @ivar wsdl: A wsdl object. 46 @type wsdl: L{suds.wsdl.Definitions} 47 @ivar children: A list contained schemas. 48 @type children: [L{Schema},...] 49 @ivar namespaces: A dictionary of contained schemas by namespace. 50 @type namespaces: {str:L{Schema}} 51 """ 52
53 - def __init__(self, wsdl):
54 """ 55 @param wsdl: A wsdl object. 56 @type wsdl: L{suds.wsdl.Definitions} 57 """ 58 self.wsdl = wsdl 59 self.children = [] 60 self.namespaces = {}
61
62 - def add(self, schema):
63 """ 64 Add a schema node to the collection. Schema(s) within the same target 65 namespace are consolidated. 66 @param schema: A schema object. 67 @type schema: (L{Schema}) 68 """ 69 key = schema.tns[1] 70 existing = self.namespaces.get(key) 71 if existing is None: 72 self.children.append(schema) 73 self.namespaces[key] = schema 74 else: 75 existing.root.children += schema.root.children 76 existing.root.nsprefixes.update(schema.root.nsprefixes)
77
78 - def load(self, options):
79 """ 80 Load the schema objects for the root nodes. 81 - de-references schemas 82 - merge schemas 83 @param options: An options dictionary. 84 @type options: L{options.Options} 85 @return: The merged schema. 86 @rtype: L{Schema} 87 """ 88 if options.autoblend: 89 self.autoblend() 90 for child in self.children: 91 child.build() 92 for child in self.children: 93 child.open_imports(options) 94 for child in self.children: 95 child.dereference() 96 log.debug('loaded:\n%s', self) 97 merged = self.merge() 98 log.debug('MERGED:\n%s', merged) 99 return merged
100
101 - def autoblend(self):
102 """ 103 Ensure that all schemas within the collection 104 import each other which has a blending effect. 105 @return: self 106 @rtype: L{SchemaCollection} 107 """ 108 namespaces = self.namespaces.keys() 109 for s in self.children: 110 for ns in namespaces: 111 tns = s.root.get('targetNamespace') 112 if tns == ns: 113 continue 114 for imp in s.root.getChildren('import'): 115 if imp.get('namespace') == ns: 116 continue 117 imp = Element('import', ns=Namespace.xsdns) 118 imp.set('namespace', ns) 119 s.root.append(imp) 120 return self
121
122 - def locate(self, ns):
123 """ 124 Find a schema by namespace. Only the URI portion of 125 the namespace is compared to each schema's I{targetNamespace} 126 @param ns: A namespace. 127 @type ns: (prefix,URI) 128 @return: The schema matching the namesapce, else None. 129 @rtype: L{Schema} 130 """ 131 return self.namespaces.get(ns[1])
132
133 - def merge(self):
134 """ 135 Merge the contained schemas into one. 136 @return: The merged schema. 137 @rtype: L{Schema} 138 """ 139 if len(self): 140 schema = self.children[0] 141 for s in self.children[1:]: 142 schema.merge(s) 143 return schema 144 else: 145 return None
146
147 - def __len__(self):
148 return len(self.children)
149
150 - def __str__(self):
151 return unicode(self).encode('utf-8')
152
153 - def __unicode__(self):
154 result = ['\nschema collection'] 155 for s in self.children: 156 result.append(s.str(1)) 157 return '\n'.join(result)
158 159
160 -class Schema:
161 """ 162 The schema is an objectification of a <schema/> (xsd) definition. 163 It provides inspection, lookup and type resolution. 164 @ivar root: The root node. 165 @type root: L{sax.element.Element} 166 @ivar baseurl: The I{base} URL for this schema. 167 @type baseurl: str 168 @ivar container: A schema collection containing this schema. 169 @type container: L{SchemaCollection} 170 @ivar children: A list of direct top level children. 171 @type children: [L{SchemaObject},...] 172 @ivar all: A list of all (includes imported) top level children. 173 @type all: [L{SchemaObject},...] 174 @ivar types: A schema types cache. 175 @type types: {name:L{SchemaObject}} 176 @ivar imports: A list of import objects. 177 @type imports: [L{SchemaObject},...] 178 @ivar elements: A list of <element/> objects. 179 @type elements: [L{SchemaObject},...] 180 @ivar attributes: A list of <attribute/> objects. 181 @type attributes: [L{SchemaObject},...] 182 @ivar groups: A list of group objects. 183 @type groups: [L{SchemaObject},...] 184 @ivar agrps: A list of attribute group objects. 185 @type agrps: [L{SchemaObject},...] 186 @ivar form_qualified: The flag indicating: 187 (@elementFormDefault). 188 @type form_qualified: bool 189 """ 190 191 Tag = 'schema' 192
193 - def __init__(self, root, baseurl, options, container=None):
194 """ 195 @param root: The xml root. 196 @type root: L{sax.element.Element} 197 @param baseurl: The base url used for importing. 198 @type baseurl: basestring 199 @param options: An options dictionary. 200 @type options: L{options.Options} 201 @param container: An optional container. 202 @type container: L{SchemaCollection} 203 """ 204 self.root = root 205 self.id = objid(self) 206 self.tns = self.mktns() 207 self.baseurl = baseurl 208 self.container = container 209 self.children = [] 210 self.all = [] 211 self.types = {} 212 self.imports = [] 213 self.elements = {} 214 self.attributes = {} 215 self.groups = {} 216 self.agrps = {} 217 if options.doctor is not None: 218 options.doctor.examine(root) 219 form = self.root.get('elementFormDefault') 220 if form is None: 221 self.form_qualified = False 222 else: 223 self.form_qualified = ( form == 'qualified' ) 224 if container is None: 225 self.build() 226 self.open_imports(options) 227 log.debug('built:\n%s', self) 228 self.dereference() 229 log.debug('dereferenced:\n%s', self)
230
231 - def mktns(self):
232 """ 233 Make the schema's target namespace. 234 @return: The namespace representation of the schema's 235 targetNamespace value. 236 @rtype: (prefix, uri) 237 """ 238 tns = [None, self.root.get('targetNamespace')] 239 if tns[1] is not None: 240 tns[0] = self.root.findPrefix(tns[1]) 241 return tuple(tns)
242
243 - def build(self):
244 """ 245 Build the schema (object graph) using the root node 246 using the factory. 247 - Build the graph. 248 - Collate the children. 249 """ 250 self.children = BasicFactory.build(self.root, self) 251 collated = BasicFactory.collate(self.children) 252 self.children = collated[0] 253 self.attributes = collated[2] 254 self.imports = collated[1] 255 self.elements = collated[3] 256 self.types = collated[4] 257 self.groups = collated[5] 258 self.agrps = collated[6]
259
260 - def merge(self, schema):
261 """ 262 Merge the contents from the schema. Only objects not already contained 263 in this schema's collections are merged. This is to provide for bidirectional 264 import which produce cyclic includes. 265 @returns: self 266 @rtype: L{Schema} 267 """ 268 for item in schema.attributes.items(): 269 if item[0] in self.attributes: 270 continue 271 self.all.append(item[1]) 272 self.attributes[item[0]] = item[1] 273 for item in schema.elements.items(): 274 if item[0] in self.elements: 275 continue 276 self.all.append(item[1]) 277 self.elements[item[0]] = item[1] 278 for item in schema.types.items(): 279 if item[0] in self.types: 280 continue 281 self.all.append(item[1]) 282 self.types[item[0]] = item[1] 283 for item in schema.groups.items(): 284 if item[0] in self.groups: 285 continue 286 self.all.append(item[1]) 287 self.groups[item[0]] = item[1] 288 for item in schema.agrps.items(): 289 if item[0] in self.agrps: 290 continue 291 self.all.append(item[1]) 292 self.agrps[item[0]] = item[1] 293 schema.merged = True 294 return self
295
296 - def open_imports(self, options):
297 """ 298 Instruct all contained L{sxbasic.Import} children to import 299 the schema's which they reference. The contents of the 300 imported schema are I{merged} in. 301 @param options: An options dictionary. 302 @type options: L{options.Options} 303 """ 304 for imp in self.imports: 305 imported = imp.open(options) 306 if imported is None: 307 continue 308 imported.open_imports(options) 309 log.debug('imported:\n%s', imported) 310 self.merge(imported)
311
312 - def dereference(self):
313 """ 314 Instruct all children to perform dereferencing. 315 """ 316 all = [] 317 indexes = {} 318 for child in self.children: 319 child.content(all) 320 deplist = DepList() 321 for x in all: 322 x.qualify() 323 midx, deps = x.dependencies() 324 item = (x, tuple(deps)) 325 deplist.add(item) 326 indexes[x] = midx 327 for x, deps in deplist.sort(): 328 midx = indexes.get(x) 329 if midx is None: continue 330 d = deps[midx] 331 log.debug('(%s) merging %s <== %s', self.tns[1], Repr(x), Repr(d)) 332 x.merge(d)
333
334 - def locate(self, ns):
335 """ 336 Find a schema by namespace. Only the URI portion of 337 the namespace is compared to each schema's I{targetNamespace}. 338 The request is passed to the container. 339 @param ns: A namespace. 340 @type ns: (prefix,URI) 341 @return: The schema matching the namesapce, else None. 342 @rtype: L{Schema} 343 """ 344 if self.container is not None: 345 return self.container.locate(ns) 346 else: 347 return None
348
349 - def custom(self, ref, context=None):
350 """ 351 Get whether the specified reference is B{not} an (xs) builtin. 352 @param ref: A str or qref. 353 @type ref: (str|qref) 354 @return: True if B{not} a builtin, else False. 355 @rtype: bool 356 """ 357 if ref is None: 358 return True 359 else: 360 return ( not self.builtin(ref, context) )
361
362 - def builtin(self, ref, context=None):
363 """ 364 Get whether the specified reference is an (xs) builtin. 365 @param ref: A str or qref. 366 @type ref: (str|qref) 367 @return: True if builtin, else False. 368 @rtype: bool 369 """ 370 w3 = 'http://www.w3.org' 371 try: 372 if isqref(ref): 373 ns = ref[1] 374 return ( ref[0] in Factory.tags and ns.startswith(w3) ) 375 if context is None: 376 context = self.root 377 prefix = splitPrefix(ref)[0] 378 prefixes = context.findPrefixes(w3, 'startswith') 379 return ( prefix in prefixes and ref[0] in Factory.tags ) 380 except: 381 return False
382
383 - def instance(self, root, baseurl, options):
384 """ 385 Create and return an new schema object using the 386 specified I{root} and I{url}. 387 @param root: A schema root node. 388 @type root: L{sax.element.Element} 389 @param baseurl: A base URL. 390 @type baseurl: str 391 @param options: An options dictionary. 392 @type options: L{options.Options} 393 @return: The newly created schema object. 394 @rtype: L{Schema} 395 @note: This is only used by Import children. 396 """ 397 return Schema(root, baseurl, options)
398
399 - def str(self, indent=0):
400 tab = '%*s'%(indent*3, '') 401 result = [] 402 result.append('%s%s' % (tab, self.id)) 403 result.append('%s(raw)' % tab) 404 result.append(self.root.str(indent+1)) 405 result.append('%s(model)' % tab) 406 for c in self.children: 407 result.append(c.str(indent+1)) 408 result.append('') 409 return '\n'.join(result)
410
411 - def __repr__(self):
412 myrep = '<%s tns="%s"/>' % (self.id, self.tns[1]) 413 return myrep.encode('utf-8')
414
415 - def __str__(self):
416 return unicode(self).encode('utf-8')
417
418 - def __unicode__(self):
419 return self.str()
420