示例#1
0
 def test_next_node_return_self_on_root_element(self):
     ele = Element("foo")
     ctx = XmlContext()
     cfg = ParserConfig()
     meta = ctx.build(Foo)
     node = RootNode(position=0, meta=meta, config=cfg)
     self.assertIs(node, node.next_node(ele, 0, ctx))
示例#2
0
    def test_bind_objects(self):
        @dataclass
        class A:
            x: int
            y: int = field(init=False)
            w: object = field(metadata=dict(type=XmlType.WILDCARD))

        ctx = XmlContext()
        meta = ctx.build(A)
        x = meta.find_var("x")
        y = meta.find_var("y")
        w = meta.find_var("wild")
        wild_element = AnyElement(qname="foo")

        objects = [
            ("foo", 0),
            (x.qname, 1),
            (y.qname, 2),
            (w.qname, None),
            (w.qname, wild_element),
        ]

        params = {}
        ParserUtils.bind_objects(params, meta, 1, objects)
        expected = {
            "x": 1,
            "w": AnyElement(
                children=[AnyElement(qname="w", text=""), AnyElement(qname="foo")]
            ),
        }

        self.assertEqual(expected, params)
示例#3
0
    def test_bind_returns_best_matching_dataclass(self):
        @dataclass
        class Item:
            value: str = field()
            a: int = attribute()
            b: int = attribute()

        @dataclass
        class Item2:
            a: int = attribute()

        @dataclass
        class Root:
            item: Union[str, int, Item2, Item] = element()

        ctx = XmlContext()
        meta = ctx.build(Root)
        var = meta.vars[0]
        attrs = {"a": "1", "b": 2}
        ns_map = {}
        node = UnionNode(position=0, var=var, context=ctx, attrs=attrs, ns_map=ns_map)
        objects = []

        self.assertTrue(node.bind("item", "foo", None, objects))
        self.assertIsInstance(objects[-1][1], Item)
        self.assertEqual(1, objects[-1][1].a)
        self.assertEqual(2, objects[-1][1].b)
        self.assertEqual("foo", objects[-1][1].value)
        self.assertEqual("item", objects[-1][0])

        self.assertEqual(2, len(node.events))
        self.assertEqual(("start", "item", attrs, ns_map), node.events[0])
        self.assertEqual(("end", "item", "foo", None), node.events[1])
        self.assertIsNot(node.attrs, node.events[0][2])
        self.assertIs(node.ns_map, node.events[0][3])
示例#4
0
    def test_find_var(self):
        ctx = XmlContext()
        meta = ctx.build(BookForm)

        self.assertTrue(meta.find_var("author").element)
        self.assertIsNone(meta.find_var("author", FindMode.ATTRIBUTE))
        self.assertIsNone(meta.find_var("nope"))
示例#5
0
    def test_find_var_uses_cache(self):
        ctx = XmlContext()
        meta = ctx.build(BookForm)

        self.assertEqual("author", meta.find_var("author").name)
        self.assertEqual(1, len(meta.cache))
        key = tuple(meta.cache.keys())[0]

        meta.cache[key] = 1
        self.assertEqual("title", meta.find_var("author").name)
示例#6
0
    def test_find_var(self):
        ctx = XmlContext()
        meta = ctx.build(BookForm)
        author = QName("author")
        excluded = set()
        excluded.add("author")

        self.assertIsInstance(meta.find_var(author), XmlElement)
        self.assertIsNone(meta.find_var(author, FindMode.ATTRIBUTE))
        self.assertIsNone(meta.find_var(QName("nope")))
示例#7
0
    def test_next_node_return_next_node(self):
        root = Element("a")
        ele = SubElement(root, "b")

        ctx = XmlContext()
        cfg = ParserConfig()
        meta = ctx.build(Foo)
        node = RootNode(position=0, meta=meta, config=cfg)
        actual = node.next_node(ele, 0, ctx)

        self.assertIsInstance(actual, PrimitiveNode)
        self.assertIsNot(actual, node)
示例#8
0
    def test_bind_elements(
        self,
        mock_bind_element_param,
        mock_find_eligible_wildcard,
        mock_bind_element_wildcard_param,
    ):
        @dataclass
        class A:
            x: int
            y: int = field(init=False)
            w: object = field(metadata=dict(type=XmlType.WILDCARD))

        ctx = XmlContext()
        meta = ctx.build(A)
        x = meta.find_var(QName("x"))
        y = meta.find_var(QName("y"))
        w = meta.find_var(QName("wild"))
        wild_element = AnyElement(qname="foo")

        objects = [
            ("foo", 0),
            (x.qname, 1),
            (y.qname, 2),
            (w.qname, None),
            (w.qname, wild_element),
        ]

        mock_bind_element_param.side_effect = [True, False, False]
        mock_find_eligible_wildcard.side_effect = [None, w]

        params = {}
        ParserUtils.bind_element_children(params, meta, 1, objects)

        mock_bind_element_param.assert_has_calls(
            [
                mock.call(params, x, 1),
                mock.call(params, w, ""),
                mock.call(params, w, wild_element),
            ]
        )
        mock_find_eligible_wildcard.assert_has_calls(
            [mock.call(meta, w.qname, params), mock.call(meta, QName("foo"), params)]
        )

        mock_bind_element_wildcard_param.assert_called_once_with(
            params, w, w.qname, wild_element
        )
示例#9
0
    def test_parse_element(
        self,
        mock_bind_element_attrs,
        mock_bind_element_text,
        mock_bind_element_children,
        mock_bind_element_wild_text,
    ):
        def add_attr(x, *args):
            x["a"] = 1

        def add_text(x, *args):
            x["b"] = 2

        def add_child(x, *args):
            x["c"] = 3

        def add_wild_text(x, *args):
            x["d"] = 4

        mock_bind_element_attrs.side_effect = add_attr
        mock_bind_element_text.side_effect = add_text
        mock_bind_element_children.side_effect = add_child
        mock_bind_element_wild_text.side_effect = add_wild_text

        ctx = XmlContext()
        meta = ctx.build(Foo)

        ele = Element("foo")
        pool = [1, 2, 3]

        node = ElementNode(position=0,
                           meta=meta,
                           default=None,
                           config=ParserConfig())
        qname, obj = node.parse_element(ele, pool)

        self.assertEqual(QName(ele.tag), qname)
        self.assertEqual(Foo(1, 2, 3, 4), obj)

        mock_bind_element_attrs.assert_called_once_with(mock.ANY, meta, ele)
        mock_bind_element_text.assert_called_once_with(mock.ANY, meta, ele)
        mock_bind_element_children.assert_called_once_with(
            mock.ANY, meta, 0, pool)
        mock_bind_element_wild_text.assert_called_once_with(
            mock.ANY, meta, ele)
示例#10
0
    def test_bind_raises_parser_error_on_failure(self):
        @dataclass
        class Item:
            value: str = field()

        @dataclass
        class Root:
            item: Union[int, Item] = element()

        ctx = XmlContext()
        meta = ctx.build(Root)
        meta.vars[0]

        node = UnionNode(position=0, var=meta.vars[0], context=ctx, attrs={}, ns_map={})

        with self.assertRaises(ParserError) as cm:
            node.bind("item", None, None, [])

        self.assertEqual("Failed to parse union node: item", str(cm.exception))
