Skip to content

Commit 963001f

Browse files
committed
Create an internal SchemaContainer class to manage multiple schemas
1 parent a243090 commit 963001f

File tree

3 files changed

+82
-51
lines changed

3 files changed

+82
-51
lines changed

src/zeep/xsd/elements/any.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def parse(self, xmlelement, schema, context=None):
5555
qname = etree.QName(xmlelement.tag)
5656
if context and context.schemas:
5757
for context_schema in context.schemas:
58-
if context_schema._has_schema_document(qname.namespace):
58+
if context_schema.documents.has_schema_document_for_ns(qname.namespace):
5959
schema = context_schema
6060
break
6161

src/zeep/xsd/schema.py

Lines changed: 80 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def __init__(self, node=None, transport=None, location=None, strict=True):
2828

2929
self._transport = transport
3030

31-
self._documents = OrderedDict()
31+
self.documents = _SchemaContainer()
3232
self._prefix_map_auto = {}
3333
self._prefix_map_custom = {}
3434

@@ -40,11 +40,12 @@ def __init__(self, node=None, transport=None, location=None, strict=True):
4040
nodes = node
4141
self.add_documents(nodes, location)
4242

43-
@property
44-
def documents(self):
45-
for documents in self._documents.values():
46-
for document in documents:
47-
yield document
43+
def __repr__(self):
44+
main_doc = self.root_document
45+
if main_doc:
46+
return '<Schema(location=%r, tns=%r)>' % (
47+
main_doc._location, main_doc._target_namespace)
48+
return '<Schema()>'
4849

4950
@property
5051
def prefix_map(self):
@@ -69,7 +70,7 @@ def is_empty(self):
6970

7071
@property
7172
def namespaces(self):
72-
return set(self._documents.keys())
73+
return self.documents.get_all_namespaces()
7374

7475
@property
7576
def elements(self):
@@ -99,20 +100,13 @@ def types(self):
99100
yield type_
100101
seen.add(type_.qname)
101102

102-
def __repr__(self):
103-
main_doc = self.root_document
104-
if main_doc:
105-
return '<Schema(location=%r, tns=%r)>' % (
106-
main_doc._location, main_doc._target_namespace)
107-
return '<Schema()>'
108-
109103
def add_documents(self, schema_nodes, location):
110-
documents = []
104+
resolve_queue = []
111105
for node in schema_nodes:
112106
document = self.create_new_document(node, location)
113-
documents.append(document)
107+
resolve_queue.append(document)
114108

115-
for document in documents:
109+
for document in resolve_queue:
116110
document.resolve()
117111

118112
self._prefix_map_auto = self._create_prefix_map()
@@ -199,19 +193,24 @@ def get_shorthand_for_ns(self, namespace):
199193
return namespace
200194

201195
def create_new_document(self, node, url, base_url=None):
196+
"""
197+
198+
:rtype: zeep.xsd.schema.SchemaDocument
199+
200+
"""
202201
namespace = node.get('targetNamespace') if node is not None else None
203202
if base_url is None:
204203
base_url = url
205204

206205
schema = SchemaDocument(namespace, url, base_url)
207-
self._add_schema_document(schema)
206+
self.documents.add(schema)
208207
schema.load(self, node)
209208
return schema
210209

211210
def merge(self, schema):
212211
"""Merge an other XSD schema in this one"""
213212
for document in schema.documents:
214-
self._add_schema_document(document)
213+
self.documents.add(document)
215214
self._prefix_map_auto = self._create_prefix_map()
216215

217216
def _load_default_documents(self):
@@ -226,7 +225,7 @@ def _load_default_documents(self):
226225
schema.register_element(cls.qname, instance)
227226

228227
schema._is_internal = True
229-
self._add_schema_document(schema)
228+
self.documents.add(schema)
230229
return schema
231230

232231
def _get_instance(self, qname, method_name, name):
@@ -277,64 +276,96 @@ def _create_prefix_map(self):
277276
'xsd': 'http://www.w3.org/2001/XMLSchema',
278277
}
279278
i = 0
280-
for namespace in self._documents.keys():
279+
for namespace in self.documents.get_all_namespaces():
281280
if namespace is None or namespace in prefix_map.values():
282281
continue
283282

284283
prefix_map['ns%d' % i] = namespace
285284
i += 1
286285
return prefix_map
287286

