Пример #1
0
    def parse_xmlelement(self,
                         xmlelement,
                         schema,
                         allow_none=True,
                         context=None):
        """Consume matching xmlelements and call parse() on each"""
        # If this is an empty complexType (<xsd:complexType name="x"/>)
        if not self.attributes and not self.elements:
            return None

        attributes = xmlelement.attrib
        init_kwargs = OrderedDict()

        # If this complexType extends a simpleType then we have no nested
        # elements. Parse it directly via the type object. This is the case
        # for xsd:simpleContent
        if isinstance(self._element, Element) and isinstance(
                self._element.type, SimpleType):
            name, element = self.elements_nested[0]
            init_kwargs[name] = element.type.parse_xmlelement(xmlelement,
                                                              schema,
                                                              name,
                                                              context=context)
        else:
            elements = deque(xmlelement.iterchildren())
            if allow_none and len(elements) == 0 and len(attributes) == 0:
                return

            # Parse elements. These are always indicator elements (all, choice,
            # group, sequence)
            for name, element in self.elements_nested:
                try:
                    result = element.parse_xmlelements(elements,
                                                       schema,
                                                       name,
                                                       context=context)
                    if result:
                        init_kwargs.update(result)
                except UnexpectedElementError as exc:
                    raise XMLParseError(exc.message)

            # Check if all children are consumed (parsed)
            if elements:
                raise XMLParseError("Unexpected element %r" % elements[0].tag)

        # Parse attributes
        if attributes:
            attributes = copy.copy(attributes)
            for name, attribute in self.attributes:
                if attribute.name:
                    if attribute.qname.text in attributes:
                        value = attributes.pop(attribute.qname.text)
                        init_kwargs[name] = attribute.parse(value)
                else:
                    init_kwargs[name] = attribute.parse(attributes)

        return self(**init_kwargs)
Пример #2
0
def as_qname(value: str, nsmap, target_namespace=None) -> etree.QName:
    """Convert the given value to a QName"""
    value = value.strip()  # some xsd's contain leading/trailing spaces
    if ":" in value:
        prefix, local = value.split(":")

        # The xml: prefix is always bound to the XML namespace, see
        # https://www.w3.org/TR/xml-names/
        if prefix == "xml":
            namespace = "http://www.w3.org/XML/1998/namespace"
        else:
            namespace = nsmap.get(prefix)

        if not namespace:
            raise XMLParseError("No namespace defined for %r (%r)" % (prefix, value))

        # Workaround for https://github.com/mvantellingen/python-zeep/issues/349
        if not local:
            return etree.QName(XSD, "anyType")

        return etree.QName(namespace, local)

    if target_namespace:
        return etree.QName(target_namespace, value)

    if nsmap.get(None):
        return etree.QName(nsmap[None], value)
    return etree.QName(value)
Пример #3
0
def as_qname(value, nsmap, target_namespace=None):
    """Convert the given value to a QName"""
    if ':' in value:
        prefix, local = value.split(':')

        # The xml: prefix is always bound to the XML namespace, see
        # https://www.w3.org/TR/xml-names/
        if prefix == 'xml':
            namespace = 'http://www.w3.org/XML/1998/namespace'
        else:
            namespace = nsmap.get(prefix)

        if not namespace:
            raise XMLParseError("No namespace defined for %r" % prefix)

        # Workaround for https://github.com/mvantellingen/python-zeep/issues/349
        if not local:
            return etree.QName(XSD, 'anyType')

        return etree.QName(namespace, local)

    if target_namespace:
        return etree.QName(target_namespace, value)

    if nsmap.get(None):
        return etree.QName(nsmap[None], value)
    return etree.QName(value)
