Ejemplo n.º 1
0
    def bind_element_children(
        cls, params: Dict, meta: XmlMeta, position: int, objects: List,
    ):
        """Return a dictionary of qualified object names and their values for
        the given queue item."""

        while len(objects) > position:
            qname, value = objects.pop(position)
            arg = meta.find_var(qname, FindMode.NOT_WILDCARD) or meta.find_var(
                qname, FindMode.WILDCARD
            )

            if not arg:
                raise ParserError("Impossible exception!")

            if not arg.init:
                continue

            if value is None:
                value = ""

            if not cls.bind_element_param(params, arg, value):
                lookup = QName(value.qname) if isinstance(value, AnyElement) else qname
                wild = cls.find_eligible_wildcard(meta, lookup, params)

                if not wild:
                    logger.warning("Unassigned parsed object %s", qname)
                else:
                    cls.bind_element_wildcard_param(params, wild, qname, value)
Ejemplo n.º 2
0
    def bind_element_wild_text(cls, params: Dict, meta: XmlMeta, element: Element):
        """
        Extract the text and tail content and bind it accordingly in the params
        dictionary.

        - var is a list prepend the text and append the tail.
        - var is present in the params assign the text and tail to the generic object.
        - Otherwise bind the given element to a new generic object.
        """
        var = meta.find_var(mode=FindMode.WILDCARD)
        if not var:
            return

        txt, tail = cls.element_text_and_tail(element)
        if not txt and not tail:
            return

        if var.is_list:
            if var.name not in params:
                params[var.name] = []
            if txt:
                params[var.name].insert(0, txt)
            if tail:
                params[var.name].append(tail)
        elif var.name in params:
            params[var.name].text = txt
            params[var.name].tail = tail
        else:
            params[var.name] = cls.parse_any_element(element, False)
Ejemplo n.º 3
0
    def test_next_node_when_given_qname_matches_dataclass_var(
            self, mock_ctx_fetch, mock_element_xsi_type):
        ele = Element("a")
        ctx = XmlContext()
        cfg = ParserConfig()
        var = XmlElement(name="a",
                         qname=QName("a"),
                         types=[Foo],
                         dataclass=True)
        meta = XmlMeta(
            name="foo",
            clazz=None,
            qname=QName("foo"),
            source_qname=QName("foo"),
            nillable=False,
            vars=[var],
        )
        xsi_type = QName("foo")
        namespace = meta.qname.namespace
        mock_ctx_fetch.return_value = replace(meta)
        mock_element_xsi_type.return_value = xsi_type
        node = ElementNode(position=0, meta=meta, config=cfg)

        actual = node.next_node(ele, 10, ctx)
        self.assertIsInstance(actual, ElementNode)
        self.assertEqual(10, actual.position)
        self.assertIs(mock_ctx_fetch.return_value, actual.meta)
        mock_ctx_fetch.assert_called_once_with(var.clazz, namespace, xsi_type)
Ejemplo n.º 4
0
    def build(self, clazz: Type, parent_ns: Optional[str] = None) -> XmlMeta:
        """Fetch from cache or build the metadata object for the given class
        and parent namespace."""

        if clazz not in self.cache:

            # Ensure the given type is a dataclass.
            if not is_dataclass(clazz):
                raise XmlContextError(f"Object {clazz} is not a dataclass.")

            # Fetch the dataclass meta settings and make sure we don't inherit
            # the parent class meta.
            meta = getattr(clazz, "Meta", None)
            if meta and meta.__qualname__ != f"{clazz.__name__}.Meta":
                meta = None

            name = getattr(meta, "name", self.name_generator(clazz.__name__))
            nillable = getattr(meta, "nillable", False)
            namespace = getattr(meta, "namespace", parent_ns)
            module = sys.modules[clazz.__module__]
            source_namespace = getattr(module, "__NAMESPACE__", None)

            self.cache[clazz] = XmlMeta(
                name=name,
                clazz=clazz,
                qname=QName(namespace, name),
                source_qname=QName(source_namespace, name),
                nillable=nillable,
                vars=list(self.get_type_hints(clazz, namespace)),
            )
        return self.cache[clazz]
