Python-zeep: Error with WSDL on version 3.3.0

Created on 8 Mar 2019  路  4Comments  路  Source: mvantellingen/python-zeep

Experiencing an error with the latest release 3.3.0

Here are the steps to reproduce:

from zeep import Client, Settings

settings = Settings(strict=False, xml_huge_tree=True)
client = Client('https://adwords.google.com/api/adwords/mcm/v201809/ManagedCustomerService?wsdl', settings=settings)

'NoneType' object has no attribute 'find'

Full Traceback:

AttributeError                            Traceback (most recent call last)
<command-6287780> in <module>()
      2 
      3 settings = Settings(strict=False, xml_huge_tree=True)
----> 4 client = Client('https://adwords.google.com/api/adwords/mcm/v201809/ManagedCustomerService?wsdl', settings=settings)

/local_disk0/pythonVirtualEnvDirs/virtualEnv-ed69fce1-1e72-47e6-8036-d7e99c9b2a4b/local/lib/python2.7/site-packages/zeep/client.pyc in __init__(self, wsdl, wsse, transport, service_name, port_name, plugins, settings)
     60         self.transport = transport if transport is not None else Transport()
     61         self.wsdl = Document(
---> 62             wsdl, self.transport, settings=self.settings)
     63         self.wsse = wsse
     64         self.plugins = plugins if plugins is not None else []

/local_disk0/pythonVirtualEnvDirs/virtualEnv-ed69fce1-1e72-47e6-8036-d7e99c9b2a4b/local/lib/python2.7/site-packages/zeep/wsdl/wsdl.pyc in __init__(self, location, transport, base, settings)
     82         document = self._get_xml_document(location)
     83 
---> 84         root_definitions = Definition(self, document, self.location)
     85         root_definitions.resolve_imports()
     86 

/local_disk0/pythonVirtualEnvDirs/virtualEnv-ed69fce1-1e72-47e6-8036-d7e99c9b2a4b/local/lib/python2.7/site-packages/zeep/wsdl/wsdl.pyc in __init__(self, wsdl, doc, location)
    181         self.parse_imports(doc)
    182 
--> 183         self.parse_types(doc)
    184         self.messages = self.parse_messages(doc)
    185         self.port_types = self.parse_ports(doc)

/local_disk0/pythonVirtualEnvDirs/virtualEnv-ed69fce1-1e72-47e6-8036-d7e99c9b2a4b/local/lib/python2.7/site-packages/zeep/wsdl/wsdl.pyc in parse_types(self, doc)
    314         schema_nodes = findall_multiple_ns(
    315             doc, 'wsdl:types/xsd:schema', namespace_sets)
--> 316         self.types.add_documents(schema_nodes, self.location)
    317 
    318     def parse_messages(self, doc):

/local_disk0/pythonVirtualEnvDirs/virtualEnv-ed69fce1-1e72-47e6-8036-d7e99c9b2a4b/local/lib/python2.7/site-packages/zeep/xsd/schema.pyc in add_documents(self, schema_nodes, location)
    112         resolve_queue = []
    113         for node in schema_nodes:
--> 114             document = self.create_new_document(node, location)
    115             resolve_queue.append(document)
    116 

/local_disk0/pythonVirtualEnvDirs/virtualEnv-ed69fce1-1e72-47e6-8036-d7e99c9b2a4b/local/lib/python2.7/site-packages/zeep/xsd/schema.pyc in create_new_document(self, node, url, base_url, target_namespace)
    213         schema = SchemaDocument(namespace, url, base_url)
    214         self.documents.add(schema)
--> 215         schema.load(self, node)
    216         return schema
    217 

/local_disk0/pythonVirtualEnvDirs/virtualEnv-ed69fce1-1e72-47e6-8036-d7e99c9b2a4b/local/lib/python2.7/site-packages/zeep/xsd/schema.pyc in load(self, schema, node)
    433         #     self.xml_schema = etree.XMLSchema(node)
    434         visitor = SchemaVisitor(schema, self)
--> 435         visitor.visit_schema(node)
    436 
    437     def resolve(self):