Пример #4
0
def as_qname(value, nsmap, target_namespace=None):
    """Convert the given value to a QName"""
    value = value.strip()  # some xsd's contain leading/trailing spaces
    if ':' in value:
        prefix, local = value.split(':')

        # The xml: prefix is always bound to the XML namespace, see
        # https://www.w3.org/TR/xml-names/
        if prefix == 'xml':
            namespace = 'http://www.w3.org/XML/1998/namespace'
        else:
            namespace = nsmap.get(prefix)

        if not namespace:
            raise XMLParseError("No namespace defined for %r (%r)" %
                                (prefix, value))

        # Workaround for https://github.com/mvantellingen/python-zeep/issues/349
        if not local:
            return etree.QName(XSD, 'anyType')

        # Workaround a idiotice do b1ws devolver <env:Value>env:-1013</env:Value>
        # O -1013 dá erro.
        if local[0] in "-0123456789":
            return etree.QName(namespace, "_" + local)

        return etree.QName(namespace, local)

    if target_namespace:
        return etree.QName(target_namespace, value)

    if nsmap.get(None):
        return etree.QName(nsmap[None], value)
    return etree.QName(value)
Пример #5
0
 def _process_attributes(self, node, items):
     attributes = []
     for child in items:
         attribute = self.process(child, node)
         if child.tag in (tags.attribute, tags.attributeGroup, tags.anyAttribute):
             attributes.append(attribute)
         else:
             raise XMLParseError("Unexpected tag: %s" % child.tag)
     return attributes
Пример #6
0
 def _process_attributes(self, node, items):
     attributes = []
     for child in items:
         if child.tag in (tags.attribute, tags.attributeGroup,
                          tags.anyAttribute):
             attribute = self.process(child, node)
             attributes.append(attribute)
         else:
             raise XMLParseError("Unexpected tag `%s`" % (child.tag),
                                 filename=self.document._location,
                                 sourceline=node.sourceline)
     return attributes
Пример #7
0
def as_qname(value, nsmap, target_namespace):
    """Convert the given value to a QName"""
    if ':' in value:
        prefix, local = value.split(':')
        namespace = nsmap.get(prefix)
        if not namespace:
            raise XMLParseError("No namespace defined for %r" % prefix)

        # Workaround for https://github.com/mvantellingen/python-zeep/issues/349
        if not local:
            return etree.QName(XSD, 'anyType')

        return etree.QName(namespace, local)

    if target_namespace:
        return etree.QName(target_namespace, value)

    if nsmap.get(None):
        return etree.QName(nsmap[None], value)
    return etree.QName(value)
Пример #8
0
    def visit_import(self, node, parent):
        """

        Definition::

            <import
              id = ID
              namespace = anyURI
              schemaLocation = anyURI
              {any attributes with non-schema Namespace}...>
            Content: (annotation?)
            </import>

        :param node: The XML node
        :type node: lxml.etree._Element
        :param parent: The parent XML node
        :type parent: lxml.etree._Element

        """
        schema_node = None
        namespace = node.get("namespace")
        location = node.get("schemaLocation")
        if location:
            location = normalize_location(self.schema.settings, location,
                                          self.document._base_url)

        if not namespace and not self.document._target_namespace:
            raise XMLParseError(
                "The attribute 'namespace' must be existent if the "
                "importing schema has no target namespace.",
                filename=self.document.location,
                sourceline=node.sourceline,
            )

        # We found an empty <import/> statement, this needs to trigger 4.1.2
        # from https://www.w3.org/TR/2012/REC-xmlschema11-1-20120405/#src-resolve
        # for QName resolving.
        # In essence this means we will resolve QNames without a namespace to no
        # namespace instead of the target namespace.
        # The following code snippet works because imports have to occur before we
        # visit elements.
        if not namespace and not location:
            self.document._has_empty_import = True

        # Check if the schema is already imported before based on the
        # namespace. Schema's without namespace are registered as 'None'
        document = self.schema.documents.get_by_namespace_and_location(
            namespace, location)
        if document:
            logger.debug("Returning existing schema: %r", location)
            self.register_import(namespace, document)
            return document

        # Hardcode the mapping between the xml namespace and the xsd for now.
        # This seems to fix issues with exchange wsdl's, see #220
        if not location and namespace == "http://www.w3.org/XML/1998/namespace":
            location = "https://www.w3.org/2001/xml.xsd"

        # Silently ignore import statements which we can't resolve via the
        # namespace and doesn't have a schemaLocation attribute.
        if not location:
            logger.debug(
                "Ignoring import statement for namespace %r " +
                "(missing schemaLocation)",
                namespace,
            )
            return

        # Load the XML
        schema_node = self._retrieve_data(location,
                                          base_url=self.document._location)

        # Check if the xsd:import namespace matches the targetNamespace. If
        # the xsd:import statement didn't specify a namespace then make sure
        # that the targetNamespace wasn't declared by another schema yet.
        schema_tns = schema_node.get("targetNamespace")
        if namespace and schema_tns and namespace != schema_tns:
            raise XMLParseError(
                ("The namespace defined on the xsd:import doesn't match the "
                 "imported targetNamespace located at %r ") % (location),
                filename=self.document._location,
                sourceline=node.sourceline,
            )

        # If the imported schema doesn't define a target namespace and the
        # node doesn't specify it either then inherit the existing target
        # namespace.
        elif not schema_tns and not namespace:
            namespace = self.document._target_namespace

        schema = self.schema.create_new_document(schema_node,
                                                 location,
                                                 target_namespace=namespace)
        self.register_import(namespace, schema)
        return schema
