Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Improve converter warnings #1036

Merged
merged 1 commit into from
May 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion tests/formats/dataclass/parsers/nodes/test_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -538,5 +538,5 @@ def test_build_node_with_primitive_var(self):

self.assertIsInstance(actual, PrimitiveNode)
self.assertEqual(ns_map, actual.ns_map)
self.assertEqual(self.meta, actual.meta)
self.assertEqual(var, actual.var)
self.assertEqual(self.node.meta.mixed_content, actual.mixed)
36 changes: 19 additions & 17 deletions tests/formats/dataclass/parsers/nodes/test_primitive.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
from unittest import TestCase, mock

from tests.fixtures.artists import Artist
from xsdata.exceptions import XmlContextError
from xsdata.formats.dataclass.models.elements import XmlType
from xsdata.formats.dataclass.parsers.nodes import PrimitiveNode
from xsdata.formats.dataclass.parsers.utils import ParserUtils
from xsdata.utils.testing import XmlVarFactory
from xsdata.utils.testing import XmlMetaFactory, XmlVarFactory


class PrimitiveNodeTests(TestCase):
@mock.patch.object(ParserUtils, "parse_value")
def test_bind(self, mock_parse_value):
mock_parse_value.return_value = 13
def setUp(self):
super().setUp()
self.meta = XmlMetaFactory.create(clazz=Artist)

@mock.patch.object(ParserUtils, "parse_var")
def test_bind(self, mock_parse_var):
mock_parse_var.return_value = 13
var = XmlVarFactory.create(
xml_type=XmlType.TEXT, name="foo", types=(int,), format="Nope"
)
ns_map = {"foo": "bar"}
node = PrimitiveNode(var, ns_map, False)
node = PrimitiveNode(self.meta, var, ns_map)
objects = []

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

mock_parse_value.assert_called_once_with(
value="13",
types=var.types,
default=var.default,
ns_map=ns_map,
tokens_factory=var.tokens_factory,
format=var.format,
mock_parse_var.assert_called_once_with(
meta=self.meta, var=var, value="13", ns_map=ns_map
)

def test_bind_nillable_content(self):
var = XmlVarFactory.create(
xml_type=XmlType.TEXT, name="foo", types=(str,), nillable=False
)
ns_map = {"foo": "bar"}
node = PrimitiveNode(var, ns_map, False)
node = PrimitiveNode(self.meta, var, ns_map)
objects = []

self.assertTrue(node.bind("foo", None, None, objects))
Expand All @@ -53,7 +53,7 @@ def test_bind_nillable_bytes_content(self):
nillable=False,
)
ns_map = {"foo": "bar"}
node = PrimitiveNode(var, ns_map, False)
node = PrimitiveNode(self.meta, var, ns_map)
objects = []

self.assertTrue(node.bind("foo", None, None, objects))
Expand All @@ -64,25 +64,27 @@ def test_bind_nillable_bytes_content(self):
self.assertIsNone(objects[-1][1])

def test_bind_mixed_with_tail_content(self):
self.meta.mixed_content = True
var = XmlVarFactory.create(xml_type=XmlType.TEXT, name="foo", types=(int,))
node = PrimitiveNode(var, {}, True)
node = PrimitiveNode(self.meta, var, {})
objects = []

self.assertTrue(node.bind("foo", "13", "tail", objects))
self.assertEqual((None, "tail"), objects[-1])
self.assertEqual(13, objects[-2][1])

def test_bind_mixed_without_tail_content(self):
self.meta.mixed_content = True
var = XmlVarFactory.create(xml_type=XmlType.TEXT, name="foo", types=(int,))
node = PrimitiveNode(var, {}, True)
node = PrimitiveNode(self.meta, var, {})
objects = []

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

def test_child(self):
var = XmlVarFactory.create(xml_type=XmlType.TEXT, name="foo")
node = PrimitiveNode(var, {}, False)
node = PrimitiveNode(self.meta, var, {})

with self.assertRaises(XmlContextError):
node.child("foo", {}, {}, 0)
26 changes: 17 additions & 9 deletions tests/formats/dataclass/parsers/nodes/test_standard.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,46 @@
from unittest import TestCase