Ejemplo n.º 5
0
    def build(self, clazz: Type, parent_ns: Optional[str] = None) -> XmlMeta:
        """
        Fetch from cache or build the binding metadata for the given class and
        parent namespace.

        :param clazz: A dataclass type
        :param parent_ns: The inherited parent namespace
        """

        if clazz not in self.cache:

            # Ensure the given type is a dataclass.
            if not is_dataclass(clazz):
                raise XmlContextError(f"Object {clazz} is not a dataclass.")

            # Fetch the dataclass meta settings and make sure we don't inherit
            # the parent class meta.
            meta = clazz.Meta if "Meta" in clazz.__dict__ else None
            name = getattr(meta, "name", None) or self.local_name(
                clazz.__name__)
            nillable = getattr(meta, "nillable", False)
            namespace = getattr(meta, "namespace", parent_ns)
            module = sys.modules[clazz.__module__]
            source_namespace = getattr(module, "__NAMESPACE__", None)

            self.cache[clazz] = XmlMeta(
                clazz=clazz,
                qname=build_qname(namespace, name),
                source_qname=build_qname(source_namespace, name),
                nillable=nillable,
                vars=list(self.get_type_hints(clazz, namespace)),
            )
        return self.cache[clazz]
Ejemplo n.º 6
0
 def bind_element_text(cls, params: Dict, metadata: XmlMeta, element: Element):
     """Add the given element's text content if any to the params dictionary
     with the text var name as key."""
     var = metadata.find_var(mode=FindMode.TEXT)
     if var and element.text is not None and var.init:
         params[var.name] = cls.parse_value(
             var.types, element.text, var.default, element.nsmap, var.is_tokens
         )
Ejemplo n.º 7
0
    def bind_objects(cls, params: Dict, meta: XmlMeta, position: int,
                     objects: List):
        """Return a dictionary of qualified object names and their values for
        the given queue item."""

        while len(objects) > position:
            qname, value = objects.pop(position)

            arg = meta.find_var(qname, FindMode.ELEMENT)
            if arg and cls.bind_var(params, arg, value):
                continue

            arg = meta.find_var(qname, FindMode.WILDCARD)
            if arg and cls.bind_wild_var(params, arg, qname, value):
                continue

            logger.warning("Unassigned parsed object %s", qname)
Ejemplo n.º 8
0
 def test_fetch(self, mock_build, mock_find_subclass):
     meta = XmlMeta(
         clazz=ItemsType,
         qname="ItemsType",
         source_qname="ItemsType",
         nillable=False,
     )
     mock_build.return_value = meta
     actual = self.ctx.fetch(ItemsType, "foo")
     self.assertEqual(meta, actual)
     self.assertEqual(0, mock_find_subclass.call_count)
     mock_build.assert_called_once_with(ItemsType, "foo")
Ejemplo n.º 9
0
 def setUp(self) -> None:
     super().setUp()
     self.context = XmlContext()
     self.meta = XmlMeta(clazz=Foo, qname="foo", source_qname="foo", nillable=False)
     self.node = ElementNode(
         position=0,
         meta=self.meta,
         context=self.context,
         config=ParserConfig(),
         attrs={},
         ns_map={},
     )
Ejemplo n.º 10
0
    def bind_element_attrs(cls, params: Dict, metadata: XmlMeta, element: Element):
        """Parse the given element's attributes and any text content and return
        a dictionary of field names and values based on the given class
        metadata."""

        if not element.attrib:
            return

        wildcard = metadata.find_var(mode=FindMode.ATTRIBUTES)
        for key, value in element.attrib.items():
            var = metadata.find_var(QName(key), FindMode.ATTRIBUTE)

            if var and var.name not in params:
                if var.init:
                    params[var.name] = cls.parse_value(
                        var.types, value, var.default, element.nsmap, var.is_tokens
                    )
            elif wildcard:
                if wildcard.name not in params:
                    params[wildcard.name] = {}
                params[wildcard.name][key] = value
Ejemplo n.º 11
0
 def test_next_node_when_config_fail_on_unknown_properties_is_false(self):
     ele = Element("nope")
     ctx = XmlContext()
     cfg = ParserConfig(fail_on_unknown_properties=False)
     meta = XmlMeta(
         name="foo",
         clazz=None,
         qname=QName("foo"),
         source_qname=QName("foo"),
         nillable=False,
     )
     node = ElementNode(position=0, meta=meta, config=cfg)
     actual = node.next_node(ele, 10, ctx)
     self.assertEqual(SkipNode(position=10), actual)
