Skip to content

Commit

Permalink
Apply xsi:lookup for xs:any elements
Browse files Browse the repository at this point in the history
  • Loading branch information
tefra committed Feb 12, 2021
1 parent 3bdda16 commit 44d5576
Show file tree
Hide file tree
Showing 13 changed files with 291 additions and 230 deletions.
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ exclude: tests/fixtures|docs/examples

repos:
- repo: https://github.com/asottile/pyupgrade
rev: v2.9.0
rev: v2.10.0
hooks:
- id: pyupgrade
args: [--py37-plus]
- repo: https://github.com/asottile/reorder_python_imports
rev: v2.3.6
rev: v2.4.0
hooks:
- id: reorder-python-imports
- repo: https://github.com/ambv/black
Expand Down
3 changes: 2 additions & 1 deletion tests/fixtures/defxmlschema/chapter12.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@
"other_attributes": {
"{http://example.org/oth}custom": "12"
}
}
},
"substituted": false
}
]
}
3 changes: 2 additions & 1 deletion tests/fixtures/defxmlschema/chapter17.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"quantity": 1,
"color": null,
"number": 563
}
},
"substituted": false
}
]
},
Expand Down
42 changes: 40 additions & 2 deletions tests/formats/dataclass/parsers/test_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,11 @@ def test_bind_choice_generic_with_derived(self):
XmlVar(element=True, name="b", qname="b", types=[float]),
],
)
data = {"qname": "a", "value": 1, "substituted": True}

self.assertEqual(
DerivedElement(qname="a", value=1),
self.parser.bind_choice({"qname": "a", "value": 1}, var),
DerivedElement(qname="a", value=1, substituted=True),
self.parser.bind_choice(data, var),
)

def test_bind_choice_generic_with_wildcard(self):
Expand Down Expand Up @@ -199,3 +200,40 @@ def test_bind_choice_generic_with_unknown_qname(self):
"XmlElements undefined choice: `compound` for qname `foo`",
str(cm.exception),
)

def test_bind_wildcard_with_any_element(self):
var = XmlVar(
wildcard=True,
name="any_element",
qname="any_element",
types=[object],
)

self.assertEqual(
AnyElement(qname="a", text="1"),
self.parser.bind_value(var, {"qname": "a", "text": 1}),
)

def test_bind_wildcard_with_derived_element(self):
var = XmlVar(
any_type=True,
name="a",
qname="a",
types=[object],
)
actual = DerivedElement(qname="a", value=Books(book=[]), substituted=True)
data = {"qname": "a", "value": {"book": []}, "substituted": True}

self.assertEqual(actual, self.parser.bind_value(var, data))

def test_bind_wildcard_with_no_matching_value(self):
var = XmlVar(
any_type=True,
name="a",
qname="a",
types=[object],
)

data = {"test_bind_wildcard_with_no_matching_value": False}
self.assertEqual(data, self.parser.bind_value(var, data))
self.assertEqual(1, self.parser.bind_value(var, 1))
156 changes: 48 additions & 108 deletions tests/formats/dataclass/parsers/test_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from xsdata.formats.dataclass.models.generics import DerivedElement
from xsdata.formats.dataclass.parsers.config import ParserConfig
from xsdata.formats.dataclass.parsers.mixins import XmlHandler
from xsdata.formats.dataclass.parsers.nodes import AnyTypeNode
from xsdata.formats.dataclass.parsers.nodes import ElementNode
from xsdata.formats.dataclass.parsers.nodes import NodeParser
from xsdata.formats.dataclass.parsers.nodes import PrimitiveNode
Expand Down Expand Up @@ -330,7 +329,7 @@ def test_build_node_with_dataclass_union_var(self):
)
attrs = {"a": "b"}
ns_map = {"ns0": "xsdata"}
actual = self.node.build_node(var, attrs, ns_map, 10)
actual = self.node.build_node("a", var, attrs, ns_map, 10)

self.assertIsInstance(actual, UnionNode)
self.assertEqual(10, actual.position)
Expand All @@ -353,7 +352,7 @@ def test_build_node_with_dataclass_var(self, mock_ctx_fetch, mock_xsi_type):

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