Пример #9
0
 def _create_error(self, message, node):
     return XMLParseError(message,
                          filename=self.document._location,
                          sourceline=node.sourceline)
Пример #10
0
    def visit_import(self, node, parent):
        """

        Definition::

            <import
              id = ID
              namespace = anyURI
              schemaLocation = anyURI
              {any attributes with non-schema Namespace}...>
            Content: (annotation?)
            </import>

        :param node: The XML node
        :type node: lxml.etree._Element
        :param parent: The parent XML node
        :type parent: lxml.etree._Element

        """
        schema_node = None
        namespace = node.get('namespace')
        location = node.get('schemaLocation')
        if location:
            location = absolute_location(location, self.document._base_url)

        if not namespace and not self.document._target_namespace:
            raise XMLParseError(
                "The attribute 'namespace' must be existent if the "
                "importing schema has no target namespace.",
                filename=self._document.location,
                sourceline=node.sourceline)

        # Check if the schema is already imported before based on the
        # namespace. Schema's without namespace are registered as 'None'
        document = self.schema.documents.get_by_namespace_and_location(
            namespace, location)
        if document:
            logger.debug("Returning existing schema: %r", location)
            self.register_import(namespace, document)
            return document

        # Hardcode the mapping between the xml namespace and the xsd for now.
        # This seems to fix issues with exchange wsdl's, see #220
        if not location and namespace == 'http://www.w3.org/XML/1998/namespace':
            location = 'https://www.w3.org/2001/xml.xsd'

        # Silently ignore import statements which we can't resolve via the
        # namespace and doesn't have a schemaLocation attribute.
        if not location:
            logger.debug(
                "Ignoring import statement for namespace %r " +
                "(missing schemaLocation)", namespace)
            return

        # Load the XML
        schema_node = load_external(location,
                                    self.schema._transport,
                                    settings=self.schema.settings)

        # Check if the xsd:import namespace matches the targetNamespace. If
        # the xsd:import statement didn't specify a namespace then make sure
        # that the targetNamespace wasn't declared by another schema yet.
        schema_tns = schema_node.get('targetNamespace')
        if namespace and schema_tns and namespace != schema_tns:
            raise XMLParseError(
                ("The namespace defined on the xsd:import doesn't match the "
                 "imported targetNamespace located at %r ") % (location),
                filename=self.document._location,
                sourceline=node.sourceline)

        schema = self.schema.create_new_document(schema_node, location)
        self.register_import(namespace, schema)
        return schema