Ejemplo n.º 12
0
    def bind_attrs(cls, params: Dict, metadata: XmlMeta, attrs: Dict,
                   ns_map: Dict):
        """Parse the given element's attributes and any text content and return
        a dictionary of field names and values based on the given class
        metadata."""

        if not attrs:
            return

        wildcard = metadata.find_var(mode=FindMode.ATTRIBUTES)
        if wildcard:
            params[wildcard.name] = {}

        for qname, value in attrs.items():
            var = metadata.find_var(qname, FindMode.ATTRIBUTE)
            if var and var.name not in params:
                if var.init:
                    params[var.name] = cls.parse_value(value, var.types,
                                                       var.default, ns_map,
                                                       var.tokens, var.format)
            elif wildcard:
                params[wildcard.name][qname] = cls.parse_any_attribute(
                    value, ns_map)
Ejemplo n.º 13
0
    def test_fetch_with_xsi_type_and_subclass_not_found(
            self, mock_build, mock_find_subclass):
        meta = XmlMeta(
            clazz=ItemsType,
            qname="ItemsType",
            source_qname="ItemsType",
            nillable=False,
        )

        mock_build.return_value = meta
        mock_find_subclass.return_value = None
        actual = self.ctx.fetch(ItemsType, xsi_type="foo")
        self.assertEqual(meta, actual)
        mock_find_subclass.assert_called_once_with(ItemsType, "foo")
Ejemplo n.º 14
0
    def test_fetch_with_xsi_type_and_subclass_found(self, mock_build,
                                                    mock_find_subclass):
        meta = XmlMeta(
            clazz=ItemsType,
            qname="ItemsType",
            source_qname="ItemsType",
            nillable=False,
        )
        xsi_meta = replace(meta, qname="XsiType")

        mock_build.side_effect = [meta, xsi_meta]
        mock_find_subclass.return_value = xsi_meta
        actual = self.ctx.fetch(ItemsType, xsi_type="foo")
        self.assertEqual(xsi_meta, actual)
        mock_find_subclass.assert_called_once_with(ItemsType, "foo")
Ejemplo n.º 15
0
    def test_build_build_vars(self, mock_get_type_hints):
        var = XmlVar(element=True, name="foo", qname="{foo}bar", types=[int])
        mock_get_type_hints.return_value = [var]

        result = self.ctx.build(ItemsType, None)
        expected = XmlMeta(
            clazz=ItemsType,
            qname="ItemsType",
            source_qname="ItemsType",
            nillable=False,
            vars=[var],
        )

        self.assertEqual(expected, result)
        mock_get_type_hints.assert_called_once_with(ItemsType, None)
Ejemplo n.º 16
0
    def test_next_node_when_given_qname_does_not_match_any_var(self):
        ele = Element("nope")
        ctx = XmlContext()
        cfg = ParserConfig()
        meta = XmlMeta(
            name="foo",
            clazz=None,
            qname=QName("foo"),
            source_qname=QName("foo"),
            nillable=False,
        )
        node = ElementNode(position=0, meta=meta, config=cfg)

        with self.assertRaises(XmlContextError) as cm:
            node.next_node(ele, 10, ctx)

        self.assertEqual("foo does not support mixed content: nope",
                         str(cm.exception))
Ejemplo n.º 17
0
    def test_next_node_when_given_qname_matches_primitive_var(self):
        ele = Element("a")
        ctx = XmlContext()
        cfg = ParserConfig()
        var = XmlText(name="a", qname=QName("a"), types=[int], default=100)
        meta = XmlMeta(
            name="foo",
            clazz=None,
            qname=QName("foo"),
            source_qname=QName("foo"),
            nillable=False,
            vars=[var],
        )
        node = ElementNode(position=0, meta=meta, config=cfg)

        actual = node.next_node(ele, 10, ctx)
        self.assertIsInstance(actual, PrimitiveNode)
        self.assertEqual(10, actual.position)
        self.assertEqual(var, actual.var)