self.assertIsInstance(actual, ElementNode)
self.assertEqual(10, actual.position)
Expand All @@ -371,19 +370,19 @@ def test_build_node_with_dataclass_var_validates_nillable(self, mock_ctx_fetch):
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))
self.assertIsNotNone(self.node.build_node("a", var, attrs, ns_map, 10))

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

attrs = {QNames.XSI_NIL: "false"}
self.assertIsNone(self.node.build_node(var, attrs, ns_map, 10))
self.assertIsNone(self.node.build_node("a", 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)
actual = self.node.build_node("a", var, attrs, ns_map, 10)

self.assertIsInstance(actual, ElementNode)
self.assertEqual(10, actual.position)
Expand All @@ -392,34 +391,47 @@ def test_build_node_with_any_type_var_with_matching_xsi_type(self):
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("a", var, attrs, ns_map, 10)

self.assertIsInstance(actual, PrimitiveNode)
self.assertEqual(ns_map, actual.ns_map)
self.assertEqual([DataType.HEX_BINARY.type], actual.types)
self.assertIsNone(actual.default)
self.assertFalse(actual.tokens)
self.assertEqual(DataType.HEX_BINARY.format, actual.format)
self.assertEqual(var.derived, actual.derived)
self.assertEqual(DataType.HEX_BINARY.wrapper, actual.wrapper)

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)
actual = self.node.build_node("a", var, attrs, {}, 10)

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

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)
actual = self.node.build_node("a", var, attrs, {}, 10)

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

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)
actual = self.node.build_node("a", var, {}, {}, 10)

self.assertIsInstance(actual, WildcardNode)
self.assertEqual(10, actual.position)
Expand All @@ -429,98 +441,16 @@ 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)
actual = self.node.build_node("a", var, attrs, ns_map, 10)

self.assertIsInstance(actual, PrimitiveNode)
self.assertEqual(var, actual.var)
self.assertEqual(ns_map, actual.ns_map)


class AnyTypeNodeTests(TestCase):
def setUp(self) -> None:
self.var = XmlVar(element=True, name="a", qname="a", types=[object])
self.node = AnyTypeNode(position=0, var=self.var, attrs={}, ns_map={})

def test_child(self):
self.assertFalse(self.node.has_children)

attrs = {"a": 1}
ns_map = {"ns0": "b"}
actual = self.node.child("foo", attrs, ns_map, 10)

self.assertIsInstance(actual, WildcardNode)
self.assertEqual(10, actual.position)
self.assertEqual(self.var, actual.var)
self.assertEqual(attrs, actual.attrs)
self.assertEqual(ns_map, actual.ns_map)
self.assertTrue(self.node.has_children)

def test_bind_with_children(self):
text = "\n "
tail = "bar"
generic = AnyElement(
qname="a",
text=None,
tail="bar",
attributes={},
children=[1, 2, 3],
)

objects = [("a", 1), ("b", 2), ("c", 3)]

self.node.has_children = True
self.assertTrue(self.node.bind("a", text, tail, objects))
self.assertEqual(self.var.qname, objects[-1][0])
self.assertEqual(generic, objects[-1][1])

def test_bind_with_simple_type(self):
objects = []

self.node.attrs[QNames.XSI_TYPE] = "xs:float"
self.node.ns_map["xs"] = Namespace.XS.uri

self.assertTrue(self.node.bind("a", "10", None, objects))
self.assertEqual(self.var.qname, objects[-1][0])
self.assertEqual(10.0, objects[-1][1])

def test_bind_with_simple_type_that_has_wrapper_class(self):
objects = []

self.node.attrs[QNames.XSI_TYPE] = "xs:hexBinary"
self.node.ns_map["xs"] = Namespace.XS.uri

self.assertTrue(self.node.bind("a", "4368726973", None, objects))
self.assertEqual(self.var.qname, objects[-1][0])
self.assertEqual(b"Chris", objects[-1][1])
self.assertIsInstance(objects[-1][1], XmlHexBinary)

def test_bind_with_simple_type_derived(self):
objects = []

