Skip to content

Commit

Permalink
Unify wildcard/anyType node parsers
Browse files Browse the repository at this point in the history
Notes:
This will produce more consistent results accross the board.
  • Loading branch information
tefra committed Feb 14, 2021
1 parent 3bdda16 commit 37e5856
Show file tree
Hide file tree
Showing 13 changed files with 311 additions and 226 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))
151 changes: 53 additions & 98 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 @@ -392,29 +391,42 @@ 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(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)

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)

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)
Expand All @@ -432,95 +444,13 @@ def test_build_node_with_primitive_var(self):
actual = self.node.build_node(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 All @@ -546,6 +476,21 @@ def test_bind(self):
self.assertEqual(var.qname, objects[-1][0])
self.assertEqual(generic, objects[-1][1])

# Preserve whitespace if no children
node.position = 1
node.bind("foo", text, tail, objects)
generic.text = text
generic.children = []
self.assertEqual(generic, objects[-1][1])

# Not a wildcard, no tail/attrs/children skip wrapper
tail = None
text = "1"
node.attrs = {}
node.position = 2
node.bind("a", text, tail, objects)
self.assertEqual("1", objects[-1][1])

def test_child(self):
attrs = {"id": "1"}
ns_map = {"ns0": "xsdata"}
Expand Down Expand Up @@ -648,7 +593,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 +603,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 +764,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
33 changes: 32 additions & 1 deletion tests/formats/dataclass/serializers/test_xml.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ def test_write_any_type_with_primitive_element(self):
self.assertIsInstance(result, Generator)
self.assertEqual(expected, list(result))

def test_write_any_type_with_generic_object(self):
def test_write_any_type_with_any_element(self):
var = XmlVar(wildcard=True, qname="a", name="a")
value = AnyElement(
qname="a",
Expand All @@ -218,6 +218,37 @@ def test_write_any_type_with_generic_object(self):
self.assertIsInstance(result, Generator)
self.assertEqual(expected, list(result))

def test_write_any_type_with_derived_element_primitive(self):
var = XmlVar(wildcard=True, qname="a", name="a")
value = DerivedElement(qname="a", value=1)
expected = [
(XmlWriterEvent.START, "a"),
(XmlWriterEvent.ATTR, QNames.XSI_TYPE, QName(str(DataType.SHORT))),
(XmlWriterEvent.DATA, 1),
(XmlWriterEvent.END, "a"),
]

result = self.serializer.write_value(value, var, "xsdata")
self.assertIsInstance(result, Generator)
self.assertEqual(expected, list(result))

def test_write_any_type_with_derived_element_dataclass(self):
var = XmlVar(wildcard=True, qname="a", name="a")
value = DerivedElement(qname="a", value=BookForm(title="def"), substituted=True)
expected = [
(XmlWriterEvent.START, "a"),
(XmlWriterEvent.ATTR, "lang", "en"),
(XmlWriterEvent.ATTR, QNames.XSI_TYPE, QName("{urn:books}BookForm")),
(XmlWriterEvent.START, "title"),
(XmlWriterEvent.DATA, "def"),
(XmlWriterEvent.END, "title"),
(XmlWriterEvent.END, "a"),
]

result = self.serializer.write_value(value, var, "xsdata")
self.assertIsInstance(result, Generator)
self.assertEqual(expected, list(result))

def test_write_xsi_type(self):
var = XmlVar(
element=True, qname="a", name="a", dataclass=True, types=[BookForm]
Expand Down
Loading

0 comments on commit 37e5856

Please sign in to comment.