Пример #11
0
    def parse_xmlelement(self,
                         xmlelement,
                         schema=None,
                         allow_none=True,
                         context=None,
                         schema_type=None):
        """Consume matching xmlelements and call parse() on each

        :param xmlelement: XML element objects
        :type xmlelement: lxml.etree._Element
        :param schema: The parent XML schema
        :type schema: zeep.xsd.Schema
        :param allow_none: Allow none
        :type allow_none: bool
        :param context: Optional parsing context (for inline schemas)
        :type context: zeep.xsd.context.XmlParserContext
        :param schema_type: The original type (not overriden via xsi:type)
        :type schema_type: zeep.xsd.types.base.Type
        :rtype: dict or None

        """
        # If this is an empty complexType (<xsd:complexType name="x"/>)
        if not self.attributes and not self.elements:
            return None

        attributes = xmlelement.attrib
        init_kwargs = OrderedDict()

        # If this complexType extends a simpleType then we have no nested
        # elements. Parse it directly via the type object. This is the case
        # for xsd:simpleContent
        if isinstance(self._element, Element) and isinstance(
                self._element.type, AnySimpleType):
            name, element = self.elements_nested[0]
            init_kwargs[name] = element.type.parse_xmlelement(xmlelement,
                                                              schema,
                                                              name,
                                                              context=context)
        else:
            elements = deque(xmlelement.iterchildren())
            if allow_none and len(elements) == 0 and len(attributes) == 0:
                return

            # Parse elements. These are always indicator elements (all, choice,
            # group, sequence)
            assert len(self.elements_nested) < 2
            for name, element in self.elements_nested:
                try:
                    result = element.parse_xmlelements(elements,
                                                       schema,
                                                       name,
                                                       context=context)
                    if result:
                        init_kwargs.update(result)
                except UnexpectedElementError as exc:
                    raise XMLParseError(exc.message)

            # Check if all children are consumed (parsed)
            if elements:
                if schema.strict:
                    raise XMLParseError("Unexpected element %r" %
                                        elements[0].tag)
                else:
                    init_kwargs['_raw_elements'] = elements

        # Parse attributes
        if attributes:
            attributes = copy.copy(attributes)
            for name, attribute in self.attributes:
                if attribute.name:
                    if attribute.qname.text in attributes:
                        value = attributes.pop(attribute.qname.text)
                        init_kwargs[name] = attribute.parse(value)
                else:
                    init_kwargs[name] = attribute.parse(attributes)

        value = self._value_class(**init_kwargs)
        schema_type = schema_type or self
        if schema_type and getattr(schema_type, '_array_type', None):
            value = schema_type._array_class.from_value_object(value)
        return value
Пример #12
0
    def visit_import(self, node, parent):
        """
            <import
              id = ID
              namespace = anyURI
              schemaLocation = anyURI
              {any attributes with non-schema Namespace}...>
            Content: (annotation?)
            </import>
        """
        schema_node = None
        namespace = node.get('namespace')
        location = node.get('schemaLocation')
        if location:
            location = absolute_location(location, self.schema._base_url)

        if not namespace and not self.schema._target_namespace:
            raise XMLParseError(
                "The attribute 'namespace' must be existent if the "
                "importing schema has no target namespace.")

        # Check if the schema is already imported before based on the
        # namespace. Schema's without namespace are registered as 'None'
        schema = self.parser_context.schema_objects.get(namespace)
        if schema:
            if location and schema._location != location:
                # Use same warning message as libxml2
                message = ("Skipping import of schema located at %r " +
                           "for the namespace %r, since the namespace was " +
                           "already imported with the schema located at %r"
                           ) % (location, namespace
                                or '(null)', schema._location)
                warnings.warn(message, ZeepWarning, stacklevel=6)

                return
            logger.debug("Returning existing schema: %r", location)
            self.schema._imports[namespace] = schema
            return schema

        # Silently ignore import statements which we can't resolve via the
        # namespace and doesn't have a schemaLocation attribute.
        if not location:
            logger.debug(
                "Ignoring import statement for namespace %r " +
                "(missing schemaLocation)", namespace)
            return

        # Load the XML
        schema_node = load_external(location, self.schema._transport,
                                    self.parser_context)

        # Check if the xsd:import namespace matches the targetNamespace. If
        # the xsd:import statement didn't specify a namespace then make sure
        # that the targetNamespace wasn't declared by another schema yet.
        schema_tns = schema_node.get('targetNamespace')
        if namespace and schema_tns and namespace != schema_tns:
            raise XMLParseError(
                ("The namespace defined on the xsd:import doesn't match the "
                 "imported targetNamespace located at %r ") % (location))
        elif schema_tns in self.parser_context.schema_objects:
            schema = self.parser_context.schema_objects.get(schema_tns)
            message = ("Skipping import of schema located at %r " +
                       "for the namespace %r, since the namespace was " +
                       "already imported with the schema located at %r") % (
                           location, namespace or '(null)', schema._location)
            warnings.warn(message, ZeepWarning, stacklevel=6)

        # If this schema location is 'internal' then retrieve the original
        # location since that is used as base url for sub include/imports
        if location in self.parser_context.schema_locations:
            base_url = self.parser_context.schema_locations[location]
        else:
            base_url = location

        schema = self.schema.__class__(schema_node, self.schema._transport,
                                       location, self.parser_context, base_url)

        self.schema._imports[namespace] = schema
        return schema
