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))
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)
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])
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"))
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)
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")))
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)
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_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)
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))
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))
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))
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))
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)
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)
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)))