Example #1
0
    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
Example #2
0
    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
Example #3
0
    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)
Example #4
0
    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
Example #5
0
    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
Example #6
0
    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)
Example #7
0
    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
Example #8
0
    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)
Example #9
0
    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
Example #11
0
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
Example #12
0
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
Example #13
0
    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)
Example #14
0
    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)