Пример #13
0
    def visit_import(self, node, parent):
        """
            <import
              id = ID
              namespace = anyURI
              schemaLocation = anyURI
              {any attributes with non-schema Namespace}...>
            Content: (annotation?)
            </import>
        """
        schema_node = None
        namespace = node.get('namespace')
        location = node.get('schemaLocation')
        if location:
            location = absolute_location(location, self.document._base_url)

        if not namespace and not self.document._target_namespace:
            raise XMLParseError(
                "The attribute 'namespace' must be existent if the "
                "importing schema has no target namespace.")

        # Check if the schema is already imported before based on the
        # namespace. Schema's without namespace are registered as 'None'
        schema = self.schema._get_schema_document(namespace, fail_silently=True)
        if schema:
            if location and schema._location == location:
                logger.debug("Returning existing schema: %r", location)
                self.document.register_import(namespace, schema)
                return schema
            else:
                # Use same warning message as libxml2
                message = (
                    "Skipping import of schema located at %r " +
                    "for the namespace %r, since the namespace was " +
                    "already imported with the schema located at %r"
                    ) % (location, namespace or '(null)', schema._location)
                warnings.warn(message, ZeepWarning, stacklevel=6)
                return

        # Hardcode the mapping between the xml namespace and the xsd for now.
        # This seems to fix issues with exchange wsdl's, see #220
        if not location and namespace == 'http://www.w3.org/XML/1998/namespace':
            location = 'https://www.w3.org/2001/xml.xsd'

        # Silently ignore import statements which we can't resolve via the
        # namespace and doesn't have a schemaLocation attribute.
        if not location:
            logger.debug(
                "Ignoring import statement for namespace %r " +
                "(missing schemaLocation)", namespace)
            return

        # Load the XML
        schema_node = load_external(location, self.schema._transport)

        # Check if the xsd:import namespace matches the targetNamespace. If
        # the xsd:import statement didn't specify a namespace then make sure
        # that the targetNamespace wasn't declared by another schema yet.
        schema_tns = schema_node.get('targetNamespace')
        if namespace and schema_tns and namespace != schema_tns:
            raise XMLParseError((
                "The namespace defined on the xsd:import doesn't match the "
                "imported targetNamespace located at %r "
                ) % (location))
        elif self.schema._has_schema_document(schema_tns):
            schema = self.schema._get_schema_document(schema_tns)
            message = (
                "Skipping import of schema located at %r " +
                "for the namespace %r, since the namespace was " +
                "already imported with the schema located at %r"
                ) % (location, namespace or '(null)', schema._location)
            warnings.warn(message, ZeepWarning, stacklevel=6)

        schema = self.schema.create_new_document(schema_node, location)
        self.document.register_import(namespace, schema)
        return schema