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'
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.
Most helpful comment
Thanks for the detailed report. Just released 3.3.1 with a fix