def parse_types(self, doc): """Return an xsd.Schema() instance for the given wsdl:types element. If the wsdl:types contain multiple schema definitions then a new wrapping xsd.Schema is defined with xsd:import statements linking them together. If the wsdl:types doesn't container an xml schema then an empty schema is returned instead. <definitions .... > <types> <xsd:schema .... />* </types> </definitions> :param doc: The source document :type doc: lxml.etree._Element """ namespace_sets = [ { 'xsd': 'http://www.w3.org/2001/XMLSchema', 'wsdl': 'http://schemas.xmlsoap.org/wsdl/', }, { 'xsd': 'http://www.w3.org/1999/XMLSchema', 'wsdl': 'http://schemas.xmlsoap.org/wsdl/', }, ] # Find xsd:schema elements (wsdl:types/xsd:schema) schema_nodes = findall_multiple_ns(doc, 'wsdl:types/xsd:schema', namespace_sets) if not schema_nodes: return Schema(None, self.wsdl.transport, self.location, self.wsdl._parser_context) # FIXME: This fixes `test_parse_types_nsmap_issues`, lame solution... schema_nodes = [ xsd_parse_xml(etree.tostring(schema_node), self.location) for schema_node in schema_nodes ] # If there are multiple xsd:schema's defined in the wsdl then we # combine them if len(schema_nodes) > 1: schema_node = combine_schemas(schema_nodes, self.location, self.wsdl._parser_context) else: schema_node = schema_nodes[0] schema = Schema(schema_node, self.wsdl.transport, self.location, self.wsdl._parser_context) # Merge xsd schemas in imported wsdl's for imported_wsdl in self.imports.values(): if imported_wsdl.types: schema.merge(imported_wsdl.types) return schema
def __init__(self, location, transport): """Initialize a WSDL document. The root definition properties are exposed as entry points. :param location: Location of this WSDL :type location: string :param transport: The transport object to be used :type transport: zeep.transports.Transport """ self.location = location if not hasattr(location, 'read') else None self.transport = transport # Dict with all definition objects within this WSDL self._definitions = {} self.types = Schema([], transport=self.transport) # Dict with internal schema objects, used for lxml.ImportResolver self._parser_context = ParserContext() document = self._load_content(location) root_definitions = Definition(self, document, self.location) root_definitions.resolve_imports() # Make the wsdl definitions public self.messages = root_definitions.messages self.port_types = root_definitions.port_types self.bindings = root_definitions.bindings self.services = root_definitions.services
def __init__( self, location, transport: typing.Type["Transport"], base=None, settings=None ): """Initialize a WSDL document. The root definition properties are exposed as entry points. """ self.settings = settings or Settings() if isinstance(location, str): if is_relative_path(location): location = os.path.abspath(location) self.location = location else: self.location = base self.transport = transport # Dict with all definition objects within this WSDL self._definitions = ( {} ) # type: typing.Dict[typing.Tuple[str, str], "Definition"] self.types = Schema( node=None, transport=self.transport, location=self.location, settings=self.settings, ) self.load(location)
def __init__(self, location, transport, base=None, strict=True): """Initialize a WSDL document. The root definition properties are exposed as entry points. """ if isinstance(location, six.string_types): if is_relative_path(location): location = os.path.abspath(location) self.location = location else: self.location = base self.transport = transport self.strict = strict # Dict with all definition objects within this WSDL self._definitions = {} self.types = Schema(node=None, transport=self.transport, location=self.location, strict=self.strict) document = self._get_xml_document(location) root_definitions = Definition(self, document, self.location) root_definitions.resolve_imports() # Make the wsdl definitions public self.messages = root_definitions.messages self.port_types = root_definitions.port_types self.bindings = root_definitions.bindings self.services = root_definitions.services
def parse_imports(self, doc): """Import other WSDL definitions in this document. Note that imports are non-transitive, so only import definitions which are defined in the imported document and ignore definitions imported in that document. This should handle recursive imports though: A -> B -> A A -> B -> C -> A :param doc: The source document :type doc: lxml.etree._Element """ for import_node in doc.findall("wsdl:import", namespaces=NSMAP): location = import_node.get('location') namespace = import_node.get('namespace') if namespace in self.wsdl._definitions: self.imports[namespace] = self.wsdl._definitions[namespace] else: document = self.wsdl._load_content(location) location = absolute_location(location, self.location) if etree.QName(document.tag).localname == 'schema': self.schema = Schema(document, self.wsdl.transport, location, self.wsdl._parser_context) else: wsdl = Definition(self.wsdl, document, location) self.imports[namespace] = wsdl
def resolve_imports(self): """Resolve all root elements (types, messages, etc).""" # Simple guard to protect against cyclic imports if self._resolved_imports: return self._resolved_imports = True # Create a reference to an imported schema if the definition has no # schema of it's own. if self.schema is None: for definition in self.imports.values(): if definition.schema and not definition.schema.is_empty: self.schema = definition.schema break else: logger.debug("No suitable main schema found for wsdl. " + "Creating an empty placeholder") self.schema = Schema(None, self.wsdl.transport, self.location, self.wsdl._parser_context, self.location) for definition in self.imports.values(): definition.resolve_imports() for message in self.messages.values(): message.resolve(self) for port_type in self.port_types.values(): port_type.resolve(self) for binding in self.bindings.values(): binding.resolve(self) for service in self.services.values(): service.resolve(self)
def __init__(self, location, transport, base=None): """Initialize a WSDL document. The root definition properties are exposed as entry points. :param location: Location of this WSDL :type location: string :param transport: The transport object to be used :type transport: zeep.transports.Transport """ if isinstance(location, six.string_types): if is_relative_path(location): location = os.path.abspath(location) self.location = location else: self.location = base self.transport = transport # Dict with all definition objects within this WSDL self._definitions = {} self.types = Schema([], transport=self.transport, location=self.location) document = self._load_content(location) root_definitions = Definition(self, document, self.location) root_definitions.resolve_imports() # Make the wsdl definitions public self.messages = root_definitions.messages self.port_types = root_definitions.port_types self.bindings = root_definitions.bindings self.services = root_definitions.services
def initialize_schema(self): if self.schema: return for definition in self.imports.values(): if self.schema is None and definition.schema: self.schema = definition.schema break else: self.schema = Schema( None, self.wsdl.transport, self.location, self.wsdl._parser_context, self.location)
def parse_types(self, doc): """Return a `types.Schema` instance. Note that a WSDL can contain multiple XSD schema's. The schemas can reference import each other using xsd:import statements. <definitions .... > <types> <xsd:schema .... />* </types> </definitions> """ namespace_sets = [ { 'xsd': 'http://www.w3.org/2001/XMLSchema' }, { 'xsd': 'http://www.w3.org/1999/XMLSchema' }, ] types = doc.find('wsdl:types', namespaces=NSMAP) schema_nodes = findall_multiple_ns(types, 'xsd:schema', namespace_sets) if not schema_nodes: return None # FIXME: This fixes `test_parse_types_nsmap_issues`, lame solution... schema_nodes = [ self._parse_content(etree.tostring(schema_node)) for schema_node in schema_nodes ] for schema_node in schema_nodes: tns = schema_node.get('targetNamespace') self.schema_references['intschema+%s' % tns] = schema_node # Only handle the import statements from the 2001 xsd's for now import_tag = QName('http://www.w3.org/2001/XMLSchema', 'import').text for schema_node in schema_nodes: for import_node in schema_node.findall(import_tag): if import_node.get('schemaLocation'): continue namespace = import_node.get('namespace') import_node.set('schemaLocation', 'intschema+%s' % namespace) schema_node = schema_nodes[0] return Schema(schema_node, self.transport, self.schema_references)
def __init__(self, location, transport: typing.Type["Transport"], base=None, settings=None): """Initialize a WSDL document. The root definition properties are exposed as entry points. """ self.settings = settings or Settings() if isinstance(location, str): if is_relative_path(location): location = os.path.abspath(location) self.location = location else: self.location = base self.transport = transport # Dict with all definition objects within this WSDL self._definitions = ( {}) # type: typing.Dict[typing.Tuple[str, str], "Definition"] self.types = Schema( node=None, transport=self.transport, location=self.location, settings=self.settings, ) document = self._get_xml_document(location) root_definitions = Definition(self, document, self.location) root_definitions.resolve_imports() # Make the wsdl definitions public self.messages = root_definitions.messages self.port_types = root_definitions.port_types self.bindings = root_definitions.bindings self.services = root_definitions.services
def test_parse_response(): schema_node = etree.fromstring(b""" <?xml version="1.0"?> <wsdl:definitions xmlns="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://tests.python-zeep.org/"> <wsdl:types> <schema targetNamespace="http://tests.python-zeep.org/" xmlns:tns="http://tests.python-zeep.org/" elementFormDefault="qualified"> <complexType name="Item"> <sequence> <element minOccurs="0" maxOccurs="1" name="Key" type="string" /> <element minOccurs="1" maxOccurs="1" name="Value" type="int" /> </sequence> </complexType> <complexType name="ArrayOfItems"> <sequence> <element minOccurs="0" maxOccurs="unbounded" name="Item" nillable="true" type="tns:Item" /> </sequence> </complexType> <complexType name="ZeepExampleResult"> <sequence> <element minOccurs="1" maxOccurs="1" name="SomeValue" type="int" /> <element minOccurs="0" maxOccurs="1" name="Results" type="tns:ArrayOfItems" /> </sequence> </complexType> <element name="ZeepExampleResponse"> <complexType> <sequence> <element minOccurs="0" maxOccurs="1" name="ZeepExampleResult" type="tns:ZeepExampleResult" /> </sequence> </complexType> </element> </schema> </wsdl:types> </wsdl:definitions> """.strip()) # noqa response_node = etree.fromstring(b""" <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <ZeepExampleResponse xmlns="http://tests.python-zeep.org/"> <ZeepExampleResult> <SomeValue>45313</SomeValue> <Results> <Item> <Key>ABC100</Key> <Value>10</Value> </Item> <Item> <Key>ABC200</Key> <Value>20</Value> </Item> </Results> </ZeepExampleResult> </ZeepExampleResponse> </soap:Body> </soap:Envelope> """.strip()) schema = Schema(schema_node.find('*/{http://www.w3.org/2001/XMLSchema}schema')) assert schema response_type = schema.get_element( '{http://tests.python-zeep.org/}ZeepExampleResponse') nsmap = { 'soap': 'http://schemas.xmlsoap.org/soap/envelope/', 'tns': 'http://tests.python-zeep.org/', } node = response_node.find('soap:Body/tns:ZeepExampleResponse', namespaces=nsmap) assert node is not None obj = response_type.parse(node) assert obj.ZeepExampleResult.SomeValue == 45313 assert len(obj.ZeepExampleResult.Results.Item) == 2 assert obj.ZeepExampleResult.Results.Item[0].Key == 'ABC100' assert obj.ZeepExampleResult.Results.Item[0].Value == 10 assert obj.ZeepExampleResult.Results.Item[1].Key == 'ABC200' assert obj.ZeepExampleResult.Results.Item[1].Value == 20
def test_parse_response(): schema_node = etree.fromstring(b""" <?xml version="1.0"?> <wsdl:definitions xmlns="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://tests.python-zeep.org/"> <wsdl:types> <schema targetNamespace="http://tests.python-zeep.org/" xmlns:tns="http://tests.python-zeep.org/" elementFormDefault="qualified"> <complexType name="Item"> <sequence> <element minOccurs="0" maxOccurs="1" name="Key" type="string" /> <element minOccurs="1" maxOccurs="1" name="Value" type="int" /> </sequence> </complexType> <complexType name="ArrayOfItems"> <sequence> <element minOccurs="0" maxOccurs="unbounded" name="Item" nillable="true" type="tns:Item" /> </sequence> </complexType> <complexType name="ZeepExampleResult"> <sequence> <element minOccurs="1" maxOccurs="1" name="SomeValue" type="int" /> <element minOccurs="0" maxOccurs="1" name="Results" type="tns:ArrayOfItems" /> </sequence> </complexType> <element name="ZeepExampleResponse"> <complexType> <sequence> <element minOccurs="0" maxOccurs="1" name="ZeepExampleResult" type="tns:ZeepExampleResult" /> </sequence> </complexType> </element> </schema> </wsdl:types> </wsdl:definitions> """.strip()) # noqa response_node = etree.fromstring(b""" <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <ZeepExampleResponse xmlns="http://tests.python-zeep.org/"> <ZeepExampleResult> <SomeValue>45313</SomeValue> <Results> <Item> <Key>ABC100</Key> <Value>10</Value> </Item> <Item> <Key>ABC200</Key> <Value>20</Value> </Item> </Results> </ZeepExampleResult> </ZeepExampleResponse> </soap:Body> </soap:Envelope> """.strip()) schema = Schema(schema_node.find('*/{http://www.w3.org/2001/XMLSchema}schema')) assert schema response_type = schema.get_element( '{http://tests.python-zeep.org/}ZeepExampleResponse') nsmap = { 'soap': 'http://schemas.xmlsoap.org/soap/envelope/', 'tns': 'http://tests.python-zeep.org/', } node = response_node.find('soap:Body/tns:ZeepExampleResponse', namespaces=nsmap) assert node is not None obj = response_type.parse(node, schema) assert obj.ZeepExampleResult.SomeValue == 45313 assert len(obj.ZeepExampleResult.Results.Item) == 2 assert obj.ZeepExampleResult.Results.Item[0].Key == 'ABC100' assert obj.ZeepExampleResult.Results.Item[0].Value == 10 assert obj.ZeepExampleResult.Results.Item[1].Key == 'ABC200' assert obj.ZeepExampleResult.Results.Item[1].Value == 20
def parse_types(self, doc): """Return a `types.Schema` instance. Note that a WSDL can contain multiple XSD schema's. The schemas can reference each other using xsd:import statements. <definitions .... > <types> <xsd:schema .... />* </types> </definitions> :param doc: The source document :type doc: lxml.etree._Element """ namespace_sets = [ { 'xsd': 'http://www.w3.org/2001/XMLSchema' }, { 'xsd': 'http://www.w3.org/1999/XMLSchema' }, ] # Find xsd:schema elements (wsdl:types/xsd:schema) types = doc.find('wsdl:types', namespaces=NSMAP) if types is None or len(types) == 0: schema_nodes = [] else: schema_nodes = findall_multiple_ns(types, 'xsd:schema', namespace_sets) if not schema_nodes: return None # FIXME: This fixes `test_parse_types_nsmap_issues`, lame solution... schema_nodes = [ self.wsdl._parse_content(etree.tostring(schema_node), self.location) for schema_node in schema_nodes ] if len(schema_nodes) == 1: return Schema(schema_nodes[0], self.wsdl.transport, self.location, self.wsdl._parser_context, self.location) # A wsdl can contain multiple schema nodes. These can import each other # by simply referencing them by the namespace. To handle this in a way # that lxml schema can also handle it we create a new container schema # which imports the other schemas. Since imports are non-transitive we # need to copy the schema imports the newyl created container schema. # Create namespace mapping (namespace -> internal location) schema_ns = {} for i, schema_node in enumerate(schema_nodes): ns = schema_node.get('targetNamespace') int_name = schema_ns[ns] = 'intschema:xsd%d' % i self.wsdl._parser_context.schema_nodes.add(schema_ns[ns], schema_node) self.wsdl._parser_context.schema_locations[ int_name] = self.location # Only handle the import statements from the 2001 xsd's for now import_tag = '{http://www.w3.org/2001/XMLSchema}import' # Create a new schema node with xsd:import statements for all # schema's listed here. container = etree.Element('{http://www.w3.org/2001/XMLSchema}schema') for i, schema_node in enumerate(schema_nodes): # Create a new xsd:import element to import the schema import_node = etree.Element(import_tag) import_node.set('schemaLocation', 'intschema:xsd%d' % i) if schema_node.get('targetNamespace'): import_node.set('namespace', schema_node.get('targetNamespace')) container.append(import_node) # Add the namespace mapping created earlier here to the import # statements. for import_node in schema_node.findall(import_tag): location = import_node.get('schemaLocation') namespace = import_node.get('namespace') if not location and namespace in schema_ns: import_node.set('schemaLocation', schema_ns[namespace]) container.append(deepcopy(import_node)) schema_node = container return Schema(schema_node, self.wsdl.transport, self.location, self.wsdl._parser_context, self.location)
def parse_types(self, doc): """Return a `types.Schema` instance. Note that a WSDL can contain multiple XSD schema's. The schemas can reference import each other using xsd:import statements. <definitions .... > <types> <xsd:schema .... />* </types> </definitions> """ namespace_sets = [ { 'xsd': 'http://www.w3.org/2001/XMLSchema' }, { 'xsd': 'http://www.w3.org/1999/XMLSchema' }, ] types = doc.find('wsdl:types', namespaces=NSMAP) if types is None: return schema_nodes = findall_multiple_ns(types, 'xsd:schema', namespace_sets) if not schema_nodes: return None # FIXME: This fixes `test_parse_types_nsmap_issues`, lame solution... schema_nodes = [ self.wsdl._parse_content(etree.tostring(schema_node), self.location) for schema_node in schema_nodes ] if len(schema_nodes) == 1: return Schema(schema_nodes[0], self.wsdl.transport, self.location, self.wsdl._parser_context) # A wsdl can container multiple schema nodes. The can import each # other by simply referencing by the namespace. To handle this in a # way that lxml schema can also handle it we create a new root schema # which imports the other schemas. This seems to work fine, although # I'm not sure how the non-transitive nature of imports impact it. # Create namespace mapping (namespace -> internal location) schema_ns = {} for i, schema_node in enumerate(schema_nodes): ns = schema_node.get('targetNamespace') schema_ns[ns] = 'intschema:xsd%d' % i self.wsdl._parser_context.schema_nodes.add(schema_ns[ns], schema_node) # Only handle the import statements from the 2001 xsd's for now import_tag = QName('http://www.w3.org/2001/XMLSchema', 'import').text # Create a new schema node with xsd:import statements for all # schema's listed here. root = etree.Element( etree.QName('http://www.w3.org/2001/XMLSchema', 'schema')) for i, schema_node in enumerate(schema_nodes): import_node = etree.Element( etree.QName('http://www.w3.org/2001/XMLSchema', 'import')) import_node.set('schemaLocation', 'intschema:xsd%d' % i) if schema_node.get('targetNamespace'): import_node.set('namespace', schema_node.get('targetNamespace')) root.append(import_node) # Add the namespace mapping created earlier here to the import # statements. for import_node in schema_node.findall(import_tag): if import_node.get('schemaLocation'): continue namespace = import_node.get('namespace') import_node.set('schemaLocation', schema_ns[namespace]) schema_node = root return Schema(schema_node, self.wsdl.transport, self.location, self.wsdl._parser_context)