from tests.fixtures.artists import Artist
from xsdata.exceptions import XmlContextError
from xsdata.formats.dataclass.models.generics import DerivedElement
from xsdata.formats.dataclass.parsers.nodes import StandardNode
from xsdata.models.enums import DataType
from xsdata.utils.testing import XmlMetaFactory, XmlVarFactory


class StandardNodeTests(TestCase):
def setUp(self):
super().setUp()
self.meta = XmlMetaFactory.create(clazz=Artist)
self.var = XmlVarFactory.create()

def test_bind_simple(self):
var = DataType.INT
node = StandardNode(var, {}, False, False)
datatype = DataType.INT
node = StandardNode(self.meta, self.var, datatype, {}, False, False)
objects = []

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

def test_bind_derived(self):
var = DataType.INT
node = StandardNode(var, {}, False, DerivedElement)
datatype = DataType.INT
node = StandardNode(self.meta, self.var, datatype, {}, False, DerivedElement)
objects = []

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

def test_bind_wrapper_type(self):
var = DataType.HEX_BINARY
node = StandardNode(var, {}, False, DerivedElement)
datatype = DataType.HEX_BINARY
node = StandardNode(self.meta, self.var, datatype, {}, False, DerivedElement)
objects = []

self.assertTrue(node.bind("a", "13", None, objects))
self.assertEqual(("a", DerivedElement(qname="a", value=b"\x13")), objects[-1])

def test_bind_nillable(self):
var = DataType.STRING
node = StandardNode(var, {}, True, None)
datatype = DataType.STRING
node = StandardNode(self.meta, self.var, datatype, {}, True, None)
objects = []

self.assertTrue(node.bind("a", None, None, objects))
Expand All @@ -44,7 +51,8 @@ def test_bind_nillable(self):
self.assertEqual(("a", ""), objects[-1])

def test_child(self):
node = StandardNode(DataType.STRING, {}, False, False)
datatype = DataType.STRING
node = StandardNode(self.meta, self.var, datatype, {}, False, False)

with self.assertRaises(XmlContextError):
node.child("foo", {}, {}, 0)
17 changes: 12 additions & 5 deletions tests/formats/dataclass/parsers/nodes/test_union.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
from typing import Union
from unittest import TestCase

from tests.fixtures.artists import Artist
from tests.fixtures.models import UnionType
from xsdata.exceptions import ParserError
from xsdata.formats.dataclass.context import XmlContext
from xsdata.formats.dataclass.models.elements import XmlType
from xsdata.formats.dataclass.parsers.config import ParserConfig
from xsdata.formats.dataclass.parsers.nodes import UnionNode
from xsdata.models.mixins import attribute
from xsdata.utils.testing import XmlVarFactory
from xsdata.utils.testing import XmlMetaFactory, XmlVarFactory