self.node.var = XmlVar(
element=True, name="a", qname="a", types=[object], derived=True
)
self.node.attrs[QNames.XSI_TYPE] = str(DataType.FLOAT)

self.assertTrue(self.node.bind("a", "10", None, objects))
self.assertEqual(self.var.qname, objects[-1][0])
self.assertEqual(DerivedElement(qname="a", value=10.0), objects[-1][1])

def test_bind_with_simple_type_with_mixed_content(self):
objects = []

self.node.mixed = True
self.node.attrs[QNames.XSI_TYPE] = str(DataType.FLOAT)

self.assertTrue(self.node.bind("a", "10", "pieces", objects))
self.assertEqual(self.var.qname, objects[-2][0])
self.assertEqual(10.0, objects[-2][1])
self.assertIsNone(objects[-1][0])
self.assertEqual("pieces", objects[-1][1])

self.assertTrue(self.node.bind("a", "10", "\n", objects))
self.assertEqual(self.var.qname, objects[-1][0])
self.assertEqual(10.0, objects[-1][1])
self.assertEqual(actual.types, var.types)
self.assertEqual(actual.tokens, var.tokens)
self.assertEqual(actual.format, var.format)
self.assertEqual(actual.derived, var.derived)
self.assertEqual(actual.default, var.default)
self.assertIsNone(actual.wrapper)


class WildcardNodeTests(TestCase):
Expand Down Expand Up @@ -648,7 +578,7 @@ def test_bind(self, mock_parse_value):
mock_parse_value.return_value = 13
var = XmlVar(text=True, name="foo", qname="foo", types=[int], format="Nope")
ns_map = {"foo": "bar"}
node = PrimitiveNode(var=var, ns_map=ns_map)
node = PrimitiveNode.from_var(var, ns_map)
objects = []

self.assertTrue(node.bind("foo", "13", "Impossible", objects))
Expand All @@ -658,17 +588,27 @@ def test_bind(self, mock_parse_value):
"13", var.types, var.default, ns_map, var.tokens, var.format
)

def test_bind_derived_var(self):
def test_bind_derived_mode(self):
var = XmlVar(text=True, name="foo", qname="foo", types=[int], derived=True)
ns_map = {"foo": "bar"}
node = PrimitiveNode(var=var, ns_map=ns_map)
node = PrimitiveNode.from_var(var, ns_map)
objects = []

self.assertTrue(node.bind("foo", "13", "Impossible", objects))
self.assertEqual(DerivedElement("foo", 13), objects[-1][1])

def test_bind_wrapper_mode(self):
datatype = DataType.HEX_BINARY
ns_map = {"foo": "bar"}
node = PrimitiveNode.from_datatype(datatype, True, ns_map)
objects = []

self.assertTrue(node.bind("foo", "13", "Impossible", objects))
self.assertEqual(DerivedElement("foo", XmlHexBinary(b"\x13")), objects[-1][1])

def test_child(self):
node = PrimitiveNode(var=XmlVar(text=True, name="foo", qname="foo"), ns_map={})
var = XmlVar(text=True, name="foo", qname="foo")
node = PrimitiveNode.from_var(var, {})

with self.assertRaises(XmlContextError):
node.child("foo", {}, {}, 0)
Expand Down Expand Up @@ -809,7 +749,7 @@ def test_end(self, mock_assemble):
objects = [("q", "result")]
queue = []
var = XmlVar(text=True, name="foo", qname="foo")
queue.append(PrimitiveNode(var=var, ns_map={}))
queue.append(PrimitiveNode.from_var(var, ns_map={}))

result = parser.end(queue, objects, "author", "foobar", None)
self.assertEqual("result", result)
Expand Down
2 changes: 1 addition & 1 deletion tests/formats/dataclass/parsers/test_xml.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def test_end(self, mock_emit_event):
objects = []
queue = []
var = XmlVar(text=True, name="foo", qname="foo", types=[bool])
queue.append(PrimitiveNode(var=var, ns_map={}))
queue.append(PrimitiveNode.from_var(var, {}))

result = self.parser.end(queue, objects, "enabled", "true", None)
self.assertTrue(result)
Expand Down
Loading

0 comments on commit 44d5576

Please sign in to comment.