示例#11
0
class XmlContextTests(TestCase):
    def setUp(self):
        self.ctx = XmlContext()
        super().setUp()

    @mock.patch.object(XmlContext, "find_subclass")
    @mock.patch.object(XmlContext, "build")
    def test_fetch(self, mock_build, mock_find_subclass):
        meta = XmlMeta(
            name="ItemsType",
            clazz=ItemsType,
            qname=QName("ItemsType"),
            source_qname=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")

    @mock.patch.object(XmlContext, "find_subclass")
    @mock.patch.object(XmlContext, "build")
    def test_fetch_with_xsi_type_and_subclass_not_found(
        self, mock_build, mock_find_subclass
    ):
        meta = XmlMeta(
            name="ItemsType",
            clazz=ItemsType,
            qname=QName("ItemsType"),
            source_qname=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")

    @mock.patch.object(XmlContext, "find_subclass")
    @mock.patch.object(XmlContext, "build")
    def test_fetch_with_xsi_type_and_subclass_found(
        self, mock_build, mock_find_subclass
    ):
        meta = XmlMeta(
            name="ItemsType",
            clazz=ItemsType,
            qname=QName("ItemsType"),
            source_qname=QName("ItemsType"),
            nillable=False,
        )
        xsi_meta = replace(meta, name="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")

    def test_find_subclass(self):
        a = make_dataclass("A", fields=[])
        b = make_dataclass("B", fields=[], bases=(a,))
        c = make_dataclass("C", fields=[], bases=(a,))

        self.assertEqual(b, self.ctx.find_subclass(a, "B"))
        self.assertEqual(b, self.ctx.find_subclass(c, "B"))
        self.assertEqual(a, self.ctx.find_subclass(b, "A"))
        self.assertEqual(a, self.ctx.find_subclass(c, "A"))
        self.assertIsNone(self.ctx.find_subclass(c, "What"))

    def test_match_class_name(self):
        qname_foo = QName("qname_foo")
        qname_items = QName("ItemsType")
        qname_product = QName("http://datypic.com/prod", "product")
        qname_object = QName("object")
        qname_int = QName("int")

        # no meta name
        self.assertFalse(self.ctx.match_class_source_qname(ItemsType, qname_foo))
        self.assertTrue(self.ctx.match_class_source_qname(ItemsType, qname_items))

        # with meta name
        self.assertFalse(self.ctx.match_class_source_qname(Product, qname_items))
        self.assertTrue(self.ctx.match_class_source_qname(Product, qname_product))

        # not dataclass
        self.assertFalse(self.ctx.match_class_source_qname(object, qname_object))
        self.assertFalse(self.ctx.match_class_source_qname(int, qname_int))

    @mock.patch.object(XmlContext, "get_type_hints")
    def test_build_build_vars(self, mock_get_type_hints):
        var = XmlElement(name="foo", qname=QName("foo", "bar"), types=[int])
        mock_get_type_hints.return_value = [var]

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

        self.assertEqual(expected, result)
        mock_get_type_hints.assert_called_once_with(ItemsType, None)

    @mock.patch.object(XmlContext, "get_type_hints", return_value=dict())
    def test_build_with_meta_namespace(self, mock_get_type_hints):
        namespace = Product.Meta.namespace
        result = self.ctx.build(Product, None)

        self.assertEqual(QName(namespace, "product"), result.qname)
        self.assertEqual(QName(namespace, "product"), result.source_qname)
        mock_get_type_hints.assert_called_once_with(Product, namespace)

    @mock.patch.object(XmlContext, "get_type_hints", return_value=dict())
    def test_build_with_parent_ns(self, mock_get_type_hints):
        result = self.ctx.build(ProductType, "http://xsdata")

        self.assertEqual(QName("http://xsdata", "ProductType"), str(result.qname))
        mock_get_type_hints.assert_called_once_with(ProductType, "http://xsdata")

    @mock.patch.object(XmlContext, "get_type_hints", return_value=dict())
    def test_build_with_no_meta_name_and_name_generator(self, *args):
        inspect = XmlContext(name_generator=lambda x: text.snake_case(x))
        result = inspect.build(ItemsType)

        self.assertEqual(QName("items_type"), str(result.qname))

    def test_build_with_no_meta_not_inherit_from_parent(self):
        @dataclass
        class Bar:
            class Meta:
                name = "bar"

        @dataclass
        class Foo(Bar):
            pass

        result = self.ctx.build(Foo)
        self.assertEqual("Foo", result.name)
        self.assertIsNone(result.qname.namespace)

    @mock.patch.object(XmlContext, "get_type_hints", return_value=dict())
    def test_build_with_no_dataclass_raises_exception(self, *args):
        with self.assertRaises(XmlContextError) as cm:
            self.ctx.build(int)

        self.assertEqual(f"Object {int} is not a dataclass.", str(cm.exception))

    def test_get_type_hints(self):
        result = self.ctx.get_type_hints(BookForm, None)
        self.assertIsInstance(result, Iterator)

        expected = [
            XmlElement(name="author", qname=QName("author"), types=[str],),
            XmlElement(name="title", qname=QName("title"), types=[str]),
            XmlElement(name="genre", qname=QName("genre"), types=[str]),
            XmlElement(name="price", qname=QName("price"), types=[float],),
            XmlElement(name="pub_date", qname=QName("pub_date"), types=[str],),
            XmlElement(name="review", qname=QName("review"), types=[str],),
            XmlAttribute(name="id", qname=QName("id"), types=[str]),
            XmlAttribute(
                name="lang", qname=QName("lang"), types=[str], init=False, default="en",
            ),
        ]

        result = list(result)
        self.assertEqual(expected, result)
        for var in result:
            self.assertFalse(var.dataclass)
            self.assertIsNone(var.clazz)

    def test_get_type_hints_with_dataclass_list(self):
        result = list(self.ctx.get_type_hints(Books, None))

        expected = XmlElement(
            name="book",
            qname=QName("book"),
            types=[BookForm],
            dataclass=True,
            default=list,
        )

        self.assertTrue(expected.is_list)
        self.assertEqual(1, len(result))
        self.assertEqual(expected, result[0])
        self.assertTrue(result[0].dataclass)
        self.assertEqual(BookForm, result[0].clazz)

    def test_get_type_hints_with_wildcard_element(self):
        result = list(self.ctx.get_type_hints(TextType, None))

        expected = XmlWildcard(
            name="any_element",
            qname=QName(None, "any_element"),
            types=[object],
            init=True,
            nillable=False,
            dataclass=False,
            default=list,
            namespaces=["##any"],
        )

        self.assertEqual(2, len(result))
        self.assertEqual(expected, result[0])

    def test_get_type_hints_with_no_dataclass(self):
        with self.assertRaises(TypeError):
            list(self.ctx.get_type_hints(self.__class__, None))

    def test_resolve_namespaces(self):
        self.assertEqual(
            ["foo"], self.ctx.resolve_namespaces(XmlType.ELEMENT, "foo", "bar")
        )

        self.assertEqual([], self.ctx.resolve_namespaces(XmlType.ELEMENT, "", "bar"))

        self.assertEqual(
            ["bar"], self.ctx.resolve_namespaces(XmlType.ELEMENT, None, "bar")
        )

        self.assertEqual(
            [], self.ctx.resolve_namespaces(XmlType.ATTRIBUTE, None, "bar")
        )

        self.assertEqual(
            ["p"], self.ctx.resolve_namespaces(XmlType.WILDCARD, None, "p")
        )

        self.assertEqual(
            ["##any"], self.ctx.resolve_namespaces(XmlType.WILDCARD, "##any", "p")
        )

        self.assertEqual(
            ["##any"],
            self.ctx.resolve_namespaces(XmlType.WILDCARD, "##targetNamespace", ""),
        )

        self.assertEqual(
            ["##any"],
            self.ctx.resolve_namespaces(XmlType.WILDCARD, "##targetNamespace", None),
        )

        self.assertEqual(
            ["p"],
            self.ctx.resolve_namespaces(XmlType.WILDCARD, "##targetNamespace", "p"),
        )

        self.assertEqual(
            [""], self.ctx.resolve_namespaces(XmlType.WILDCARD, "##local", "p")
        )

        self.assertEqual(
            ["!p"], self.ctx.resolve_namespaces(XmlType.WILDCARD, "##other", "p")
        )

        self.assertEqual(
            ["", "!p"],
            sorted(
                self.ctx.resolve_namespaces(XmlType.WILDCARD, "##other   ##local", "p")
            ),
        )

        self.assertEqual(
            ["foo", "p"],
            sorted(
                self.ctx.resolve_namespaces(
                    XmlType.WILDCARD, "##targetNamespace   foo", "p"
                )
            ),
        )

    def test_is_derived(self):
        a = make_dataclass("A", fields=[])
        b = make_dataclass("B", fields=[], bases=(a,))
        c = make_dataclass("C", fields=[], bases=(a,))
        d = make_dataclass("D", fields=[])

        self.assertTrue(self.ctx.is_derived(c(), b))
        self.assertTrue(self.ctx.is_derived(b(), c))
        self.assertTrue(self.ctx.is_derived(a(), b))
        self.assertTrue(self.ctx.is_derived(a(), c))
        self.assertTrue(self.ctx.is_derived(a(), a))
        self.assertFalse(self.ctx.is_derived(a(), d))
示例#12
0
    def test_build_with_no_meta_name_and_name_generator(self, *args):
        inspect = XmlContext(name_generator=lambda x: text.snake_case(x))
        result = inspect.build(ItemsType)

        self.assertEqual(QName("items_type"), str(result.qname))
示例#13
0
class XmlContextTests(TestCase):
    def setUp(self):
        self.ctx = XmlContext()
        super().setUp()

    @mock.patch.object(XmlContext, "find_subclass")
    @mock.patch.object(XmlContext, "build")
    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")

    @mock.patch.object(XmlContext, "find_subclass")
    @mock.patch.object(XmlContext, "build")
    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")

    @mock.patch.object(XmlContext, "find_subclass")
    @mock.patch.object(XmlContext, "build")
    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")

    def test_find(self):
        self.assertIsNone(self.ctx.find_type(str(DataType.FLOAT)))
        self.assertEqual(BookForm, self.ctx.find_type("{urn:books}BookForm"))

        self.ctx.xsi_cache["{urn:books}BookForm"].append(BooksForm)
        self.assertEqual(BooksForm, self.ctx.find_type("{urn:books}BookForm"))

    def test_find_type_by_fields(self):
        field_names = {f.name for f in fields(BookForm)}
        self.assertEqual(BookForm, self.ctx.find_type_by_fields(field_names))
        self.assertIsNone(
            self.ctx.find_type_by_fields({"please", "dont", "exist"}))

    def test_find_subclass(self):
        a = make_dataclass("A", fields=[])
        b = make_dataclass("B", fields=[], bases=(a, ))
        c = make_dataclass("C", fields=[], bases=(a, ))
        other = make_dataclass("Other", fields=[])

        self.assertEqual(b, self.ctx.find_subclass(a, "B"))
        self.assertEqual(b, self.ctx.find_subclass(c, "B"))
        self.assertEqual(a, self.ctx.find_subclass(b, "A"))
        self.assertEqual(a, self.ctx.find_subclass(c, "A"))
        self.assertIsNone(self.ctx.find_subclass(c, "Unknown"))
        self.assertIsNone(self.ctx.find_subclass(c, "Other"))

    @mock.patch.object(XmlContext, "get_type_hints")
    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)

    @mock.patch.object(XmlContext, "get_type_hints", return_value={})
    def test_build_with_meta_namespace(self, mock_get_type_hints):
        namespace = Product.Meta.namespace
        result = self.ctx.build(Product, None)

        self.assertEqual(build_qname(namespace, "product"), result.qname)
        self.assertEqual(build_qname(namespace, "product"),
                         result.source_qname)
        mock_get_type_hints.assert_called_once_with(Product, namespace)

    @mock.patch.object(XmlContext, "get_type_hints", return_value={})
    def test_build_with_parent_ns(self, mock_get_type_hints):
        result = self.ctx.build(ProductType, "http://xsdata")

        self.assertEqual(build_qname("http://xsdata", "ProductType"),
                         str(result.qname))
        mock_get_type_hints.assert_called_once_with(ProductType,
                                                    "http://xsdata")

    @mock.patch.object(XmlContext, "get_type_hints", return_value={})
    def test_build_with_no_meta_name_and_name_generator(self, *args):
        inspect = XmlContext(element_name=lambda x: text.snake_case(x))
        result = inspect.build(ItemsType)

        self.assertEqual("items_type", result.qname)

    def test_build_with_no_meta_not_inherit_from_parent(self):
        @dataclass
        class Bar:
            class Meta:
                name = "bar"

        @dataclass
        class Foo(Bar):
            pass

        @dataclass
        class Thug(Bar):
            class Meta:
                name = "thug"

        result = self.ctx.build(Foo)
        self.assertEqual("Foo", result.qname)

        result = self.ctx.build(Thug)
        self.assertEqual("thug", result.qname)

    @mock.patch.object(XmlContext, "get_type_hints", return_value={})
    def test_build_with_no_dataclass_raises_exception(self, *args):
        with self.assertRaises(XmlContextError) as cm:
            self.ctx.build(int)

        self.assertEqual(f"Object {int} is not a dataclass.",
                         str(cm.exception))

    def test_get_type_hints(self):
        result = self.ctx.get_type_hints(BookForm, None)
        self.assertIsInstance(result, Iterator)

        expected = [
            XmlVar(element=True, name="author", qname="author", types=[str]),
            XmlVar(element=True, name="title", qname="title", types=[str]),
            XmlVar(element=True, name="genre", qname="genre", types=[str]),
            XmlVar(element=True, name="price", qname="price", types=[float]),
            XmlVar(element=True,
                   name="pub_date",
                   qname="pub_date",
                   types=[str]),
            XmlVar(element=True, name="review", qname="review", types=[str]),
            XmlVar(attribute=True, name="id", qname="id", types=[str]),
            XmlVar(
                attribute=True,
                name="lang",
                qname="lang",
                types=[str],
                init=False,
                default="en",
            ),
        ]

        result = list(result)
        self.assertEqual(expected, result)
        for var in result:
            self.assertFalse(var.dataclass)
            self.assertIsNone(var.clazz)

    def test_get_type_hints_with_union_types(self):
        @dataclass
        class Example:
            bool: bool
            int: int
            union: Union[int, bool]

        result = list(self.ctx.get_type_hints(Example, None))
        expected = [
            XmlVar(element=True, name="bool", qname="bool", types=[bool]),
            XmlVar(element=True, name="int", qname="int", types=[int]),
            XmlVar(element=True,
                   name="union",
                   qname="union",
                   types=[bool, int]),
        ]

        if sys.version_info < (3, 7):
            expected[2].types.remove(bool)

        self.assertEqual(expected, result)

    def test_get_type_hints_with_dataclass_list(self):
        result = list(self.ctx.get_type_hints(Books, None))

        expected = XmlVar(
            element=True,
            name="book",
            qname="book",
            types=[BookForm],
            dataclass=True,
            default=list,
            list_element=True,
        )

        self.assertTrue(expected.list_element)
        self.assertEqual(1, len(result))
        self.assertEqual(expected, result[0])
        self.assertTrue(result[0].dataclass)
        self.assertEqual(BookForm, result[0].clazz)

    def test_get_type_hints_with_wildcard_element(self):
        result = list(self.ctx.get_type_hints(Umbrella, None))

        expected = XmlVar(
            wildcard=True,
            name="any_element",
            qname="any_element",
            types=[object],
            default=None,
            namespaces=["##any"],
        )

        self.assertEqual(1, len(result))
        self.assertEqual(expected, result[0])

    def test_get_type_hints_with_undefined_types(self):
        @dataclass
        class Currency:
            id: int = field(metadata=dict(type="Attribute", name="ID"))
            iso_code: str = field(metadata=dict(name="CharCode"))
            nominal: int = field(metadata=dict(name="Nominal"))

        @dataclass
        class Currencies:
            name: str = field(metadata=dict(type="Attribute"))
            values: List[Currency] = field(default_factory=list)

        expected = [
            XmlVar(attribute=True, name="id", qname="ID", types=[int]),
            XmlVar(element=True,
                   name="iso_code",
                   qname="CharCode",
                   types=[str]),
            XmlVar(element=True, name="nominal", qname="Nominal", types=[int]),
        ]
        self.assertEqual(expected,
                         list(self.ctx.get_type_hints(Currency, None)))

        expected = [
            XmlVar(attribute=True, name="name", qname="name", types=[str]),
            XmlVar(
                element=True,
                name="values",
                qname="values",
                dataclass=True,
                list_element=True,
                default=list,
                types=[Currency],
            ),
        ]
        self.assertEqual(expected,
                         list(self.ctx.get_type_hints(Currencies, None)))

    def test_get_type_hints_with_choices(self):
        actual = self.ctx.get_type_hints(Node, "bar")
        self.assertIsInstance(actual, Generator)
        expected = XmlVar(
            elements=True,
            name="compound",
            qname="compound",
            list_element=True,
            any_type=True,
            default=list,
            choices=[
                XmlVar(
                    element=True,
                    name="compound",
                    qname="{foo}node",
                    dataclass=True,
                    types=[Node],
                    namespaces=["foo"],
                    derived=False,
                ),
                XmlVar(
                    element=True,
                    name="compound",
                    qname="{bar}x",
                    tokens=True,
                    types=[str],
                    namespaces=["bar"],
                    derived=False,
                    default=return_true,
                ),
                XmlVar(
                    element=True,
                    name="compound",
                    qname="{bar}y",
                    nillable=True,
                    types=[int],
                    namespaces=["bar"],
                    derived=False,
                ),
                XmlVar(
                    element=True,
                    name="compound",
                    qname="{bar}z",
                    nillable=False,
                    types=[int],
                    namespaces=["bar"],
                    derived=True,
                ),
                XmlVar(
                    element=True,
                    name="compound",
                    qname="{bar}o",
                    nillable=False,
                    types=[object],
                    namespaces=["bar"],
                    derived=True,
                    any_type=True,
                ),
                XmlVar(
                    element=True,
                    name="compound",
                    qname="{bar}p",
                    types=[float],
                    namespaces=["bar"],
                    default=1.1,
                ),
                XmlVar(
                    wildcard=True,
                    name="compound",
                    qname="{http://www.w3.org/1999/xhtml}any",
                    types=[object],
                    namespaces=["http://www.w3.org/1999/xhtml"],
                    derived=True,
                    any_type=False,
                ),
            ],
            types=[object],
        )
        self.assertEqual(expected, list(actual)[0])

    def test_get_type_hints_with_no_dataclass(self):
        with self.assertRaises(TypeError):
            list(self.ctx.get_type_hints(self.__class__, None))

    def test_resolve_namespaces(self):
        self.assertEqual(["foo"],
                         self.ctx.resolve_namespaces(XmlType.ELEMENT, "foo",
                                                     "bar"))

        self.assertEqual([],
                         self.ctx.resolve_namespaces(XmlType.ELEMENT, "",
                                                     "bar"))

        self.assertEqual(["bar"],
                         self.ctx.resolve_namespaces(XmlType.ELEMENT, None,
                                                     "bar"))

        self.assertEqual([],
                         self.ctx.resolve_namespaces(XmlType.ATTRIBUTE, None,
                                                     "bar"))

        self.assertEqual(["p"],
                         self.ctx.resolve_namespaces(XmlType.WILDCARD, None,
                                                     "p"))

        self.assertEqual(["##any"],
                         self.ctx.resolve_namespaces(XmlType.WILDCARD, "##any",
                                                     "p"))

        self.assertEqual(
            ["##any"],
            self.ctx.resolve_namespaces(XmlType.WILDCARD, "##targetNamespace",
                                        ""),
        )

        self.assertEqual(
            ["##any"],
            self.ctx.resolve_namespaces(XmlType.WILDCARD, "##targetNamespace",
                                        None),
        )

        self.assertEqual(
            ["p"],
            self.ctx.resolve_namespaces(XmlType.WILDCARD, "##targetNamespace",
                                        "p"),
        )

        self.assertEqual([""],
                         self.ctx.resolve_namespaces(XmlType.WILDCARD,
                                                     "##local", "p"))

        self.assertEqual(["!p"],
                         self.ctx.resolve_namespaces(XmlType.WILDCARD,
                                                     "##other", "p"))

        self.assertEqual(
            ["", "!p"],
            sorted(
                self.ctx.resolve_namespaces(XmlType.WILDCARD,
                                            "##other   ##local", "p")),
        )

        self.assertEqual(
            ["foo", "p"],
            sorted(
                self.ctx.resolve_namespaces(XmlType.WILDCARD,
                                            "##targetNamespace   foo", "p")),
        )

    def test_is_derived(self):
        a = make_dataclass("A", fields=[])
        b = make_dataclass("B", fields=[], bases=(a, ))
        c = make_dataclass("C", fields=[], bases=(a, ))
        d = make_dataclass("D", fields=[])

        self.assertTrue(self.ctx.is_derived(c(), b))
        self.assertTrue(self.ctx.is_derived(b(), c))
        self.assertTrue(self.ctx.is_derived(a(), b))
        self.assertTrue(self.ctx.is_derived(a(), c))
        self.assertTrue(self.ctx.is_derived(a(), a))
        self.assertFalse(self.ctx.is_derived(a(), d))
        self.assertFalse(self.ctx.is_derived(None, d))

    def test_is_element_list(self):
        @dataclass
        class Fixture:
            list_int: List[int]
            list_list_int: List[List[int]]

        type_hints = get_type_hints(Fixture)

        self.assertTrue(self.ctx.is_element_list(type_hints["list_int"],
                                                 False))
        self.assertFalse(self.ctx.is_element_list(type_hints["list_int"],
                                                  True))

        self.assertTrue(
            self.ctx.is_element_list(type_hints["list_list_int"], False))
        self.assertTrue(
            self.ctx.is_element_list(type_hints["list_list_int"], True))

    def test_default_xml_type(self):
        cls = make_dataclass("a", [("x", int)])
        self.assertEqual(XmlType.TEXT, self.ctx.default_xml_type(cls))

        cls = make_dataclass("b", [("x", int), ("y", int)])
        self.assertEqual(XmlType.ELEMENT, self.ctx.default_xml_type(cls))

        cls = make_dataclass("c",
                             [("x", int),
                              ("y", int, field(metadata=dict(type="Text")))])
        self.assertEqual(XmlType.ELEMENT, self.ctx.default_xml_type(cls))

        cls = make_dataclass(
            "d", [("x", int),
                  ("y", int, field(metadata=dict(type="Element")))])
        self.assertEqual(XmlType.TEXT, self.ctx.default_xml_type(cls))

        with self.assertRaises(XmlContextError) as cm:
            cls = make_dataclass(
                "e",
                [
                    ("x", int, field(metadata=dict(type="Text"))),
                    ("y", int, field(metadata=dict(type="Text"))),
                ],
            )
            self.ctx.default_xml_type(cls)

        self.assertEqual("Dataclass `e` includes more than one text node!",
                         str(cm.exception))
示例#14
0
class ElementNodeTests(TestCase):
    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={},
        )

    @mock.patch.object(ParserUtils, "bind_objects")
    @mock.patch.object(ParserUtils, "bind_wild_content")
    @mock.patch.object(ParserUtils, "bind_content")
    @mock.patch.object(ParserUtils, "bind_attrs")
    def test_bind(
        self,
        mock_bind_attrs,
        mock_bind_content,
        mock_bind_wild_content,
        mock_bind_objects,
    ):
        mock_bind_attrs.side_effect = add_attr
        mock_bind_content.side_effect = add_text
        mock_bind_objects.side_effect = add_child

        node = ElementNode(
            position=0,
            meta=self.context.build(Foo),
            context=self.context,
            config=ParserConfig(),
            attrs={"a": "b"},
            ns_map={"ns0": "xsdata"},
        )

        objects = [1, 2, 3]

        self.assertTrue(node.bind("foo", "text", "tail", objects))
        self.assertEqual("foo", objects[-1][0])
        self.assertEqual(Foo(1, 2, 3), objects[-1][1])

        mock_bind_attrs.assert_called_once_with(
            mock.ANY, node.meta, node.attrs, node.ns_map
        )
        mock_bind_content.assert_called_once_with(
            mock.ANY, node.meta, "text", node.ns_map
        )
        mock_bind_objects.assert_called_once_with(mock.ANY, node.meta, 0, objects)
        self.assertEqual(0, mock_bind_wild_content.call_count)

    def test_bind_with_derived_element(self):
        a = make_dataclass("A", fields=[])
        node = ElementNode(
            position=0,
            meta=self.context.build(a),
            context=self.context,
            config=ParserConfig(),
            attrs={},
            ns_map={},
            derived=True,
        )

        objects = []

        self.assertTrue(node.bind("foo", None, None, objects))
        self.assertEqual("foo", objects[-1][0])
        self.assertEqual(DerivedElement("foo", a()), objects[-1][1])

    @mock.patch.object(XmlMeta, "find_var")
    @mock.patch.object(ParserUtils, "bind_objects")
    @mock.patch.object(ParserUtils, "bind_wild_content")
    @mock.patch.object(ParserUtils, "bind_content")
    @mock.patch.object(ParserUtils, "bind_attrs")
    def test_bind_with_wildcard_var(
        self,
        mock_bind_attrs,
        mock_bind_content,
        mock_bind_wild_content,
        mock_bind_objects,
        mock_find_var,
    ):
        mock_bind_attrs.side_effect = add_attr
        mock_bind_content.return_value = False
        mock_bind_wild_content.side_effect = add_text
        mock_bind_objects.side_effect = add_child
        mock_find_var.return_value = XmlVar(wildcard=True, qname="b", name="b")

        node = ElementNode(
            position=0,
            meta=self.context.build(Foo),
            context=self.context,
            config=ParserConfig(),
            attrs={"a": "b"},
            ns_map={"ns0": "xsdata"},
        )

        objects = [1, 2, 3]

        self.assertTrue(node.bind("foo", "text", "tail", objects))
        self.assertEqual("foo", objects[-1][0])
        self.assertEqual(Foo(1, 2, 3), objects[-1][1])

        mock_bind_attrs.assert_called_once_with(
            mock.ANY, node.meta, node.attrs, node.ns_map
        )
        mock_bind_content.assert_called_once_with(
            mock.ANY, node.meta, "text", node.ns_map
        )
        mock_bind_objects.assert_called_once_with(mock.ANY, node.meta, 0, objects)

    @mock.patch.object(ParserUtils, "bind_objects")
    @mock.patch.object(ParserUtils, "bind_content")
    @mock.patch.object(ParserUtils, "bind_attrs")
    def test_bind_with_mixed_flag_true(
        self, mock_bind_attrs, mock_bind_content, mock_bind_objects
    ):
        mock_bind_attrs.side_effect = add_attr
        mock_bind_content.side_effect = add_text
        mock_bind_objects.side_effect = add_child

        node = ElementNode(
            position=0,
            meta=self.context.build(Foo),
            context=self.context,
            config=ParserConfig(),
            attrs={"a": "b"},
            ns_map={"ns0": "xsdata"},
            mixed=True,
        )

        objects = []
        self.assertTrue(node.bind("foo", "text", "   ", objects))
        self.assertEqual(1, len(objects))

        objects = []
        self.assertTrue(node.bind("foo", "text", " tail ", objects))
        self.assertEqual(2, len(objects))
        self.assertEqual(None, objects[-1][0])
        self.assertEqual(" tail ", objects[-1][1])

    @mock.patch.object(ParserUtils, "bind_mixed_objects")
    @mock.patch.object(ParserUtils, "bind_wild_content")
    @mock.patch.object(ParserUtils, "bind_attrs")
    def test_bind_with_mixed_content_var(
        self,
        mock_bind_attrs,
        mock_bind_wild_content,
        mock_bind_mixed_objects,
    ):
        mock_bind_attrs.side_effect = add_attr
        mock_bind_wild_content.side_effect = add_text
        mock_bind_mixed_objects.side_effect = add_content

        node = ElementNode(
            position=0,
            meta=self.context.build(FooMixed),
            context=self.context,
            config=ParserConfig(),
            attrs={"a": "b"},
            ns_map={"ns0": "xsdata"},
        )
        objects = [1, 2, 3]

        self.assertTrue(node.bind("foo", "text", "tail", objects))

        self.assertEqual("foo", objects[-1][0])
        self.assertEqual(FooMixed(1, 2, 3), objects[-1][1])

        mock_bind_attrs.assert_called_once_with(
            mock.ANY, node.meta, node.attrs, node.ns_map
        )
        mock_bind_wild_content.assert_called_once_with(
            mock.ANY, node.meta.vars[2], "text", "tail", node.attrs, node.ns_map
        )
        mock_bind_mixed_objects.assert_called_once_with(
            mock.ANY, node.meta.vars[2], 0, objects
        )

    def test_fetch_vars(self):
        elem = XmlVar(element=True, name="a", qname="a", types=[Foo], dataclass=True)
        wild = XmlVar(wildcard=True, name="a", qname="a", types=[Foo], dataclass=True)
        self.meta.vars.extend((wild, elem))

        matching_vars = self.node.fetch_vars("a")
        self.assertIsInstance(matching_vars, Generator)
        self.assertEqual([(id(elem), elem), (None, wild)], list(matching_vars))

    def test_fetch_vars_with_elements_var(self):
        elem = XmlVar(element=True, name="a", qname="a", types=[Foo], dataclass=True)
        elems = XmlVar(elements=True, name="compound", qname="compound", choices=[elem])
        self.meta.vars.append(elems)

        matching_vars = self.node.fetch_vars("a")
        self.assertIsInstance(matching_vars, Generator)
        self.assertEqual((None, elem), next(matching_vars))

    @mock.patch.object(ElementNode, "fetch_vars")
    def test_child(self, mock_match_vars):
        var = XmlVar(element=True, name="a", qname="a", types=[Foo], dataclass=True)
        attrs = {"a": "b"}
        ns_map = {"ns0": "xsdata"}
        position = 1
        mock_match_vars.return_value = [(id(var), var)]

        actual = self.node.child("a", attrs, ns_map, position)
        self.assertIsInstance(actual, ElementNode)
        self.assertEqual(attrs, actual.attrs)
        self.assertEqual(ns_map, actual.ns_map)
        self.assertEqual(position, actual.position)

    def test_child_unique_vars(self):
        single = XmlVar(element=True, name="a", qname="a", types=[Foo], dataclass=True)
        wildcard = XmlVar(wildcard=True, name="a", qname="a", types=[object])

        self.meta.vars.append(single)
        self.meta.vars.append(wildcard)

        attrs = {"a": "b"}
        ns_map = {"ns0": "xsdata"}
        position = 1

        actual = self.node.child("a", attrs, ns_map, position)
        self.assertIsInstance(actual, ElementNode)
        self.assertIn(id(single), self.node.assigned)

        actual = self.node.child("a", attrs, ns_map, position)
        self.assertIsInstance(actual, WildcardNode)
        self.assertNotIn(id(wildcard), self.node.assigned)

    @mock.patch.object(ElementNode, "build_node")
    def test_child_when_failed_to_build_next_node(self, mock_build_node):
        mock_build_node.return_value = None
        self.meta.vars.append(XmlVar(element=True, name="a", qname="a"))
        self.meta.vars.append(XmlVar(wildcard=True, name="a", qname="a"))

        with self.assertRaises(ParserError) as cm:
            self.node.child("a", {}, {}, 0)

        self.assertEqual("Unknown property foo:a", str(cm.exception))

        self.node.config.fail_on_unknown_properties = False

        actual = self.node.child("foobar", {}, {}, 0)
        self.assertIsInstance(actual, SkipNode)

    def test_build_node_with_dataclass_union_var(self):
        var = XmlVar(
            element=True, name="a", qname="a", types=[Foo, FooMixed], dataclass=True
        )
        attrs = {"a": "b"}
        ns_map = {"ns0": "xsdata"}
        actual = self.node.build_node(var, attrs, ns_map, 10)

        self.assertIsInstance(actual, UnionNode)
        self.assertEqual(10, actual.position)
        self.assertIs(var, actual.var)
        self.assertEqual(attrs, actual.attrs)
        self.assertEqual(ns_map, actual.ns_map)
        self.assertEqual(0, actual.level)
        self.assertEqual(0, len(actual.events))

    @mock.patch.object(ParserUtils, "xsi_type", return_value="foo")
    @mock.patch.object(XmlContext, "fetch")
    def test_build_node_with_dataclass_var(self, mock_ctx_fetch, mock_xsi_type):
        var = XmlVar(
            element=True, name="a", qname="a", types=[Foo], dataclass=True, derived=True
        )
        xsi_type = "foo"
        namespace = self.meta.namespace
        mock_ctx_fetch.return_value = self.meta
        mock_xsi_type.return_value = xsi_type

        attrs = {"a": "b"}
        ns_map = {"ns0": "xsdata"}
        actual = self.node.build_node(var, attrs, ns_map, 10)

        self.assertIsInstance(actual, ElementNode)
        self.assertEqual(10, actual.position)
        self.assertTrue(actual.derived)
        self.assertIs(mock_ctx_fetch.return_value, actual.meta)

        mock_xsi_type.assert_called_once_with(attrs, ns_map)
        mock_ctx_fetch.assert_called_once_with(var.clazz, namespace, xsi_type)

    @mock.patch.object(XmlContext, "fetch")
    def test_build_node_with_dataclass_var_validates_nillable(self, mock_ctx_fetch):
        var = XmlVar(element=True, name="a", qname="a", types=[Foo], dataclass=True)
        ns_map = {}
        nillable_meta = replace(self.meta, nillable=True)
        mock_ctx_fetch.side_effect = [self.meta, self.meta, nillable_meta]
        attrs = {QNames.XSI_NIL: "false"}

        self.assertIsNotNone(self.node.build_node(var, attrs, ns_map, 10))

        attrs = {QNames.XSI_NIL: "true"}
        self.assertIsNotNone(self.node.build_node(var, attrs, ns_map, 10))

        attrs = {QNames.XSI_NIL: "false"}
        self.assertIsNone(self.node.build_node(var, attrs, ns_map, 10))

    def test_build_node_with_any_type_var_with_matching_xsi_type(self):
        var = XmlVar(element=True, name="a", qname="a", types=[object], any_type=True)
        attrs = {QNames.XSI_TYPE: "bk:books"}
        ns_map = {"bk": "urn:books"}
        actual = self.node.build_node(var, attrs, ns_map, 10)

        self.assertIsInstance(actual, ElementNode)
        self.assertEqual(10, actual.position)
        self.assertEqual(self.context.build(Books), actual.meta)
        self.assertEqual(attrs, actual.attrs)
        self.assertEqual(ns_map, actual.ns_map)
        self.assertFalse(actual.mixed)

    def test_build_node_with_any_type_var_with_datatype(self):
        var = XmlVar(element=True, name="a", qname="a", types=[object], any_type=True)
        attrs = {QNames.XSI_TYPE: "xs:hexBinary"}
        ns_map = {Namespace.XS.prefix: Namespace.XS.uri}
        actual = self.node.build_node(var, attrs, ns_map, 10)

        self.assertIsInstance(actual, StandardNode)
        self.assertEqual(ns_map, actual.ns_map)
        self.assertEqual(DataType.HEX_BINARY, actual.datatype)
        self.assertEqual(var.derived, actual.derived)

    def test_build_node_with_any_type_var_with_no_matching_xsi_type(self):
        var = XmlVar(element=True, name="a", qname="a", types=[object], any_type=True)
        attrs = {QNames.XSI_TYPE: "noMatch"}
        actual = self.node.build_node(var, attrs, {}, 10)

        self.assertIsInstance(actual, WildcardNode)
        self.assertEqual(10, actual.position)
        self.assertEqual(var, actual.var)
        self.assertEqual(attrs, actual.attrs)
        self.assertEqual({}, actual.ns_map)

    def test_build_node_with_any_type_var_with_no_xsi_type(self):
        var = XmlVar(element=True, name="a", qname="a", types=[object], any_type=True)
        attrs = {}
        actual = self.node.build_node(var, attrs, {}, 10)

        self.assertIsInstance(actual, WildcardNode)
        self.assertEqual(10, actual.position)
        self.assertEqual(var, actual.var)
        self.assertEqual(attrs, actual.attrs)
        self.assertEqual({}, actual.ns_map)

    def test_build_node_with_wildcard_var(self):
        var = XmlVar(wildcard=True, name="a", qname="a", types=[], dataclass=False)

        actual = self.node.build_node(var, {}, {}, 10)

        self.assertIsInstance(actual, WildcardNode)
        self.assertEqual(10, actual.position)
        self.assertEqual(var, actual.var)

    def test_build_node_with_primitive_var(self):
        var = XmlVar(text=True, name="a", qname="a", types=[int], default=100)
        attrs = {"a": "b"}
        ns_map = {"ns0": "xsdata"}
        actual = self.node.build_node(var, attrs, ns_map, 10)

        self.assertIsInstance(actual, PrimitiveNode)
        self.assertEqual(ns_map, actual.ns_map)
        self.assertEqual(var, actual.var)
