1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
33 """
34 An I{abstract} schema-type resolver.
35 @ivar schema: A schema object.
36 @type schema: L{xsd.schema.Schema}
37 """
38
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
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
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):
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
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
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
219
220
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
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
240 """
241 Reset the resolver's state.
242 """
243 self.stack = Stack()
244
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
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
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
287 """
288 Get the current stack depth.
289 @return: The current stack depth.
290 @rtype: int
291 """
292 return len(self.stack)
293
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
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
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):
377
386
387
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
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
450
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
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
478 return '%s\n%s\n%s' % \
479 (Repr(self.type),
480 Repr(self.resolved),
481 [Repr(t) for t in self.ancestry])
482
485 if name == 'ancestry':
486 return ()
487 else:
488 return None
489
490
497