/local_disk0/pythonVirtualEnvDirs/virtualEnv-ed69fce1-1e72-47e6-8036-d7e99c9b2a4b/local/lib/python2.7/site-packages/zeep/xsd/visitor.pyc in visit_schema(self, node)
    142 
    143         for child in node:
--> 144             self.process(child, parent=node)
    145 
    146     def visit_import(self, node, parent):

/local_disk0/pythonVirtualEnvDirs/virtualEnv-ed69fce1-1e72-47e6-8036-d7e99c9b2a4b/local/lib/python2.7/site-packages/zeep/xsd/visitor.pyc in process(self, node, parent)
     71         if not visit_func:
     72             raise ValueError("No visitor defined for %r" % node.tag)
---> 73         result = visit_func(self, node, parent)
     74         return result
     75 

/local_disk0/pythonVirtualEnvDirs/virtualEnv-ed69fce1-1e72-47e6-8036-d7e99c9b2a4b/local/lib/python2.7/site-packages/zeep/xsd/visitor.pyc in visit_import(self, node, parent)
    186             self.document._has_empty_import = True
    187 
--> 188         location = normalize_location(self.schema.settings, location, self.document._location)
    189 
    190         # Check if the schema is already imported before based on the

/local_disk0/pythonVirtualEnvDirs/virtualEnv-ed69fce1-1e72-47e6-8036-d7e99c9b2a4b/local/lib/python2.7/site-packages/zeep/loader.pyc in normalize_location(settings, url, base_url)
     85     """
     86     if base_url:
---> 87         url = absolute_location(url, base_url)
     88 
     89     if base_url and settings.force_https:

/local_disk0/pythonVirtualEnvDirs/virtualEnv-ed69fce1-1e72-47e6-8036-d7e99c9b2a4b/local/lib/python2.7/site-packages/zeep/loader.pyc in absolute_location(location, base)
    112         return location
    113 
--> 114     if urlparse(location).scheme in ('http', 'https', 'file'):
    115         return location
    116 

/usr/lib/python2.7/urlparse.pyc in urlparse(url, scheme, allow_fragments)
    141     Note that we don't break the components up in smaller bits
    142     (e.g. netloc is a single string) and we don't expand % escapes."""
--> 143     tuple = urlsplit(url, scheme, allow_fragments)
    144     scheme, netloc, url, query, fragment = tuple
    145     if scheme in uses_params and ';' in url:

/usr/lib/python2.7/urlparse.pyc in urlsplit(url, scheme, allow_fragments)
    180         clear_cache()
    181     netloc = query = fragment = ''
--> 182     i = url.find(':')
    183     if i > 0:
    184         if url[:i] == 'http': # optimize the common case

AttributeError: 'NoneType' object has no attribute 'find'

Most helpful comment

Thanks for the detailed report. Just released 3.3.1 with a fix

All 4 comments

FYI, this appears to be specific to Python 2.7 (the same test in Python 3.6 didn't throw the error).

I think the changes here are the source of the problem:

https://github.com/mvantellingen/python-zeep/blob/master/src/zeep/xsd/visitor.py#L185-L188

At least in Python 2.7, the call to normalize_location() ends up throwing this error because right before that call:

location is None
self.document._location is https://adwords.google.com/api/adwords/mcm/v201809/ManagedCustomerService?wsdl
and self.document._has_empty_import is False

Since location is None, the eventual call to url.find fails with the error shown.

Looking further into urlparse() itself reveals the problem: In Python 3 there is a guard to ensure that the url passed in is coerced into a string, whereas Python 2 assumes the value is a string. That's why url.find() fails on Python 2 and succeeds on Python 3:

https://github.com/python/cpython/blob/2.7/Lib/urlparse.py#L137-L143

https://github.com/python/cpython/blob/master/Lib/urllib/parse.py#L366-L373

A possible solution here would be to add the following guard to normalize_location:

    if url is None:
        url = ''

However, as there are several calls to urlparse() across Zeep, a more complete solution would be to handle this re-encoding in a wrapper function for that function.

Thanks for the detailed report. Just released 3.3.1 with a fix

I see! Thank you.

Was this page helpful?
0 / 5 - 0 ratings