示例#15
0
class ParserUtilsTests(TestCase):
    def setUp(self) -> None:
        self.ctx = XmlContext()

    def test_parse_xsi_type(self):
        ele = Element("foo")
        self.assertIsNone(ParserUtils.parse_xsi_type(ele))

        ele.set(QNames.XSI_TYPE, "foo")
        self.assertEqual(QName("foo"), ParserUtils.parse_xsi_type(ele))

        ele = Element("foo", nsmap=dict(bar="xsdata"))
        ele.set(QNames.XSI_TYPE, "bar:foo")
        self.assertEqual(QName("xsdata", "foo"), ParserUtils.parse_xsi_type(ele))

    @mock.patch("xsdata.formats.dataclass.parsers.utils.to_python", return_value=2)
    def test_parse_value(self, mock_to_python):
        self.assertEqual(1, ParserUtils.parse_value([int], None, 1))
        self.assertIsNone(ParserUtils.parse_value([int], None, lambda: 1))

        self.assertTrue(2, ParserUtils.parse_value([int], "1", None))
        mock_to_python.assert_called_once_with([int], "1", None)

    def test_parse_value_with_tokens_true(self):
        actual = ParserUtils.parse_value([int], " 1 2 3", list, None, True)
        self.assertEqual([1, 2, 3], actual)

        actual = ParserUtils.parse_value([int], ["1", "2", "3"], list, None, True)
        self.assertEqual([1, 2, 3], actual)

    @mock.patch("xsdata.formats.dataclass.parsers.utils.to_python", return_value=2)
    def test_parse_value_with_ns_map(self, mock_to_python):
        ns_map = dict(a=1)
        ParserUtils.parse_value([int], " 1 2 3", list, ns_map, True)
        ParserUtils.parse_value([str], " 1 2 3", None, ns_map, False)

        self.assertEqual(4, mock_to_python.call_count)
        mock_to_python.assert_has_calls(
            [
                mock.call([int], "1", ns_map),
                mock.call([int], "2", ns_map),
                mock.call([int], "3", ns_map),
                mock.call([str], " 1 2 3", ns_map),
            ]
        )

    @mock.patch.object(ParserUtils, "bind_element_wildcard_param")
    @mock.patch.object(ParserUtils, "find_eligible_wildcard")
    @mock.patch.object(ParserUtils, "bind_element_param")
    def test_bind_elements(
        self,
        mock_bind_element_param,
        mock_find_eligible_wildcard,
        mock_bind_element_wildcard_param,
    ):
        @dataclass
        class A:
            x: int
            y: int = field(init=False)
            w: object = field(metadata=dict(type=XmlType.WILDCARD))

        ctx = XmlContext()
        meta = ctx.build(A)
        x = meta.find_var(QName("x"))
        y = meta.find_var(QName("y"))
        w = meta.find_var(QName("wild"))
        wild_element = AnyElement(qname="foo")

        objects = [
            ("foo", 0),
            (x.qname, 1),
            (y.qname, 2),
            (w.qname, None),
            (w.qname, wild_element),
        ]

        mock_bind_element_param.side_effect = [True, False, False]
        mock_find_eligible_wildcard.side_effect = [None, w]

        params = {}
        ParserUtils.bind_element_children(params, meta, 1, objects)

        mock_bind_element_param.assert_has_calls(
            [
                mock.call(params, x, 1),
                mock.call(params, w, ""),
                mock.call(params, w, wild_element),
            ]
        )
        mock_find_eligible_wildcard.assert_has_calls(
            [mock.call(meta, w.qname, params), mock.call(meta, QName("foo"), params)]
        )

        mock_bind_element_wildcard_param.assert_called_once_with(
            params, w, w.qname, wild_element
        )

    def test_fetch_any_children(self):
        objects = [(x, x) for x in "abc"]
        self.assertEqual(["b", "c"], ParserUtils.fetch_any_children(1, objects))

    @mock.patch.object(ParserUtils, "parse_value")
    def test_bind_element_attrs(self, mock_parse_value):
        mock_parse_value.return_value = "2020-03-02"
        metadata = self.ctx.build(ProductType)
        eff_date = metadata.find_var("effDate")
        element = Element("foo")
        element.set("effDate", "2020-03-01")
        element.set("whatever", "foo")

        params = {}
        ParserUtils.bind_element_attrs(params, metadata, element)
        expected = {"eff_date": "2020-03-02", "other_attributes": {"whatever": "foo"}}
        self.assertEqual(expected, params)
        mock_parse_value.assert_called_once_with(
            eff_date.types,
            "2020-03-01",
            eff_date.default,
            element.nsmap,
            eff_date.is_list,
        )

    def test_bind_element_attrs_doesnt_overwrite_values(self):
        metadata = self.ctx.build(ProductType)
        element = Element("foo")
        element.set("effDate", "2020-03-01")

        params = dict(eff_date="foo")

        ParserUtils.bind_element_attrs(params, metadata, element)
        expected = {"eff_date": "foo", "other_attributes": {"effDate": "2020-03-01"}}
        self.assertEqual(expected, params)

    def test_bind_elements_attrs_ignore_init_false_vars(self):
        metadata = self.ctx.build(ProductType)
        eff_date = metadata.find_var("effDate")
        metadata.vars.remove(eff_date)
        metadata.vars.append(replace(eff_date, init=False))

        element = Element("foo")
        element.set("effDate", "2020-03-01")

        params = {}
        ParserUtils.bind_element_attrs(params, metadata, element)
        self.assertEqual({}, params)

    def test_bind_element_text_with_no_text_var(self):
        element = Element("foo")
        element.text = "foo"

        params = {}
        metadata = self.ctx.build(Books)
        ParserUtils.bind_element_text(params, metadata, element)
        self.assertEqual({}, params)

    @mock.patch.object(ParserUtils, "parse_value", return_value="yes!")
    def test_bind_element_text_with_text_var(self, mock_parse_value):
        element = Element("foo")
        params = {}
        metadata = self.ctx.build(SizeType)
        var = metadata.find_var(mode=FindMode.TEXT)
        ParserUtils.bind_element_text(params, metadata, element)
        self.assertEqual({}, params)

        element.text = "foo"
        ParserUtils.bind_element_text(params, metadata, element)
        self.assertEqual({"value": "yes!"}, params)
        mock_parse_value.assert_called_once_with(
            var.types, element.text, var.default, element.nsmap, var.is_list,
        )

    def test_bind_element_param(self):
        var = XmlVar(name="a", qname=QName("a"))
        params = {}

        status = ParserUtils.bind_element_param(params, var, 1)
        self.assertTrue(status)
        self.assertEqual({"a": 1}, params)

        status = ParserUtils.bind_element_param(params, var, 2)
        self.assertFalse(status)
        self.assertEqual({"a": 1}, params)

    def test_bind_element_param_with_list_var(self):
        var = XmlVar(name="a", qname=QName("a"), default=list)
        params = {}

        status = ParserUtils.bind_element_param(params, var, 1)
        self.assertTrue(status)
        self.assertEqual({"a": [1]}, params)

        status = ParserUtils.bind_element_param(params, var, 2)
        self.assertTrue(status)
        self.assertEqual({"a": [1, 2]}, params)

    def test_bind_element_wildcard_param(self):
        params = {}
        var = XmlVar(name="a", qname=QName("a"))
        qname = QName("b")
        one = AnyElement(qname=qname, text="one")
        two = AnyElement(qname=qname, text="two")
        three = AnyElement(qname=qname, text="three")

        ParserUtils.bind_element_wildcard_param(params, var, qname, "one")
        self.assertEqual(dict(a=one), params)

        ParserUtils.bind_element_wildcard_param(params, var, qname, "two")
        self.assertEqual(dict(a=AnyElement(children=[one, two])), params)

        ParserUtils.bind_element_wildcard_param(params, var, qname, "three")
        self.assertEqual(dict(a=AnyElement(children=[one, two, three])), params)

    def test_bind_element_wildcard_param_with_dataclass(self):
        params = {}
        var = XmlVar(name="a", qname=QName("a"))
        qname = QName("b")
        value = AnyElement()
        clazz = make_dataclass("Foo", fields=[])
        foo = clazz()

        ParserUtils.bind_element_wildcard_param(params, var, qname, value)
        self.assertEqual(dict(a=value), params)
        self.assertIsNone(value.qname)

        params.clear()
        ParserUtils.bind_element_wildcard_param(params, var, qname, foo)
        self.assertEqual(dict(a=foo), params)
        self.assertEqual(qname, foo.qname)

    def test_bind_element_wild_text_when_find_var_returns_none(self):
        meta = mock.Mock(XmlMeta)
        meta.find_var = MagicMock(return_value=None)
        elem = Element("foo")
        params = {}
        ParserUtils.bind_element_wild_text(params, meta, elem)
        self.assertEqual(0, len(params))

    def test_bind_element_wild_text_when_element_has_no_text_and_tail(self):
        var = XmlVar(name="a", qname=QName("a"))
        meta = mock.Mock(XmlMeta)
        meta.find_var = MagicMock(return_value=var)
        elem = Element("foo")
        params = {}

        ParserUtils.bind_element_wild_text(params, meta, elem)
        self.assertEqual(0, len(params))

    def test_bind_element_wild_text(self):
        var = XmlVar(name="a", qname=QName("a"))
        meta = mock.Mock(XmlMeta)
        meta.find_var = MagicMock(return_value=var)
        elem = Element("foo")
        elem.text = "txt"
        elem.tail = "tail"
        params = {}

        ParserUtils.bind_element_wild_text(params, meta, elem)
        expected = AnyElement(text="txt", tail="tail")
        self.assertEqual(dict(a=expected), params)

        elem.text = "a"
        elem.tail = "b"
        expected = AnyElement(text="a", tail="b")
        ParserUtils.bind_element_wild_text(params, meta, elem)
        self.assertEqual(dict(a=expected), params)

    def test_bind_element_wild_text_when_var_is_list(self):
        var = XmlVar(name="a", qname=QName("a"), default=list)
        meta = mock.Mock(XmlMeta)
        meta.find_var = MagicMock(return_value=var)
        elem = Element("foo")
        elem.text = "txt"
        elem.tail = "tail"
        params = {}

        ParserUtils.bind_element_wild_text(params, meta, elem)
        self.assertEqual(dict(a=["txt", "tail"]), params)

        elem.text = None
        ParserUtils.bind_element_wild_text(params, meta, elem)
        self.assertEqual(dict(a=["txt", "tail", "tail"]), params)

        elem.tail = None
        elem.text = "first"
        ParserUtils.bind_element_wild_text(params, meta, elem)
        self.assertEqual(dict(a=["first", "txt", "tail", "tail"]), params)

    def test_parse_any_element(self):
        element = Element("foo")
        element.set("a", "1")
        element.set("b", "2")
        element.set(
            QName(Namespace.XSI.uri, "type").text, QName(Namespace.XS.uri, "float").text
        )
        element.text = "yes"
        element.tail = "no"

        actual = ParserUtils.parse_any_element(element)
        expected = AnyElement(
            qname=element.tag,
            text="yes",
            tail="no",
            attributes={
                "a": "1",
                "b": "2",
                QName(Namespace.XSI.uri, "type"): QName(Namespace.XS.uri, "float"),
            },
            ns_map=element.nsmap,
        )
        self.assertEqual(expected, actual)
        actual = ParserUtils.parse_any_element(element, False)
        self.assertIsNone(actual.qname)

    def test_parse_any_attributes(self):
        element = Element("foo", nsmap=dict(foo="bar"))
        element.set("a", QName("bar", "val"))
        element.set("b", "2")
        element.set("c", "what:3")

        actual = ParserUtils.parse_any_attributes(element)
        expected = {
            QName("a"): QName("bar", "val"),
            QName("b"): "2",
            QName("c"): "what:3",
        }

        self.assertEqual("foo:val", element.attrib["a"])
        self.assertEqual(expected, actual)

    def test_element_text_and_tail(self):
        element = Element("foo")

        text, tail = ParserUtils.element_text_and_tail(element)
        self.assertIsNone(text)
        self.assertIsNone(tail)

        element.text = " \n "
        element.tail = " \n  "
        text, tail = ParserUtils.element_text_and_tail(element)
        self.assertIsNone(text)
        self.assertIsNone(tail)

        element.text = " foo "
        element.tail = " bar "
        text, tail = ParserUtils.element_text_and_tail(element)
        self.assertEqual("foo", text)
        self.assertEqual("bar", tail)
