fix broken recursive WSDL imports

Suds now correctly handles twisted use-cases as seen in some M$ web services,
and whose one possible structure has been illustrated by the
test_recursive_WSDL_import() test in the test_client.py test module.

Note that this has nothing to do with recursive XSD schema imports which still
have known issues.

Updated todo list.
This commit is contained in:
Jurko Gospodnetić 2015-06-29 21:31:38 +02:00
parent 122540ec8b
commit ab2b7a4c5c
3 changed files with 46 additions and 12 deletions

View File

@ -1496,7 +1496,13 @@ PRIORITIZED:
(29.06.2015.)
* (Jurko) Fix recursive WSDL import issue.
(+) * (Jurko) Fix recursive WSDL import issue.
(+) * Fix issue.
(30.06.2015.)
* (Jurko) Fix recursive XSD import issue.
* Add tests.
* Fix issue.
* (Jurko) Review and commit unpublished cleanup work on Jurko's machine.

View File

@ -65,11 +65,28 @@ class WObject(Object):
pmd.excludes = ["root"]
pmd.wrappers = dict(qname=repr)
self.__metadata__.__print__ = pmd
self.__resolved = False
def resolve(self, definitions):
"""
Resolve named references to other WSDL objects.
Can be safely called multiple times.
@param definitions: A definitions object.
@type definitions: L{Definitions}
"""
if not self.__resolved:
self.do_resolve(definitions)
self.__resolved = True
def do_resolve(self, definitions):
"""
Internal worker resolving named references to other WSDL objects.
May only be called once per instance.
@param definitions: A definitions object.
@type definitions: L{Definitions}
@ -134,7 +151,7 @@ class Definitions(WObject):
Tag = "definitions"
def __init__(self, url, options):
def __init__(self, url, options, imported_definitions=None):
"""
@param url: A URL to the WSDL.
@type url: str
@ -165,7 +182,10 @@ class Definitions(WObject):
pmd.excludes.append("children")
pmd.excludes.append("wsdl")
pmd.wrappers["schema"] = repr
self.open_imports()
if imported_definitions is None:
imported_definitions = {}
imported_definitions[url] = self
self.open_imports(imported_definitions)
self.resolve()
self.build_schema()
self.set_wrapped()
@ -207,10 +227,10 @@ class Definitions(WObject):
self.services.append(child)
continue
def open_imports(self):
def open_imports(self, imported_definitions):
"""Import the I{imported} WSDLs."""
for imp in self.imports:
imp.load(self)
imp.load(self, imported_definitions)
def resolve(self):
"""Tell all children to resolve themselves."""
@ -319,14 +339,15 @@ class Import(WObject):
pmd = self.__metadata__.__print__
pmd.wrappers["imported"] = repr
def load(self, definitions):
def load(self, definitions, imported_definitions):
"""Load the object by opening the URL."""
url = self.location
log.debug("importing (%s)", url)
if "://" not in url:
url = urljoin(definitions.url, url)
options = definitions.options
d = Definitions(url, options)
d = imported_definitions.get(url)
if not d:
d = Definitions(url, definitions.options, imported_definitions)
if d.root.match(Definitions.Tag, wsdlns):
self.import_definitions(definitions, d)
return
@ -495,7 +516,7 @@ class PortType(NamedObject):
op.faults = faults
self.operations[op.name] = op
def resolve(self, definitions):
def do_resolve(self, definitions):
"""
Resolve named references to other WSDL objects.
@ -668,7 +689,7 @@ class Binding(NamedObject):
if part is not None:
header.part = part
def resolve(self, definitions):
def do_resolve(self, definitions):
"""
Resolve named references to other WSDL objects. This includes
cross-linking information (from) the portType (to) the I{SOAP} protocol
@ -696,6 +717,14 @@ class Binding(NamedObject):
port_type = definitions.port_types.get(ref)
if port_type is None:
raise Exception("portType '%s', not-found" % (self.type,))
# Later on we will require access to the message data referenced by
# this port_type instance, and in order for those data references to be
# available, port_type first needs to dereference its message
# identification string. The only scenario where the port_type could
# possibly not have already resolved its references, and where this
# explicit resolve() call is required, is if we are dealing with a
# recursive WSDL import chain.
port_type.resolve(definitions)
self.type = port_type
def resolvesoapbody(self, definitions, op):
@ -903,7 +932,7 @@ class Service(NamedObject):
if names is None or m.name in names:
m.location = url
def resolve(self, definitions):
def do_resolve(self, definitions):
"""
Resolve named references to other WSDL objects. Ports without SOAP
bindings are discarded.

View File

@ -819,7 +819,6 @@ class TestWSDLImportWithDifferentTargetNamespace:
input_element = input_part.element
assert input_element == ('Lollypop', 'xsd-ns')
@pytest.mark.xfail
def test_recursive_WSDL_import(self):
"""
Recursive WSDL imports should be supported.