class UnionNodeTests(TestCase):
Expand All @@ -22,10 +23,12 @@ def setUp(self) -> None:
def test_child(self):
attrs = {"id": "1"}
ns_map = {"ns0": "xsdata"}
meta = XmlMetaFactory.create(clazz=Artist)
var = XmlVarFactory.create(xml_type=XmlType.TEXT, name="foo")
node = UnionNode(
position=0,
meta=meta,
var=var,
position=0,
config=self.config,
context=self.context,
attrs={},
Expand All @@ -38,10 +41,12 @@ def test_child(self):
self.assertIsNot(attrs, node.events[0][2])

def test_bind_appends_end_event_when_level_not_zero(self):
meta = XmlMetaFactory.create(clazz=Artist)
var = XmlVarFactory.create(xml_type=XmlType.TEXT, name="foo")
node = UnionNode(
position=0,
meta=meta,
var=var,
position=0,
config=self.config,
context=self.context,
attrs={},
Expand All @@ -67,8 +72,9 @@ def test_bind_returns_best_matching_object(self):
attrs = {"a": "1", "b": 2}
ns_map = {}
node = UnionNode(
position=0,
meta=meta,
var=var,
position=0,
config=self.config,
context=self.context,
attrs=attrs,
Expand Down Expand Up @@ -102,8 +108,9 @@ def test_bind_raises_parser_error_on_failure(self):
var = next(meta.find_children("element"))

node = UnionNode(
position=0,
meta=meta,
var=var,
position=0,
config=self.config,
context=self.context,
attrs={},
Expand Down
2 changes: 1 addition & 1 deletion tests/formats/dataclass/parsers/test_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def test_decode_with_fail_on_converter_warnings(self):
self.decoder.decode(json_str, TypeA)

self.assertEqual(
"Failed to convert value `foo` to one of (<class 'int'>,)",
"Failed to convert value for `TypeA.x`\n `foo` is not a valid `int`",
str(cm.exception),
)

Expand Down
2 changes: 1 addition & 1 deletion tests/formats/dataclass/parsers/test_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def test_parse_with_fail_on_converter_warnings(self):
parser.from_string(xml, TypeA)

self.assertEqual(
"Failed to convert value `foo` to one of (<class 'int'>,)",
"Failed to convert value for `TypeA.x`\n `foo` is not a valid `int`",
str(cm.exception),
)

Expand Down
16 changes: 16 additions & 0 deletions tests/formats/dataclass/parsers/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import warnings
from unittest import mock

from tests.fixtures.models import TypeA
Expand Down Expand Up @@ -114,3 +115,18 @@ def test_validate_fixed_value(self):

var = XmlVarFactory.create("fixed", default=lambda: float("nan"))
ParserUtils.validate_fixed_value(meta, var, float("nan"))

def test_parse_var_with_warnings(self):
meta = XmlMetaFactory.create(clazz=TypeA, qname="foo")
var = XmlVarFactory.create("fixed", default="a")

with warnings.catch_warnings(record=True) as w:
result = ParserUtils.parse_var(meta, var, "a", types=[int, float])

expected = (
"Failed to convert value for `TypeA.fixed`\n"
" `a` is not a valid `int | float`"
)
self.assertEqual("a", result)

self.assertEqual(expected, str(w[-1].message))
6 changes: 4 additions & 2 deletions tests/formats/dataclass/parsers/test_xml.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from unittest import mock

from tests.fixtures.artists import Artist
from tests.fixtures.books import Books
from xsdata.formats.dataclass.models.elements import XmlType
from xsdata.formats.dataclass.parsers.nodes import PrimitiveNode, SkipNode
from xsdata.formats.dataclass.parsers.xml import UserXmlParser
from xsdata.models.enums import EventType
from xsdata.utils.testing import FactoryTestCase, XmlVarFactory
from xsdata.utils.testing import FactoryTestCase, XmlMetaFactory, XmlVarFactory


class UserXmlParserTests(FactoryTestCase):
Expand All @@ -30,8 +31,9 @@ def test_start(self, mock_emit_event):
def test_end(self, mock_emit_event):
objects = []
queue = []
meta = XmlMetaFactory.create(clazz=Artist)
var = XmlVarFactory.create(xml_type=XmlType.TEXT, name="foo", types=(bool,))
queue.append(PrimitiveNode(var, {}, False))
queue.append(PrimitiveNode(meta, var, {}))

result = self.parser.end(queue, objects, "enabled", "true", None)
self.assertTrue(result)
Expand Down
20 changes: 8 additions & 12 deletions tests/formats/test_converter.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import sys
import warnings
from datetime import date, datetime, time
from decimal import Decimal
from enum import Enum
Expand All @@ -18,12 +17,10 @@

class ConverterFactoryTests(TestCase):
def test_deserialize(self):
with warnings.catch_warnings(record=True) as w:
self.assertEqual("a", converter.deserialize("a", [int]))
with self.assertRaises(ConverterError) as cm:
converter.deserialize("a", [int])

self.assertEqual(
"Failed to convert value `a` to one of [<class 'int'>]", str(w[-1].message)
)
self.assertEqual("`a` is not a valid `int`", str(cm.exception))

self.assertFalse(converter.deserialize("false", [int, bool]))
self.assertEqual(1, converter.deserialize("1", [int, bool]))
Expand Down Expand Up @@ -73,13 +70,12 @@ def test_unknown_converter(self):
class A:
pass

class B(A):
pass

with warnings.catch_warnings(record=True) as w:
converter.serialize(B())
with self.assertRaises(ConverterError) as cm:
converter.serialize(A())

self.assertEqual(f"No converter registered for `{B}`", str(w[-1].message))
self.assertEqual(
f"No converter registered for `{A.__qualname__}`", str(cm.exception)
)

def test_register_converter(self):
class MinusOneInt(int):
Expand Down
Loading
Loading