示例#16
0
class ParserUtilsTests(TestCase):
    def setUp(self) -> None:
        self.ctx = XmlContext()

    def test_xsi_type(self):
        ns_map = {"bar": "xsdata"}
        attrs = {}
        self.assertIsNone(ParserUtils.xsi_type(attrs, ns_map))

        attrs = {QNames.XSI_TYPE: "foo"}
        self.assertEqual("foo", ParserUtils.xsi_type(attrs, ns_map))

        attrs = {QNames.XSI_TYPE: "bar:foo"}
        self.assertEqual("{xsdata}foo", ParserUtils.xsi_type(attrs, ns_map))

    def test_data_type(self):
        ns_map = {"bar": "xsdata"}
        attrs = {}
        self.assertEqual(DataType.STRING, ParserUtils.data_type(attrs, ns_map))

        ns_map = {"xs": Namespace.XS.uri}
        attrs = {QNames.XSI_TYPE: "xs:foo"}
        self.assertEqual(DataType.STRING, ParserUtils.data_type(attrs, ns_map))

        attrs = {QNames.XSI_TYPE: "xs:float"}
        self.assertEqual(DataType.FLOAT, ParserUtils.data_type(attrs, ns_map))

    @mock.patch.object(ConverterAdapter, "deserialize", return_value=2)
    def test_parse_value(self, mock_deserialize):
        self.assertEqual(1, ParserUtils.parse_value(None, [int], 1))
        self.assertIsNone(ParserUtils.parse_value(None, [int], lambda: 1))

        self.assertTrue(2, ParserUtils.parse_value("1", [int], None))
        mock_deserialize.assert_called_once_with("1", [int], ns_map=None)

    def test_parse_value_with_tokens_true(self):
        actual = ParserUtils.parse_value(" 1 2 3", [int], list, None, True)
        self.assertEqual([1, 2, 3], actual)

        actual = ParserUtils.parse_value(["1", "2", "3"], [int], list, None, True)
        self.assertEqual([1, 2, 3], actual)

        actual = ParserUtils.parse_value(None, [int], lambda: [1, 2, 3], None, True)
        self.assertEqual([1, 2, 3], actual)

    @mock.patch.object(ConverterAdapter, "deserialize", return_value=2)
    def test_parse_value_with_ns_map(self, mock_to_python):
        ns_map = dict(a=1)
        ParserUtils.parse_value(" 1 2 3", [int], list, ns_map, True)
        ParserUtils.parse_value(" 1 2 3", [str], None, ns_map, False)

        self.assertEqual(4, mock_to_python.call_count)
        mock_to_python.assert_has_calls(
            [
                mock.call("1", [int], ns_map=ns_map),
                mock.call("2", [int], ns_map=ns_map),
                mock.call("3", [int], ns_map=ns_map),
                mock.call(" 1 2 3", [str], ns_map=ns_map),
            ]
        )

    def test_bind_objects(self):
        @dataclass
        class A:
            x: int
            y: int = field(init=False)
            w: object = field(metadata=dict(type=XmlType.WILDCARD))

        ctx = XmlContext()
        meta = ctx.build(A)
        x = meta.find_var("x")
        y = meta.find_var("y")
        w = meta.find_var("wild")
        wild_element = AnyElement(qname="foo")

        objects = [
            ("foo", 0),
            (x.qname, 1),
            (y.qname, 2),
            (w.qname, None),
            (w.qname, wild_element),
        ]

        params = {}
        ParserUtils.bind_objects(params, meta, 1, objects)
        expected = {
            "x": 1,
            "w": AnyElement(
                children=[AnyElement(qname="w", text=""), AnyElement(qname="foo")]
            ),
        }

        self.assertEqual(expected, params)

    @mock.patch("xsdata.formats.dataclass.parsers.utils.logger.warning")
    def test_bind_objects_with_unassigned_object(self, mock_warning):
        a = make_dataclass("a", [("x", int)])
        meta = XmlContext().build(a)
        params = {}
        objects = [("x", 1), ("y", 2)]

        ParserUtils.bind_objects(params, meta, 0, objects)

        self.assertEqual({"x": 1}, params)
        mock_warning.assert_called_once_with("Unassigned parsed object %s", "y")

    def test_bind_mixed_objects(self):
        generic = AnyElement(qname="foo")
        data_class = make_dataclass("A", fields=[])
        derived = DerivedElement(qname="d", value=data_class)
        objects = [
            ("a", 1),
            ("b", None),
            ("d", data_class),
            ("foo", generic),
            (None, "foo"),
        ]

        var = XmlVar(wildcard=True, name="foo", qname="{any}foo")
        params = {}
        ParserUtils.bind_mixed_objects(params, var, 1, objects)

        expected = {var.name: [AnyElement(qname="b", text=""), derived, generic, "foo"]}
        self.assertEqual(expected, params)

    def test_fetch_any_children(self):
        objects = [(x, x) for x in "abc"]
        self.assertEqual(["b", "c"], ParserUtils.fetch_any_children(1, objects))

    @mock.patch.object(ParserUtils, "parse_any_attribute")
    @mock.patch.object(ParserUtils, "parse_value")
    def test_bind_attrs(self, mock_parse_value, mock_parse_any_attribute):
        mock_parse_value.return_value = "2020-03-02"
        mock_parse_any_attribute.return_value = "foobar"
        metadata = self.ctx.build(ProductType)
        eff_date = metadata.find_var("effDate")

        params = {}
        ns_map = {}
        attrs = {"effDate": "2020-03-01", "foo": "bar"}

        ParserUtils.bind_attrs(params, metadata, attrs, ns_map)
        expected = {
            "eff_date": "2020-03-02",
            "other_attributes": {"foo": "foobar"},
        }
        self.assertEqual(expected, params)
        mock_parse_any_attribute.assert_called_once_with("bar", ns_map)
        mock_parse_value.assert_called_once_with(
            "2020-03-01",
            eff_date.types,
            eff_date.default,
            ns_map,
            eff_date.list_element,
        )

    def test_parse_any_attributes(self):
        attrs = {QNames.XSI_TYPE: "xsd:string", "a": "b"}
        ns_map = {"xsi": Namespace.XSI.uri, "xsd": Namespace.XS.uri}

        result = ParserUtils.parse_any_attributes(attrs, ns_map)
        expected = {
            QNames.XSI_TYPE: "{http://www.w3.org/2001/XMLSchema}string",
            "a": "b",
        }
        self.assertEqual(expected, result)

    def test_parse_any_attribute(self):
        ns_map = {"xsi": Namespace.XSI.uri, "xsd": Namespace.XS.uri}
        value = ParserUtils.parse_any_attribute("xsd:string", ns_map)
        self.assertEqual("{http://www.w3.org/2001/XMLSchema}string", value)

        ns_map["http"] = "happens"
        value = ParserUtils.parse_any_attribute("http://www.com", ns_map)
        self.assertEqual("http://www.com", value)

    def test_bind_attrs_doesnt_overwrite_values(self):
        metadata = self.ctx.build(ProductType)
        params = dict(eff_date="foo")
        attrs = {"effDate": "2020-03-01"}
        ns_map = {}

        ParserUtils.bind_attrs(params, metadata, attrs, ns_map)

        expected = {"eff_date": "foo", "other_attributes": {"effDate": "2020-03-01"}}
        self.assertEqual(expected, params)

    def test_bind_attrs_ignore_init_false_vars(self):
        metadata = self.ctx.build(ProductType)
        eff_date = metadata.find_var("effDate")
        metadata.vars.remove(eff_date)
        metadata.vars.append(replace(eff_date, init=False, xml_type=None))

        params = {}
        attrs = {"effDate": "2020-03-01"}
        ns_map = {}

        ParserUtils.bind_attrs(params, metadata, attrs, ns_map)
        self.assertEqual({"other_attributes": {}}, params)

    @mock.patch.object(XmlMeta, "find_var")
    def test_bind_attrs_skip_empty_attrs(self, mock_find_var):
        metadata = self.ctx.build(ProductType)

        params = {}
        ParserUtils.bind_attrs(params, metadata, {}, {})
        self.assertEqual(0, len(params))
        self.assertEqual(0, mock_find_var.call_count)

    @mock.patch.object(ParserUtils, "parse_value", return_value="yes!")
    def test_bind_content(self, mock_parse_value):
        metadata = self.ctx.build(SizeType)
        var = metadata.find_var(mode=FindMode.TEXT)
        params = {}
        ns_map = {"a": "b"}

        self.assertFalse(ParserUtils.bind_content(params, metadata, None, ns_map))
        self.assertEqual({}, params)

        self.assertTrue(ParserUtils.bind_content(params, metadata, "foo", ns_map))
        self.assertEqual({"value": "yes!"}, params)
        mock_parse_value.assert_called_once_with(
            "foo", var.types, var.default, ns_map, var.list_element
        )

    def test_bind_content_with_no_text_var(self):
        params = {}
        metadata = self.ctx.build(Books)
        self.assertFalse(ParserUtils.bind_content(params, metadata, "foo", {}))
        self.assertEqual({}, params)

    def test_bind_var(self):
        var = XmlVar(name="a", qname="a")
        params = {}

        status = ParserUtils.bind_var(params, var, 1)
        self.assertTrue(status)
        self.assertEqual({"a": 1}, params)

        status = ParserUtils.bind_var(params, var, 2)
        self.assertFalse(status)
        self.assertEqual({"a": 1}, params)

    def test_bind_var_with_list_var(self):
        var = XmlVar(name="a", qname="a", list_element=True)
        params = {}

        status = ParserUtils.bind_var(params, var, 1)
        self.assertTrue(status)
        self.assertEqual({"a": [1]}, params)

        status = ParserUtils.bind_var(params, var, 2)
        self.assertTrue(status)
        self.assertEqual({"a": [1, 2]}, params)

    def test_bind_wild_var(self):
        params = {}
        var = XmlVar(name="a", qname="a")
        qname = "b"
        one = AnyElement(qname=qname, text="one")
        two = AnyElement(qname=qname, text="two")
        three = AnyElement(qname=qname, text="three")
        bind = ParserUtils.bind_wild_var

        self.assertTrue(bind(params, var, qname, "one"))
        self.assertEqual(dict(a=one), params)

        self.assertTrue(bind(params, var, qname, "two"))
        self.assertEqual(dict(a=AnyElement(children=[one, two])), params)

        self.assertTrue(bind(params, var, qname, "three"))
        self.assertEqual(dict(a=AnyElement(children=[one, two, three])), params)

    def test_bind_wild_var_with_list_var(self):
        params = {}
        var = XmlVar(name="a", qname="a", list_element=True)
        qname = "b"
        one = AnyElement(qname=qname, text="one")
        two = AnyElement(qname=qname, text="two")
        three = AnyElement(qname=qname, text="three")
        bind = ParserUtils.bind_wild_var

        self.assertTrue(bind(params, var, qname, "one"))
        self.assertTrue(bind(params, var, qname, "two"))
        self.assertTrue(bind(params, var, qname, "three"))
        self.assertEqual(dict(a=[one, two, three]), params)

    def test_bind_wild_content(self):
        var = XmlVar(name="a", qname="a")
        params = {}
        attrs = {}
        ns_map = {}

        ParserUtils.bind_wild_content(params, var, None, None, attrs, ns_map)
        self.assertEqual(0, len(params))

        params = {}
        ParserUtils.bind_wild_content(params, var, "txt", "tail", attrs, ns_map)
        expected = AnyElement(text="txt", tail="tail")
        self.assertEqual(dict(a=expected), params)

        attrs = {"a": "b"}
        ns_map = {"ns0": "a"}
        ParserUtils.bind_wild_content(params, var, "txt", "tail", attrs, ns_map)
        expected = AnyElement(
            text="txt", tail="tail", children=[expected], attributes=attrs
        )
        self.assertEqual(dict(a=expected), params)

    def test_bind_wildcard_when_var_is_list(self):
        var = XmlVar(name="a", qname="a", default=list, list_element=True)
        params = {}
        attrs = {"a", "b"}
        ns_map = {"ns0", "a"}

        ParserUtils.bind_wild_content(params, var, "txt", "tail", attrs, ns_map)
        self.assertEqual(dict(a=["txt", "tail"]), params)

        ParserUtils.bind_wild_content(params, var, None, "tail", attrs, ns_map)
        self.assertEqual(dict(a=["txt", "tail", "tail"]), params)

        ParserUtils.bind_wild_content(params, var, "first", None, attrs, ns_map)
        self.assertEqual(dict(a=["first", "txt", "tail", "tail"]), params)

    def test_prepare_generic_value(self):
        actual = ParserUtils.prepare_generic_value("a", 1)
        expected = AnyElement(qname="a", text="1")
        self.assertEqual(expected, actual)

        actual = ParserUtils.prepare_generic_value("a", "foo")
        expected = AnyElement(qname="a", text="foo")
        self.assertEqual(expected, actual)

        fixture = make_dataclass("Fixture", [("content", str)])
        actual = ParserUtils.prepare_generic_value("a", fixture("foo"))
        expected = DerivedElement(qname="a", value=fixture("foo"))
        self.assertEqual(expected, actual)

        actual = ParserUtils.prepare_generic_value("a", expected)
        self.assertIs(expected, actual)

    def test_score_object(self):
        self.assertEqual(-1.0, ParserUtils.score_object(None))

        cls = make_dataclass("b", [("x", int), ("y", str), ("z", Any)])
        self.assertEqual(2.5, ParserUtils.score_object(cls(1, "1", None)))