Ejemplo n.º 18
0
    def bind_element_wild_text(cls, params: Dict, meta: XmlMeta, element: Element):
        var = meta.find_var(mode=FindMode.WILDCARD)
        if not var:
            return

        txt, tail = cls.element_text_and_tail(element)
        if not txt and not tail:
            return

        if var.is_list:
            if var.name not in params:
                params[var.name] = list()
            if txt:
                params[var.name].insert(0, txt)
            if tail:
                params[var.name].append(tail)
        elif var.name in params:
            params[var.name].text = txt
            params[var.name].tail = tail
        else:
            params[var.name] = cls.parse_any_element(element, False)
Ejemplo n.º 19
0
    def bind_content(
        cls,
        params: Dict,
        metadata: XmlMeta,
        txt: Optional[str],
        ns_map: Dict,
    ) -> bool:
        """
        Add the given element's text content if any to the params dictionary
        with the text var name as key.

        Return if any data was bound.
        """

        if txt is not None:
            var = metadata.find_var(mode=FindMode.TEXT)
            if var and var.init:
                params[var.name] = cls.parse_value(txt, var.types, var.default,
                                                   ns_map, var.tokens)
                return True

        return False
Ejemplo n.º 20
0
    def test_property_element_form(self):
        meta = XmlMeta(
            name="foo",
            clazz=BookForm,
            qname=QName("foo"),
            source_qname=QName("foo"),
            nillable=False,
        )
        self.assertEqual(FormType.UNQUALIFIED, meta.element_form)

        meta = replace(meta, qname=QName("bar", "foo"))
        self.assertEqual(FormType.QUALIFIED, meta.element_form)

        meta.vars.append(XmlElement(name="a", qname=QName("a")))
        self.assertEqual(FormType.UNQUALIFIED, meta.element_form)

        meta.vars.append(
            XmlElement(name="b", qname=QName("b"), namespaces=["bar"]))
        self.assertEqual(FormType.UNQUALIFIED, meta.element_form)

        meta.vars.pop(0)
        self.assertEqual(FormType.QUALIFIED, meta.element_form)
Ejemplo n.º 21
0
    def test_next_node_when_given_qname_matches_any_element_var(self):
        ele = Element("a")
        ctx = XmlContext()
        cfg = ParserConfig()
        var = XmlWildcard(name="a",
                          qname=QName("a"),
                          types=[],
                          dataclass=False)
        meta = XmlMeta(
            name="foo",
            clazz=None,
            qname=QName("foo"),
            source_qname=QName("foo"),
            nillable=False,
            vars=[var],
        )
        node = ElementNode(position=0, meta=meta, config=cfg)

        actual = node.next_node(ele, 10, ctx)
        self.assertIsInstance(actual, WildcardNode)
        self.assertEqual(10, actual.position)
        self.assertEqual(var.qname, actual.qname)
Ejemplo n.º 22
0
    def build(self, clazz: Type, parent_ns: Optional[str] = None) -> XmlMeta:
        if clazz not in self.cache:
            if not is_dataclass(clazz):
                raise XmlContextError(f"Object {clazz} is not a dataclass.")

            meta = getattr(clazz, "Meta", None)
            if meta and meta.__qualname__ != f"{clazz.__name__}.Meta":
                meta = None

            name = getattr(meta, "name", self.name_generator(clazz.__name__))
            nillable = getattr(meta, "nillable", False)
            namespace = getattr(meta, "namespace", parent_ns)
            module = sys.modules[clazz.__module__]
            source_namespace = getattr(module, "__NAMESPACE__", None)

            self.cache[clazz] = XmlMeta(
                name=name,
                clazz=clazz,
                qname=QName(namespace, name),
                source_qname=QName(source_namespace, name),
                nillable=nillable,
                vars=list(self.get_type_hints(clazz, namespace)),
            )
        return self.cache[clazz]
Ejemplo n.º 23
0
 def bind_element_text(cls, params: Dict, metadata: XmlMeta, element: Element):
     var = metadata.find_var(mode=FindMode.TEXT)
     if var and element.text is not None and var.init:
         params[var.name] = cls.parse_value(
             var.types, element.text, var.default, element.nsmap, var.is_tokens
         )