288-
def _has_schema_document(self, namespace):
289-
"""Return a boolean if there is a SchemaDocumnet for the namespace.
287+
def _get_schema_documents(self, namespace, fail_silently=False):
288+
"""Return a list of SchemaDocument's for the given namespace.
290289
291-
:rtype: boolean
290+
:rtype: list of SchemaDocument
292291
293292
"""
294-
return namespace in self._documents
293+
if (
294+
not self.documents.has_schema_document_for_ns(namespace)
295+
and namespace in const.AUTO_IMPORT_NAMESPACES
296+
):
297+
logger.debug("Auto importing missing known schema: %s", namespace)
298+
self.add_document_by_url(namespace)
299+
300+
return self.documents.get_by_namespace(namespace, fail_silently)
301+
295302

296-
def _add_schema_document(self, document):
303+
class _SchemaContainer(object):
304+
"""Container instances to store multiple SchemaDocument objects per
305+
namespace.
306+
307+
"""
308+
309+
def __init__(self):
310+
self._instances = OrderedDict()
311+
312+
def __iter__(self):
313+
for document in self.values():
314+
yield document
315+
316+
def add(self, document):
317+
"""Append a schema document
318+
319+
:param document: zeep.xsd.schema.SchemaDocument
320+
321+
"""
297322
logger.debug("Add document with tns %s to schema %s", document.namespace, id(self))
298-
documents = self._documents.setdefault(document.namespace, [])
323+
documents = self._instances.setdefault(document.namespace, [])
299324
documents.append(document)
300325

301-
def _get_schema_document(self, namespace, location):
302-
"""Return a list of SchemaDocument's for the given namespace AND
326+
def get_all_namespaces(self):
327+
return self._instances.keys()
328+
329+
def get_by_namespace(self, namespace, fail_silently):
330+
if namespace not in self._instances:
331+
if fail_silently:
332+
return []
333+
raise exceptions.NamespaceError(
334+
"No schema available for the namespace %r" % namespace)
335+
return self._instances[namespace]
336+
337+
def get_by_namespace_and_location(self, namespace, location):
338+
"""Return list of SchemaDocument's for the given namespace AND
303339
location.
304340
305-
:rtype: SchemaDocument
341+
:rtype: zeep.xsd.schema.SchemaDocument
306342
307343
"""
308-
for document in self._documents.get(namespace, []):
344+
documents = self.get_by_namespace(namespace, fail_silently=True)
345+
for document in documents:
309346
if document._location == location:
310347
return document
311348

312-
def _get_schema_documents(self, namespace, fail_silently=False):
313-
"""Return a list of SchemaDocument's for the given namespace.
349+
def has_schema_document_for_ns(self, namespace):
350+
"""Return a boolean if there is a SchemaDocument for the namespace.
314351
315-
:rtype: list of SchemaDocument
352+
:rtype: boolean
316353
317354
"""
318-
if (
319-
namespace not in self._documents
320-
and namespace in const.AUTO_IMPORT_NAMESPACES
321-
):
322-
logger.debug("Auto importing missing known schema: %s", namespace)
323-
self.add_document_by_url(namespace)
355+
return namespace in self._instances
324356

325-
if namespace not in self._documents:
326-
if fail_silently:
327-
return []
328-
raise exceptions.NamespaceError(
329-
"No schema available for the namespace %r" % namespace)
330-
return self._documents[namespace]
357+
def values(self):
358+
for documents in self._instances.values():
359+
for document in documents:
360+
yield document
331361

332362

333363
class SchemaDocument(object):
334-
"""A Schema Document consists of a set of schema components for a specific
335-
target namespace.
364+
"""A Schema Document consists of a set of schema components for a
365+
specific target namespace.
336366
337367
"""
368+
338369
def __init__(self, namespace, location, base_url):
339370
logger.debug("Init schema document for %r", location)
340371

@@ -379,7 +410,7 @@ def load(self, schema, node):
379410
if node is None:
380411
return
381412

382-
if not schema._has_schema_document(self._target_namespace):
413+
if not schema.documents.has_schema_document_for_ns(self._target_namespace):
383414
raise RuntimeError(
384415
"The document needs to be registered in the schema before " +
385416
"it can be loaded")

src/zeep/xsd/visitor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ def visit_import(self, node, parent):
170170

171171
# Check if the schema is already imported before based on the
172172
# namespace. Schema's without namespace are registered as 'None'
173-
document = self.schema._get_schema_document(namespace, location)
173+
document = self.schema.documents.get_by_namespace_and_location(namespace, location)
174174
if document:
175175
logger.debug("Returning existing schema: %r", location)
176176
self.register_import(namespace, document)

0 commit comments

Comments
 (0)