Package suds :: Module resolver
[hide private]
[frames] | no frames]

Source Code for Module suds.resolver

  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{resolver} module provides a collection of classes that 
 19  provide wsdl/xsd named type resolution. 
 20  """ 
 21   
 22  import re 
 23  from logging import getLogger 
 24  from suds import * 
 25  from suds.sax import splitPrefix, Namespace 
 26  from suds.sudsobject import Object 
 27  from suds.xsd.query import BlindQuery, TypeQuery, qualify 
 28   
 29  log = getLogger(__name__) 
 30   
 31   
32 -class Resolver:
33 """ 34 An I{abstract} schema-type resolver. 35 @ivar schema: A schema object. 36 @type schema: L{xsd.schema.Schema} 37 """ 38
39 - def __init__(self, schema):
40 """ 41 @param schema: A schema object. 42 @type schema: L{xsd.schema.Schema} 43 """ 44 self.schema = schema
45
46 - def find(self, name, resolved=True):
47 """ 48 Get the definition object for the schema object by name. 49 @param name: The name of a schema object. 50 @type name: basestring 51 @param resolved: A flag indicating that the fully resolved type 52 should be returned. 53 @type resolved: boolean 54 @return: The found schema I{type} 55 @rtype: L{xsd.sxbase.SchemaObject} 56 """ 57 log.debug('searching schema for (%s)', name) 58 qref = qualify(name, self.schema.root, self.schema.tns) 59 query = BlindQuery(qref) 60 result = query.execute(self.schema) 61 if result is None: 62 log.error('(%s) not-found', name) 63 return None 64 log.debug('found (%s) as (%s)', name, Repr(result)) 65 if resolved: 66 result = result.resolve() 67 return result
68 69
70 -class PathResolver(Resolver):
71 """ 72 Resolveds the definition object for the schema type located at the specified path. 73 The path may contain (.) dot notation to specify nested types. 74 @ivar wsdl: A wsdl object. 75 @type wsdl: L{wsdl.Definitions} 76 """ 77
78 - def __init__(self, wsdl, ps='.'):
79 """ 80 @param wsdl: A schema object. 81 @type wsdl: L{wsdl.Definitions} 82 @param ps: The path separator character 83 @type ps: char 84 """ 85 Resolver.__init__(self, wsdl.schema) 86 self.wsdl = wsdl 87 self.altp = re.compile('({)(.+)(})(.+)') 88 self.splitp = re.compile('({.+})*[^\%s]+' % ps[0])
89
90 - def find(self, path, resolved=True):
91 """ 92 Get the definition object for the schema type located at the specified path. 93 The path may contain (.) dot notation to specify nested types. 94 Actually, the path separator is usually a (.) but can be redefined 95 during contruction. 96 @param path: A (.) separated path to a schema type. 97 @type path: basestring 98 @param resolved: A flag indicating that the fully resolved type 99 should be returned. 100 @type resolved: boolean 101 @return: The found schema I{type} 102 @rtype: L{xsd.sxbase.SchemaObject} 103 """ 104 result = None 105 parts = self.split(path) 106 try: 107 result = self.root(parts) 108 if len(parts) > 1: 109 result = result.resolve(nobuiltin=True) 110 result = self.branch(result, parts) 111 result = self.leaf(result, parts) 112 if resolved: 113 result = result.resolve(nobuiltin=True) 114 except PathResolver.BadPath: 115 log.error('path: "%s", not-found' % path) 116 return result
117
118 - def root(self, parts):
119 """ 120 Find the path root. 121 @param parts: A list of path parts. 122 @type parts: [str,..] 123 @return: The root. 124 @rtype: L{xsd.sxbase.SchemaObject} 125 """ 126 result = None 127 name = parts[0] 128 log.debug('searching schema for (%s)', name) 129 qref = self.qualify(parts[0]) 130 query = BlindQuery(qref) 131 result = query.execute(self.schema) 132 if result is None: 133 log.error('(%s) not-found', name) 134 raise PathResolver.BadPath(name) 135 else: 136 log.debug('found (%s) as (%s)', name, Repr(result)) 137 return result
138
139 - def branch(self, root, parts):
140 """ 141 Traverse the path until the leaf is reached. 142 @param parts: A list of path parts. 143 @type parts: [str,..] 144 @param root: The root. 145 @type root: L{xsd.sxbase.SchemaObject} 146 @return: The end of the branch. 147 @rtype: L{xsd.sxbase.SchemaObject} 148 """ 149 result = root 150 for part in parts[1:-1]: 151 name = splitPrefix(part)[1] 152 log.debug('searching parent (%s) for (%s)', Repr(result), name) 153 result, ancestry = result.get_child(name) 154 if result is None: 155 log.error('(%s) not-found', name) 156 raise PathResolver.BadPath(name) 157 else: 158 result = result.resolve(nobuiltin=True) 159 log.debug('found (%s) as (%s)', name, Repr(result)) 160 return result
161
162 - def leaf(self, parent, parts):
163 """ 164 Find the leaf. 165 @param parts: A list of path parts. 166 @type parts: [str,..] 167 @param parent: The leaf's parent. 168 @type parent: L{xsd.sxbase.SchemaObject} 169 @return: The leaf. 170 @rtype: L{xsd.sxbase.SchemaObject} 171 """ 172 name = splitPrefix(parts[-1])[1] 173 if name.startswith('@'): 174 result, path = parent.get_attribute(name[1:]) 175 else: 176 result, ancestry = parent.get_child(name) 177 if result is None: 178 raise PathResolver.BadPath(name) 179 return result
180
181 - def qualify(self, name):
182 """ 183 Qualify the name as either: 184 - plain name 185 - ns prefixed name (eg: ns0:Person) 186 - fully ns qualified name (eg: {http://myns-uri}Person) 187 @param name: The name of an object in the schema. 188 @type name: str 189 @return: A qualifed name. 190 @rtype: qname 191 """ 192 m = self.altp.match(name) 193 if m is None: 194 return qualify(name, self.wsdl.root, self.wsdl.tns) 195 else: 196 return (m.group(4), m.group(2))
197
198 - def split(self, s):
199 """ 200 Split the string on (.) while preserving any (.) inside the 201 '{}' alternalte syntax for full ns qualification. 202 @param s: A plain or qualifed name. 203 @type s: str 204 @return: A list of the name's parts. 205 @rtype: [str,..] 206 """ 207 parts = [] 208 b = 0 209 while 1: 210 m = self.splitp.match(s, b) 211 if m is None: 212 break 213 b,e = m.span() 214 parts.append(s[b:e]) 215 b = e+1 216 return parts
217
218 - class BadPath(Exception): pass
219 220
221 -class TreeResolver(Resolver):
222 """ 223 The tree resolver is a I{stateful} tree resolver 224 used to resolve each node in a tree. As such, it mirrors 225 the tree structure to ensure that nodes are resolved in 226 context. 227 @ivar stack: The context stack. 228 @type stack: list 229 """ 230
231 - def __init__(self, schema):
232 """ 233 @param schema: A schema object. 234 @type schema: L{xsd.schema.Schema} 235 """ 236 Resolver.__init__(self, schema) 237 self.stack = Stack()
238
239 - def reset(self):
240 """ 241 Reset the resolver's state. 242 """ 243 self.stack = Stack()
244
245 - def push(self, x):
246 """ 247 Push an I{object} onto the stack. 248 @param x: An object to push. 249 @type x: L{Frame} 250 @return: The pushed frame. 251 @rtype: L{Frame} 252 """ 253 if isinstance(x, Frame): 254 frame = x 255 else: 256 frame = Frame(x) 257 self.stack.append(frame) 258 log.debug('push: (%s)\n%s', Repr(frame), Repr(self.stack)) 259 return frame
260
261 - def top(self):
262 """ 263 Get the I{frame} at the top of the stack. 264 @return: The top I{frame}, else None. 265 @rtype: L{Frame} 266 """ 267 if len(self.stack): 268 return self.stack[-1] 269 else: 270 return Frame.Empty()
271
272 - def pop(self):
273 """ 274 Pop the frame at the top of the stack. 275 @return: The popped frame, else None. 276 @rtype: L{Frame} 277 """ 278 if len(self.stack): 279 popped = self.stack.pop() 280 log.debug('pop: (%s)\n%s', Repr(popped), Repr(self.stack)) 281 return popped 282 else: 283 log.debug('stack empty, not-popped') 284 return None
285
286 - def depth(self):
287 """ 288 Get the current stack depth. 289 @return: The current stack depth. 290 @rtype: int 291 """ 292 return len(self.stack)
293
294 - def getchild(self, name, parent):
295 """ get a child by name """ 296 log.debug('searching parent (%s) for (%s)', Repr(parent), name) 297 if name.startswith('@'): 298 return parent.get_attribute(name[1:]) 299 else: 300 return parent.get_child(name)
301 302
303 -class NodeResolver(TreeResolver):
304 """ 305 The node resolver is a I{stateful} XML document resolver 306 used to resolve each node in a tree. As such, it mirrors 307 the tree structure to ensure that nodes are resolved in 308 context. 309 """ 310
311 - def __init__(self, schema):
312 """ 313 @param schema: A schema object. 314 @type schema: L{xsd.schema.Schema} 315 """ 316 TreeResolver.__init__(self, schema)
317
318 - def find(self, node, resolved=False, push=True):
319 """ 320 @param node: An xml node to be resolved. 321 @type node: L{sax.element.Element} 322 @param resolved: A flag indicating that the fully resolved type should be 323 returned. 324 @type resolved: boolean 325 @param push: Indicates that the resolved type should be 326 pushed onto the stack. 327 @type push: boolean 328 @return: The found schema I{type} 329 @rtype: L{xsd.sxbase.SchemaObject} 330 """ 331 name = node.name 332 parent = self.top().resolved 333 if parent is None: 334 result, ancestry = self.query(name, node) 335 else: 336 result, ancestry = self.getchild(name, parent) 337 known = self.known(node) 338 if result is None: 339 return result 340 if push: 341 frame = Frame(result, resolved=known, ancestry=ancestry) 342 pushed = self.push(frame) 343 if resolved: 344 result = result.resolve() 345 return result
346
347 - def findattr(self, name, resolved=True):
348 """ 349 Find an attribute type definition. 350 @param name: An attribute name. 351 @type name: basestring 352 @param resolved: A flag indicating that the fully resolved type should be 353 returned. 354 @type resolved: boolean 355 @return: The found schema I{type} 356 @rtype: L{xsd.sxbase.SchemaObject} 357 """ 358 name = '@%s'%name 359 parent = self.top().resolved 360 if parent is None: 361 result, ancestry = self.query(name, node) 362 else: 363 result, ancestry = self.getchild(name, parent) 364 if result is None: 365 return result 366 if resolved: 367 result = result.resolve() 368 return result
369
370 - def query(self, name, node):
371 """ blindly query the schema by name """ 372 log.debug('searching schema for (%s)', name) 373 qref = qualify(name, node, node.namespace()) 374 query = BlindQuery(qref) 375 result = query.execute(self.schema) 376 return (result, [])
377
378 - def known(self, node):
379 """ resolve type referenced by @xsi:type """ 380 ref = node.get('type', Namespace.xsins) 381 if ref is None: 382 return None 383 qref = qualify(ref, node, node.namespace()) 384 query = BlindQuery(qref) 385 return query.execute(self.schema)
386 387
388 -class GraphResolver(TreeResolver):
389 """ 390 The graph resolver is a I{stateful} L{Object} graph resolver 391 used to resolve each node in a tree. As such, it mirrors 392 the tree structure to ensure that nodes are resolved in 393 context. 394 """ 395
396 - def __init__(self, schema):
397 """ 398 @param schema: A schema object. 399 @type schema: L{xsd.schema.Schema} 400 """ 401 TreeResolver.__init__(self, schema)
402
403 - def find(self, name, object, resolved=False, push=True):
404 """ 405 @param name: The name of the object to be resolved. 406 @type name: basestring 407 @param object: The name's value. 408 @type object: (any|L{Object}) 409 @param resolved: A flag indicating that the fully resolved type 410 should be returned. 411 @type resolved: boolean 412 @param push: Indicates that the resolved type should be 413 pushed onto the stack. 414 @type push: boolean 415 @return: The found schema I{type} 416 @rtype: L{xsd.sxbase.SchemaObject} 417 """ 418 known = None 419 parent = self.top().resolved 420 if parent is None: 421 result, ancestry = self.query(name) 422 else: 423 result, ancestry = self.getchild(name, parent) 424 if result is None: 425 return None 426 if isinstance(object, Object): 427 known = self.known(object) 428 if push: 429 frame = Frame(result, resolved=known, ancestry=ancestry) 430 pushed = self.push(frame) 431 if resolved: 432 if known is None: 433 result = result.resolve() 434 else: 435 result = known 436 return result
437
438 - def query(self, name):
439 """ blindly query the schema by name """ 440 log.debug('searching schema for (%s)', name) 441 schema = self.schema 442 wsdl = self.wsdl() 443 if wsdl is None: 444 qref = qualify(name, schema.root, schema.tns) 445 else: 446 qref = qualify(name, wsdl.root, wsdl.tns) 447 query = BlindQuery(qref) 448 result = query.execute(schema) 449 return (result, [])
450
451 - def wsdl(self):
452 """ get the wsdl """ 453 container = self.schema.container 454 if container is None: 455 return None 456 else: 457 return container.wsdl
458
459 - def known(self, object):
460 """ get the type specified in the object's metadata """ 461 try: 462 md = object.__metadata__ 463 known = md.sxtype 464 return known 465 except: 466 pass
467 468
469 -class Frame:
470 - def __init__(self, type, resolved=None, ancestry=()):
471 self.type = type 472 if resolved is None: 473 resolved = type.resolve() 474 self.resolved = resolved.resolve() 475 self.ancestry = ancestry
476
477 - def __str__(self):
478 return '%s\n%s\n%s' % \ 479 (Repr(self.type), 480 Repr(self.resolved), 481 [Repr(t) for t in self.ancestry])
482
483 - class Empty:
484 - def __getattr__(self, name):
485 if name == 'ancestry': 486 return () 487 else: 488 return None
489 490
491 -class Stack(list):
492 - def __repr__(self):
493 result = [] 494 for item in self: 495 result.append(repr(item)) 496 return '\n'.join(result)
497