From f3f1ca369df77327c3b6910f108a014b6414a273 Mon Sep 17 00:00:00 2001 From: Christodoulos Tsoulloftas Date: Tue, 27 Feb 2024 21:17:02 +0200 Subject: [PATCH] feat: Prevent classes with ambiguous choices --- tests/fixtures/models.py | 21 ++-- .../formats/dataclass/models/test_builders.py | 104 ++++++++++-------- .../dataclass/parsers/nodes/test_element.py | 2 - .../dataclass/parsers/nodes/test_primitive.py | 36 ++---- tests/formats/dataclass/parsers/test_dict.py | 16 +-- tests/formats/dataclass/parsers/test_node.py | 2 +- tests/formats/dataclass/parsers/test_xml.py | 2 +- tests/formats/dataclass/test_elements.py | 9 +- xsdata/formats/dataclass/models/builders.py | 35 ++++-- xsdata/formats/dataclass/models/elements.py | 4 - .../dataclass/parsers/nodes/element.py | 13 ++- .../dataclass/parsers/nodes/primitive.py | 11 +- .../dataclass/parsers/nodes/wildcard.py | 2 +- xsdata/utils/testing.py | 2 - 14 files changed, 129 insertions(+), 130 deletions(-) diff --git a/tests/fixtures/models.py b/tests/fixtures/models.py index e358c14cf..34f19437a 100644 --- a/tests/fixtures/models.py +++ b/tests/fixtures/models.py @@ -95,17 +95,10 @@ class ChoiceType: {"name": "a", "type": TypeA}, {"name": "b", "type": TypeB}, {"name": "int", "type": int}, - {"name": "int2", "type": int, "nillable": True}, {"name": "float", "type": float}, {"name": "qname", "type": QName}, - { - "name": "tokens", - "type": List[int], - "tokens": True, - "default_factory": return_true - }, {"name": "union", "type": Type["UnionType"], "namespace": "foo"}, - {"name": "p", "type": float, "fixed": True, "default": 1.1}, + {"name": "tokens", "type": List[str], "tokens": True}, { "wildcard": True, "type": object, @@ -128,6 +121,18 @@ class OptionalChoiceType: ) +@dataclass +class AmbiguousChoiceType: + choice: int = field( + metadata={ + "type": "Elements", + "choices": ( + {"name": "a", "type": int}, + {"name": "b", "type": int}, + ), + } + ) + @dataclass class UnionType: diff --git a/tests/formats/dataclass/models/test_builders.py b/tests/formats/dataclass/models/test_builders.py index 76c0bf5ac..79f6ced65 100644 --- a/tests/formats/dataclass/models/test_builders.py +++ b/tests/formats/dataclass/models/test_builders.py @@ -1,3 +1,4 @@ +import functools import sys import uuid from dataclasses import dataclass, field, fields, make_dataclass @@ -7,7 +8,15 @@ from tests.fixtures.artists import Artist from tests.fixtures.books import BookForm -from tests.fixtures.models import ChoiceType, Parent, TypeA, TypeB, TypeNS1, UnionType +from tests.fixtures.models import ( + AmbiguousChoiceType, + ChoiceType, + Parent, + TypeA, + TypeB, + TypeNS1, + UnionType, +) from tests.fixtures.series import Country from tests.fixtures.submodels import ChoiceTypeChild from xsdata.exceptions import XmlContextError @@ -16,7 +25,7 @@ from xsdata.formats.dataclass.models.elements import XmlMeta, XmlType from xsdata.models.datatype import XmlDate from xsdata.utils import text -from xsdata.utils.constants import return_input, return_true +from xsdata.utils.constants import return_input from xsdata.utils.namespaces import build_qname from xsdata.utils.testing import FactoryTestCase, XmlMetaFactory, XmlVarFactory @@ -132,7 +141,7 @@ def test_build_with_no_dataclass_raises_exception(self, *args): def test_build_locates_globalns_per_field(self): actual = self.builder.build(ChoiceTypeChild, None) self.assertEqual(1, len(actual.choices)) - self.assertEqual(9, len(actual.choices[0].elements)) + self.assertEqual(7, len(actual.choices[0].elements)) with self.assertRaises(XmlContextError): self.builder.find_declared_class(object, "foo") @@ -276,6 +285,8 @@ def test_default_xml_type(self): class XmlVarBuilderTests(TestCase): + maxDiff = None + def setUp(self) -> None: self.builder = XmlVarBuilder( class_type=class_types.get_type("dataclasses"), @@ -285,15 +296,14 @@ def setUp(self) -> None: ) super().setUp() - self.maxDiff = None def test_build_with_choice_field(self): globalns = sys.modules[ChoiceType.__module__].__dict__ type_hints = get_type_hints(ChoiceType) class_field = fields(ChoiceType)[0] - self.maxDiff = None actual = self.builder.build( + ChoiceType, "choice", type_hints["choice"], class_field.metadata, @@ -337,18 +347,8 @@ def test_build_with_choice_field(self): factory=list, namespaces=("bar",), ), - "{bar}int2": XmlVarFactory.create( - index=5, - name="choice", - qname="{bar}int2", - types=(int,), - derived=True, - nillable=True, - factory=list, - namespaces=("bar",), - ), "{bar}float": XmlVarFactory.create( - index=6, + index=5, name="choice", qname="{bar}float", types=(float,), @@ -356,26 +356,15 @@ def test_build_with_choice_field(self): namespaces=("bar",), ), "{bar}qname": XmlVarFactory.create( - index=7, + index=6, name="choice", qname="{bar}qname", types=(QName,), factory=list, namespaces=("bar",), ), - "{bar}tokens": XmlVarFactory.create( - index=8, - name="choice", - qname="{bar}tokens", - types=(int,), - tokens_factory=list, - derived=True, - factory=list, - default=return_true, - namespaces=("bar",), - ), "{foo}union": XmlVarFactory.create( - index=9, + index=7, name="choice", qname="{foo}union", types=(UnionType,), @@ -383,20 +372,20 @@ def test_build_with_choice_field(self): factory=list, namespaces=("foo",), ), - "{bar}p": XmlVarFactory.create( - index=10, + "{bar}tokens": XmlVarFactory.create( + index=8, name="choice", - qname="{bar}p", - types=(float,), + qname="{bar}tokens", + types=(str,), + tokens_factory=list, derived=True, factory=list, - default=1.1, namespaces=("bar",), ), }, wildcards=[ XmlVarFactory.create( - index=11, + index=9, name="choice", xml_type=XmlType.WILDCARD, qname="{http://www.w3.org/1999/xhtml}any", @@ -408,17 +397,44 @@ def test_build_with_choice_field(self): ], ) - self.maxDiff = None self.assertEqual(expected, actual) + def test_build_with_ambiguous_choices(self): + type_hints = get_type_hints(AmbiguousChoiceType) + class_field = fields(AmbiguousChoiceType)[0] + + with self.assertRaises(XmlContextError) as cm: + self.builder.build( + AmbiguousChoiceType, + "choice", + type_hints["choice"], + class_field.metadata, + True, + None, + None, + {}, + ) + + self.assertEqual( + "Error on AmbiguousChoiceType::choice: Compound field contains ambiguous types", + str(cm.exception), + ) + def test_build_validates_result(self): with self.assertRaises(XmlContextError) as cm: self.builder.build( - "foo", List[int], {"type": "Attributes"}, True, None, None, None + BookForm, + "foo", + List[int], + {"type": "Attributes"}, + True, + None, + None, + None, ) self.assertEqual( - "Xml type 'Attributes' does not support typing: typing.List[int]", + "Error on BookForm::foo: Xml Attributes does not support typing `typing.List[int]`", str(cm.exception), ) @@ -465,20 +481,22 @@ def test_resolve_namespaces(self): self.assertEqual(("foo", "p"), tuple(sorted(actual))) def test_analyze_types(self): - actual = self.builder.analyze_types(List[List[Union[str, int]]], None) + func = functools.partial(self.builder.analyze_types, BookForm, "foo") + + actual = func(List[List[Union[str, int]]], None) self.assertEqual((list, list, (int, str)), actual) - actual = self.builder.analyze_types(Union[str, int], None) + actual = func(Union[str, int], None) self.assertEqual((None, None, (int, str)), actual) - actual = self.builder.analyze_types(Dict[str, int], None) + actual = func(Dict[str, int], None) self.assertEqual((dict, None, (int, str)), actual) with self.assertRaises(XmlContextError) as cm: - self.builder.analyze_types(List[List[List[int]]], None) + func(List[List[List[int]]], None) self.assertEqual( - "Unsupported typing: typing.List[typing.List[typing.List[int]]]", + "Error on BookForm::foo: Unsupported field typing `typing.List[typing.List[typing.List[int]]]`", str(cm.exception), ) diff --git a/tests/formats/dataclass/parsers/nodes/test_element.py b/tests/formats/dataclass/parsers/nodes/test_element.py index 21f3f7840..6aaa0beae 100644 --- a/tests/formats/dataclass/parsers/nodes/test_element.py +++ b/tests/formats/dataclass/parsers/nodes/test_element.py @@ -371,7 +371,6 @@ def test_build_node_with_dataclass_var(self, mock_ctx_fetch, mock_xsi_type): name="a", qname="a", types=(TypeC,), - derived=True, ) xsi_type = "foo" namespace = self.meta.namespace @@ -384,7 +383,6 @@ def test_build_node_with_dataclass_var(self, mock_ctx_fetch, mock_xsi_type): self.assertIsInstance(actual, ElementNode) self.assertEqual(10, actual.position) - self.assertEqual(DerivedElement, actual.derived_factory) self.assertIs(mock_ctx_fetch.return_value, actual.meta) mock_xsi_type.assert_called_once_with(attrs, ns_map) diff --git a/tests/formats/dataclass/parsers/nodes/test_primitive.py b/tests/formats/dataclass/parsers/nodes/test_primitive.py index 05ff90444..a644789f4 100644 --- a/tests/formats/dataclass/parsers/nodes/test_primitive.py +++ b/tests/formats/dataclass/parsers/nodes/test_primitive.py @@ -2,7 +2,6 @@ from xsdata.exceptions import XmlContextError from xsdata.formats.dataclass.models.elements import XmlType -from xsdata.formats.dataclass.models.generics import DerivedElement from xsdata.formats.dataclass.parsers.nodes import PrimitiveNode from xsdata.formats.dataclass.parsers.utils import ParserUtils from xsdata.utils.testing import XmlVarFactory @@ -16,7 +15,7 @@ def test_bind(self, mock_parse_value): xml_type=XmlType.TEXT, name="foo", qname="foo", types=(int,), format="Nope" ) ns_map = {"foo": "bar"} - node = PrimitiveNode(var, ns_map, False, DerivedElement) + node = PrimitiveNode(var, ns_map, False) objects = [] self.assertTrue(node.bind("foo", "13", "Impossible", objects)) @@ -31,23 +30,12 @@ def test_bind(self, mock_parse_value): format=var.format, ) - def test_bind_derived_mode(self): - var = XmlVarFactory.create( - xml_type=XmlType.TEXT, name="foo", qname="foo", types=(int,), derived=True - ) - ns_map = {"foo": "bar"} - node = PrimitiveNode(var, ns_map, False, DerivedElement) - objects = [] - - self.assertTrue(node.bind("foo", "13", "Impossible", objects)) - self.assertEqual(DerivedElement("foo", 13), objects[-1][1]) - def test_bind_nillable_content(self): var = XmlVarFactory.create( xml_type=XmlType.TEXT, name="foo", qname="foo", types=(str,), nillable=False ) ns_map = {"foo": "bar"} - node = PrimitiveNode(var, ns_map, False, DerivedElement) + node = PrimitiveNode(var, ns_map, False) objects = [] self.assertTrue(node.bind("foo", None, None, objects)) @@ -66,7 +54,7 @@ def test_bind_nillable_bytes_content(self): nillable=False, ) ns_map = {"foo": "bar"} - node = PrimitiveNode(var, ns_map, False, DerivedElement) + node = PrimitiveNode(var, ns_map, False) objects = [] self.assertTrue(node.bind("foo", None, None, objects)) @@ -77,29 +65,25 @@ def test_bind_nillable_bytes_content(self): self.assertIsNone(objects[-1][1]) def test_bind_mixed_with_tail_content(self): - var = XmlVarFactory.create( - xml_type=XmlType.TEXT, name="foo", types=(int,), derived=True - ) - node = PrimitiveNode(var, {}, True, DerivedElement) + var = XmlVarFactory.create(xml_type=XmlType.TEXT, name="foo", types=(int,)) + node = PrimitiveNode(var, {}, True) objects = [] self.assertTrue(node.bind("foo", "13", "tail", objects)) self.assertEqual((None, "tail"), objects[-1]) - self.assertEqual(DerivedElement("foo", 13), objects[-2][1]) + self.assertEqual(13, objects[-2][1]) def test_bind_mixed_without_tail_content(self): - var = XmlVarFactory.create( - xml_type=XmlType.TEXT, name="foo", types=(int,), derived=True - ) - node = PrimitiveNode(var, {}, True, DerivedElement) + var = XmlVarFactory.create(xml_type=XmlType.TEXT, name="foo", types=(int,)) + node = PrimitiveNode(var, {}, True) objects = [] self.assertTrue(node.bind("foo", "13", "", objects)) - self.assertEqual(DerivedElement("foo", 13), objects[-1][1]) + self.assertEqual(13, objects[-1][1]) def test_child(self): var = XmlVarFactory.create(xml_type=XmlType.TEXT, name="foo", qname="foo") - node = PrimitiveNode(var, {}, False, DerivedElement) + node = PrimitiveNode(var, {}, False) with self.assertRaises(XmlContextError): node.child("foo", {}, {}, 0) diff --git a/tests/formats/dataclass/parsers/test_dict.py b/tests/formats/dataclass/parsers/test_dict.py index 77b1e164c..79648e268 100644 --- a/tests/formats/dataclass/parsers/test_dict.py +++ b/tests/formats/dataclass/parsers/test_dict.py @@ -232,17 +232,16 @@ def test_bind_simple_type_with_wildcard_var(self): self.assertEqual(2, actual.wildcard) def test_bind_simple_type_with_elements_var(self): - data = {"choice": ["1.0", 1, ["1"], "a", "{a}b"]} + data = {"choice": ["1.0", 1, "a", "{a}b"]} actual = self.decoder.bind_dataclass(data, ChoiceType) self.assertEqual(1.0, actual.choice[0]) self.assertEqual(1, actual.choice[1]) - self.assertEqual([1], actual.choice[2]) - self.assertEqual(QName("a"), actual.choice[3]) + self.assertEqual(QName("a"), actual.choice[2]) + self.assertIsInstance(actual.choice[2], QName) + self.assertEqual(QName("{a}b"), actual.choice[3]) self.assertIsInstance(actual.choice[3], QName) - self.assertEqual(QName("{a}b"), actual.choice[4]) - self.assertIsInstance(actual.choice[4], QName) data = {"choice": ["!NotAQname"]} with self.assertRaises(ParserError) as cm: @@ -278,13 +277,6 @@ def test_bind_choice_dataclass(self): expected = ChoiceType(choice=[TypeA(x=1), TypeB(x=1, y="a")]) self.assertEqual(expected, self.decoder.bind_dataclass(data, ChoiceType)) - def test_bind_derived_value_with_simple_type(self): - data = {"choice": [{"qname": "int2", "value": 1, "type": None}]} - - actual = self.decoder.bind_dataclass(data, ChoiceType) - expected = ChoiceType(choice=[DerivedElement(qname="int2", value=1)]) - self.assertEqual(expected, actual) - def test_bind_derived_value_with_choice_var(self): data = { "choice": [ diff --git a/tests/formats/dataclass/parsers/test_node.py b/tests/formats/dataclass/parsers/test_node.py index f72d8110e..caf86cd01 100644 --- a/tests/formats/dataclass/parsers/test_node.py +++ b/tests/formats/dataclass/parsers/test_node.py @@ -182,7 +182,7 @@ def test_end(self, mock_assemble): objects = [("q", "result")] queue = [] var = XmlVarFactory.create(xml_type=XmlType.TEXT, name="foo", qname="foo") - queue.append(PrimitiveNode(var, {}, False, DerivedElement)) + queue.append(PrimitiveNode(var, {}, False)) self.assertTrue(parser.end(queue, objects, "author", "foobar", None)) self.assertEqual(0, len(queue)) diff --git a/tests/formats/dataclass/parsers/test_xml.py b/tests/formats/dataclass/parsers/test_xml.py index 7bc3f6778..fa5af97b5 100644 --- a/tests/formats/dataclass/parsers/test_xml.py +++ b/tests/formats/dataclass/parsers/test_xml.py @@ -31,7 +31,7 @@ def test_end(self, mock_emit_event): objects = [] queue = [] var = XmlVarFactory.create(xml_type=XmlType.TEXT, name="foo", types=(bool,)) - queue.append(PrimitiveNode(var, {}, False, None)) + queue.append(PrimitiveNode(var, {}, False)) result = self.parser.end(queue, objects, "enabled", "true", None) self.assertTrue(result) diff --git a/tests/formats/dataclass/test_elements.py b/tests/formats/dataclass/test_elements.py index 6899d10cc..f2fba6e96 100644 --- a/tests/formats/dataclass/test_elements.py +++ b/tests/formats/dataclass/test_elements.py @@ -45,7 +45,7 @@ def test_property_element_types(self): meta = self.context.build(ChoiceType) var = meta.choices[0] self.assertEqual( - {TypeA, TypeB, int, float, QName, UnionType}, var.element_types + {TypeA, TypeB, int, float, QName, UnionType, str}, var.element_types ) def test_find_choice(self): @@ -88,14 +88,13 @@ def test_find_value_choice(self): meta = self.context.build(ChoiceType) var = meta.choices[0] - self.assertIsNone(var.find_value_choice(["1.1", "1.2"], False)) + self.assertEqual( + var.elements["tokens"], var.find_value_choice(["f", "e"], False) + ) self.assertIsNone(var.find_value_choice([], False)) - self.assertEqual(var.elements["int2"], var.find_value_choice(None, False)) self.assertEqual(var.elements["qname"], var.find_value_choice("foo", False)) self.assertEqual(var.elements["int"], var.find_value_choice(1, False)) - self.assertEqual(var.elements["tokens"], var.find_value_choice([1, 2], False)) self.assertEqual(var.elements["a"], var.find_value_choice(TypeA(1), True)) - der = make_dataclass("Der", fields=[], bases=(TypeA,)) self.assertEqual(var.elements["a"], var.find_value_choice(der(1), True)) diff --git a/xsdata/formats/dataclass/models/builders.py b/xsdata/formats/dataclass/models/builders.py index d7e804f05..85a7e18bf 100644 --- a/xsdata/formats/dataclass/models/builders.py +++ b/xsdata/formats/dataclass/models/builders.py @@ -190,6 +190,7 @@ def build_vars( parent_namespace = getattr(real_clazz.Meta, "namespace", namespace) var = builder.build( + clazz, field.name, type_hints[field.name], field.metadata, @@ -340,6 +341,7 @@ def __init__( def build( self, + model: Type, name: str, type_hint: Any, metadata: Mapping[str, Any], @@ -352,7 +354,8 @@ def build( """Build the binding metadata for a class field. Args: - name: The field name + model: The model class + name: The model field name type_hint: The typing annotations of the field metadata: The field metadata mapping init: Specify whether this field can be initialized @@ -380,18 +383,20 @@ def build( sequence = metadata.get("sequence", None) wrapper = metadata.get("wrapper", None) - origin, sub_origin, types = self.analyze_types(type_hint, globalns) + origin, sub_origin, types = self.analyze_types(model, name, type_hint, globalns) if not self.is_valid(xml_type, origin, sub_origin, types, tokens, init): raise XmlContextError( - f"Xml type '{xml_type}' does not support typing: {type_hint}" + f"Error on {model.__qualname__}::{name}: " + f"Xml {xml_type} does not support typing `{type_hint}`" ) if wrapper is not None and ( not isinstance(origin, type) or not issubclass(origin, (list, set, tuple)) ): raise XmlContextError( - f"a wrapper requires a collection type on attribute {name}" + f"Error on {model.__qualname__}::{name}: " + f"A wrapper field requires a collection type" ) local_name = local_name or self.build_local_name(xml_type, name) @@ -416,7 +421,7 @@ def build( self.index += 1 cur_index = self.index for choice in self.build_choices( - name, choices, origin, globalns, parent_namespace + model, name, choices, origin, globalns, parent_namespace ): if choice.is_element: elements[choice.qname] = choice @@ -444,12 +449,12 @@ def build( wildcards=wildcards, namespaces=namespaces, xml_type=xml_type, - derived=False, wrapper=wrapper, ) def build_choices( self, + model: Type, name: str, choices: List[Dict], factory: Callable, @@ -459,7 +464,8 @@ def build_choices( """Build the binding metadata for a compound dataclass field. Args: - name: The compound field name + model: The model class + name: The model field name choices: The list of choice metadata factory: The compound field values factory globalns: Python's global namespace @@ -483,6 +489,7 @@ def build_choices( metadata["type"] = XmlType.ELEMENT var = self.build( + model, name, type_hint, metadata, @@ -496,8 +503,11 @@ def build_choices( # It's impossible for choice elements to be ignorable, read above! assert var is not None - if var.any_type or any(True for tp in var.types if tp in existing_types): - var.derived = True + if any(True for tp in var.types if tp in existing_types): + raise XmlContextError( + f"Error on {model.__qualname__}::{name}: " + f"Compound field contains ambiguous types" + ) existing_types.update(var.types) @@ -588,7 +598,7 @@ def is_any_type(cls, types: Sequence[Type], xml_type: str) -> bool: @classmethod def analyze_types( - cls, type_hint: Any, globalns: Any + cls, model: Type, name: str, type_hint: Any, globalns: Any ) -> Tuple[Any, Any, Tuple[Type, ...]]: """Analyze a type hint and return the origin, sub origin and the type args. @@ -617,7 +627,10 @@ def analyze_types( return origin, sub_origin, tuple(converter.sort_types(types)) except Exception: - raise XmlContextError(f"Unsupported typing: {type_hint}") + raise XmlContextError( + f"Error on {model.__qualname__}::{name}: " + f"Unsupported field typing `{type_hint}`" + ) def is_valid( self, diff --git a/xsdata/formats/dataclass/models/elements.py b/xsdata/formats/dataclass/models/elements.py index 6179a91b9..c5d9fb006 100644 --- a/xsdata/formats/dataclass/models/elements.py +++ b/xsdata/formats/dataclass/models/elements.py @@ -69,7 +69,6 @@ class XmlVar(MetaMixin): factory: Callable factory for lists tokens_factory: Callable factory for tokens format: Information about the value format - derived: Indicates whether parsed values should be wrapped with a generic type any_type: Indicates if the field supports dynamic value types process_contents: Information about processing contents required: Indicates if the field is mandatory @@ -107,7 +106,6 @@ class XmlVar(MetaMixin): "factory", "tokens_factory", "format", - "derived", "any_type", "process_contents", "required", @@ -144,7 +142,6 @@ def __init__( factory: Optional[Callable], tokens_factory: Optional[Callable], format: Optional[str], - derived: bool, any_type: bool, process_contents: str, required: bool, @@ -167,7 +164,6 @@ def __init__( self.mixed = mixed self.tokens = tokens_factory is not None self.format = format - self.derived = derived self.any_type = any_type self.process_contents = process_contents self.required = required diff --git a/xsdata/formats/dataclass/parsers/nodes/element.py b/xsdata/formats/dataclass/parsers/nodes/element.py index 88e092e3e..24e71f5e5 100644 --- a/xsdata/formats/dataclass/parsers/nodes/element.py +++ b/xsdata/formats/dataclass/parsers/nodes/element.py @@ -501,7 +501,7 @@ def build_node( if var.clazz: return self.build_element_node( var.clazz, - var.derived, + False, var.nillable, attrs, ns_map, @@ -512,15 +512,16 @@ def build_node( ) if not var.any_type and not var.is_wildcard: - return nodes.PrimitiveNode( - var, ns_map, self.meta.mixed_content, derived_factory - ) + return nodes.PrimitiveNode(var, ns_map, self.meta.mixed_content) datatype = DataType.from_qname(xsi_type) if xsi_type else None - derived = var.derived or var.is_wildcard + derived = var.is_wildcard if datatype: return nodes.StandardNode( - datatype, ns_map, var.nillable, derived_factory if derived else None + datatype, + ns_map, + var.nillable, + derived_factory if derived else None, ) node = None diff --git a/xsdata/formats/dataclass/parsers/nodes/primitive.py b/xsdata/formats/dataclass/parsers/nodes/primitive.py index 4d3a41449..8cbcaef8c 100644 --- a/xsdata/formats/dataclass/parsers/nodes/primitive.py +++ b/xsdata/formats/dataclass/parsers/nodes/primitive.py @@ -1,4 +1,4 @@ -from typing import Dict, List, Optional, Type +from typing import Dict, List, Optional from xsdata.exceptions import XmlContextError from xsdata.formats.dataclass.models.elements import XmlVar @@ -13,15 +13,13 @@ class PrimitiveNode(XmlNode): var: The xml var instance ns_map: The element namespace prefix-URI map mixed: Specifies if this node supports mixed content - derived_factory: The derived element factory """ - __slots__ = "var", "ns_map", "derived_factory" + __slots__ = "var", "ns_map" - def __init__(self, var: XmlVar, ns_map: Dict, mixed: bool, derived_factory: Type): + def __init__(self, var: XmlVar, ns_map: Dict, mixed: bool): self.var = var self.ns_map = ns_map - self.derived_factory = derived_factory self.mixed = mixed def bind( @@ -58,9 +56,6 @@ def bind( if obj is None and not self.var.nillable: obj = b"" if bytes in self.var.types else "" - if self.var.derived: - obj = self.derived_factory(qname=qname, value=obj) - objects.append((qname, obj)) if self.mixed: diff --git a/xsdata/formats/dataclass/parsers/nodes/wildcard.py b/xsdata/formats/dataclass/parsers/nodes/wildcard.py index 4449c9d48..34ca69029 100644 --- a/xsdata/formats/dataclass/parsers/nodes/wildcard.py +++ b/xsdata/formats/dataclass/parsers/nodes/wildcard.py @@ -58,7 +58,7 @@ def bind( """ children = self.fetch_any_children(self.position, objects) attributes = ParserUtils.parse_any_attributes(self.attrs, self.ns_map) - derived = self.var.derived or qname != self.var.qname + derived = qname != self.var.qname text = ParserUtils.normalize_content(text) if children else text text = "" if text is None and not self.var.nillable else text tail = ParserUtils.normalize_content(tail) diff --git a/xsdata/utils/testing.py b/xsdata/utils/testing.py index 728551cef..11e6bc882 100644 --- a/xsdata/utils/testing.py +++ b/xsdata/utils/testing.py @@ -372,7 +372,6 @@ def create( factory: Optional[Callable] = None, tokens_factory: Optional[Callable] = None, format: Optional[str] = None, - derived: bool = False, any_type: bool = False, required: bool = False, nillable: bool = False, @@ -411,7 +410,6 @@ def create( factory=factory, tokens_factory=tokens_factory, format=format, - derived=derived, any_type=any_type, required=required, nillable=nillable,