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

xsd:string text nodes should have empty string as default value #570

Merged
merged 6 commits into from
Jul 30, 2021
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
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

repos:
- repo: https://github.com/asottile/pyupgrade
rev: v2.21.2
rev: v2.23.1
hooks:
- id: pyupgrade
args: [--py37-plus]
- repo: https://github.com/asottile/reorder_python_imports
rev: v2.5.0
rev: v2.6.0
hooks:
- id: reorder-python-imports
- repo: https://github.com/ambv/black
Expand Down
20 changes: 16 additions & 4 deletions tests/codegen/handlers/test_attribute_default_value.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from xsdata.models.config import GeneratorConfig
from xsdata.models.enums import DataType
from xsdata.models.enums import Namespace
from xsdata.models.enums import Tag
from xsdata.utils.testing import AttrFactory
from xsdata.utils.testing import AttrTypeFactory
from xsdata.utils.testing import ClassFactory
Expand Down Expand Up @@ -58,7 +59,7 @@ def test_process_with_attr_choices(self, mock_process_attribute):

def test_process_attribute_with_choice_field(self):
target = ClassFactory.create()
attr = AttrFactory.create(fixed=True, default=2)
attr = AttrFactory.element(fixed=True, default=2)
attr.restrictions.min_occurs = 1
attr.restrictions.choice = "a"
self.processor.process_attribute(target, attr)
Expand All @@ -73,7 +74,7 @@ def test_process_attribute_with_choice_field(self):

def test_process_attribute_with_sequential_field(self):
target = ClassFactory.create()
attr = AttrFactory.create(fixed=True, default=2)
attr = AttrFactory.element(fixed=True, default=2)
attr.restrictions.min_occurs = 1
attr.restrictions.sequential = True
self.processor.process_attribute(target, attr)
Expand All @@ -88,15 +89,15 @@ def test_process_attribute_with_sequential_field(self):

def test_process_attribute_with_list_field(self):
target = ClassFactory.create()
attr = AttrFactory.create(fixed=True, default=2)
attr = AttrFactory.element(fixed=True, default=2)
attr.restrictions.max_occurs = 5
self.processor.process_attribute(target, attr)
self.assertFalse(attr.fixed)
self.assertIsNone(attr.default)

def test_process_attribute_with_xsi_type(self):
target = ClassFactory.create()
attr = AttrFactory.create(
attr = AttrFactory.attribute(
fixed=True,
default="xs:int",
name="type",
Expand All @@ -115,6 +116,17 @@ def test_process_attribute_with_valid_case(self):
self.assertTrue(attr.fixed)
self.assertEqual(2, attr.default)

def test_process_attribute_with_text_attr(self):
target = ClassFactory.create()

attr = AttrFactory.native(DataType.INT, tag=Tag.EXTENSION)
self.processor.process_attribute(target, attr)
self.assertIsNone(attr.default)

attr = AttrFactory.native(DataType.STRING, tag=Tag.EXTENSION)
self.processor.process_attribute(target, attr)
self.assertEqual("", attr.default)

@mock.patch("xsdata.codegen.handlers.attribute_default_value.logger.warning")
@mock.patch.object(AttributeDefaultValueHandler, "find_enum")
def test_process_attribute_enum(self, mock_find_enum, mock_logger_warning):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from xsdata.codegen.handlers import AttributeDefaultValidateHandler
from xsdata.codegen.handlers import AttributeNormalizeHandler
from xsdata.formats.dataclass.models.elements import XmlType
from xsdata.models.enums import DataType
from xsdata.models.enums import Tag
Expand All @@ -12,42 +12,60 @@ class AttributeDefaultValidateHandlerTests(FactoryTestCase):
def setUp(self):
super().setUp()

self.processor = AttributeDefaultValidateHandler()
self.processor = AttributeNormalizeHandler()

def test_process(self):
def test_cascade_properties(self):
target = ClassFactory.create(
default="4",
fixed=True,
nillable=True,
attrs=[
AttrFactory.native(DataType.STRING, tag=Tag.SIMPLE_TYPE),
AttrFactory.native(DataType.STRING, tag=Tag.EXTENSION),
AttrFactory.native(DataType.STRING, tag=Tag.SIMPLE_TYPE, default="1"),
AttrFactory.native(DataType.STRING, tag=Tag.ATTRIBUTE, default="2"),
AttrFactory.native(DataType.STRING, tag=Tag.ELEMENT),
],
)

self.processor.process(target)

for attr in target.attrs:
self.assertNotEqual("4", attr.default)
self.assertFalse(attr.fixed)
self.assertIsNone(target.attrs[0].default)
self.assertFalse(target.attrs[0].fixed)
self.assertFalse(target.attrs[0].restrictions.nillable)

if attr.xml_type == XmlType.ELEMENT:
self.assertIsNone(attr.default)
self.assertEqual(0, attr.restrictions.min_occurs)
self.assertEqual("1", target.attrs[1].default)
self.assertFalse(target.attrs[1].fixed)
self.assertFalse(target.attrs[1].restrictions.nillable)

self.assertEqual("2", target.attrs[2].default)
self.assertFalse(target.attrs[2].fixed)
self.assertFalse(target.attrs[2].restrictions.nillable)

self.assertIsNone(target.attrs[3].default)
self.assertFalse(target.attrs[3].fixed)
self.assertFalse(target.attrs[3].restrictions.nillable)

target.nillable = False
target.default = "0"
target.fixed = True
target.nillable = True
self.processor.process(target)

# Xml text field with no default value
self.assertEqual("4", target.attrs[0].default)
self.assertEqual(True, target.attrs[0].fixed)
# text value field inherits default/fixed/nillable if they are not set
self.assertEqual("0", target.attrs[0].default)
self.assertTrue(target.attrs[0].fixed)
self.assertTrue(target.attrs[0].restrictions.nillable)

# The rest are untouched.
# text value field inherits only nillable the rest are already set
self.assertEqual("1", target.attrs[1].default)
self.assertFalse(target.attrs[1].fixed)
self.assertIsNone(target.attrs[2].default)
self.assertTrue(target.attrs[1].restrictions.nillable)

# attribute field ignored
self.assertEqual("2", target.attrs[2].default)
self.assertFalse(target.attrs[2].fixed)
self.assertFalse(target.attrs[2].restrictions.nillable)

# element field ignored
self.assertIsNone(target.attrs[3].default)
self.assertFalse(target.attrs[3].fixed)
self.assertFalse(target.attrs[3].restrictions.nillable)

def test_reset_unsupported_types_ignore_user_types(self):
attr_type = AttrTypeFactory.create(qname="foo")
Expand Down Expand Up @@ -121,3 +139,22 @@ def test_reset_unsupported_types_with_different_bytes_lexical_repr(self):
self.assertEqual(DataType.STRING, attr.types[0].datatype)
self.assertIsNone(attr.restrictions.format)
self.assertTrue(attr.restrictions.tokens)

def test_process_reset_min_occurs(self):
first = AttrFactory.native(DataType.ANY_TYPE)
first.restrictions.min_occurs = 1
first.restrictions.max_occurs = 2
second = AttrFactory.native(DataType.ANY_TYPE)
second.restrictions.min_occurs = 1
second.default = "foo"
third = AttrFactory.native(DataType.ANY_TYPE)
third.restrictions.min_occurs = 1

target = ClassFactory.create(attrs=[first, second, third])

self.processor.process(target)

self.assertEqual(1, first.restrictions.min_occurs)
self.assertEqual(2, first.restrictions.max_occurs)
self.assertEqual(1, second.restrictions.min_occurs)
self.assertEqual(0, third.restrictions.min_occurs)
16 changes: 8 additions & 8 deletions tests/codegen/handlers/test_attribute_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,18 +379,18 @@ def test_cached_dependencies(self, mock_class_dependencies):
mock_class_dependencies.assert_called_once_with()

def test_update_restrictions(self):
attr = AttrFactory.create()
self.processor.update_restrictions(attr, DataType.NMTOKENS)
attr = AttrFactory.native(DataType.NMTOKENS)
self.processor.update_restrictions(attr, attr.types[0].datatype)
self.assertTrue(attr.restrictions.tokens)

attr = AttrFactory.create()
self.processor.update_restrictions(attr, DataType.IDREFS)
attr = AttrFactory.native(DataType.IDREFS)
self.processor.update_restrictions(attr, attr.types[0].datatype)
self.assertTrue(attr.restrictions.tokens)

attr = AttrFactory.create()
self.processor.update_restrictions(attr, DataType.BASE64_BINARY)
attr = AttrFactory.native(DataType.BASE64_BINARY)
self.processor.update_restrictions(attr, attr.types[0].datatype)
self.assertEqual("base64", attr.restrictions.format)

attr = AttrFactory.create()
self.processor.update_restrictions(attr, DataType.HEX_BINARY)
attr = AttrFactory.native(DataType.HEX_BINARY)
self.processor.update_restrictions(attr, attr.types[0].datatype)
self.assertEqual("base16", attr.restrictions.format)
3 changes: 2 additions & 1 deletion tests/codegen/handlers/test_class_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ def test_add_default_attribute(self):
ClassExtensionHandler.add_default_attribute(item, extension)

expected.types.append(xs_int)
expected_restrictions = Restrictions(tokens=True, min_occurs=0, max_occurs=1)
expected_restrictions = Restrictions(tokens=True, min_occurs=1, max_occurs=1)

self.assertEqual(2, len(item.attrs))
self.assertEqual(0, len(item.extensions))
Expand All @@ -478,3 +478,4 @@ def test_add_default_attribute_with_any_type(self):
self.assertEqual(1, len(item.attrs))
self.assertEqual(0, len(item.extensions))
self.assertEqual(expected, item.attrs[0])
self.assertEqual(expected.restrictions, item.attrs[0].restrictions)
64 changes: 54 additions & 10 deletions tests/codegen/mappers/test_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from unittest import mock

from xsdata.codegen.mappers.element import ElementMapper
from xsdata.codegen.models import Restrictions
from xsdata.codegen.utils import ClassUtils
from xsdata.formats.dataclass.models.generics import AnyElement
from xsdata.models.enums import DataType
Expand Down Expand Up @@ -132,13 +133,53 @@ def test_build_class_mixed_content(self):
ns_map={},
attrs=[
AttrFactory.native(
DataType.STRING, name="child", namespace="xsdata", index=0
DataType.STRING,
name="child",
namespace="xsdata",
index=0,
restrictions=Restrictions(min_occurs=0, max_occurs=1),
),
AttrFactory.native(
DataType.STRING,
namespace="",
name="something",
index=1,
restrictions=Restrictions(min_occurs=0, max_occurs=1),
),
AttrFactory.native(DataType.STRING, name="something", index=1),
],
)
self.assertEqual(expected, actual)

element = AnyElement(
qname="{xsdata}root",
text="foo",
children=[
AnyElement(qname="{xsdata}child", text="primitive"),
],
)
actual = ElementMapper.build_class(element, None)
self.assertTrue(actual.mixed)

def test_build_class_nillable(self):
element = AnyElement(qname="{xsdata}root", attributes={QNames.XSI_NIL: "1"})
target = ElementMapper.build_class(element, None)
self.assertTrue(target.nillable)

element.attributes[QNames.XSI_NIL] = " true "
target = ElementMapper.build_class(element, None)
self.assertTrue(target.nillable)

def test_build_class_ignore_invalid(self):
element = AnyElement(
qname="{xsdata}root",
children=[
AnyElement(text="primitive"),
"",
],
)
actual = ElementMapper.build_class(element, None)
self.assertEqual(0, len(actual.attrs))

def test_build_attribute_type(self):
actual = ElementMapper.build_attribute_type(QNames.XSI_TYPE, "")
self.assertEqual(str(DataType.QNAME), actual.qname)
Expand Down Expand Up @@ -197,19 +238,22 @@ def test_select_namespace(self):
def test_sequential_names(self):
a = AnyElement(qname="a")
b = AnyElement(qname="b")
c = AnyElement(qname="c")
d = AnyElement(qname="d")
e = AnyElement(qname="e")

actual = ElementMapper.sequential_names([a, b])
p = AnyElement(children=[a, b])
actual = ElementMapper.sequential_names(p)
self.assertEqual(0, len(actual))

actual = ElementMapper.sequential_names([a, b, a])
p = AnyElement(children=[a, b, a])
actual = ElementMapper.sequential_names(p)
self.assertEqual({"a", "b"}, actual)

c = AnyElement(qname="c")
actual = ElementMapper.sequential_names([a, b, a, c])
p = AnyElement(children=[a, b, a, c])
actual = ElementMapper.sequential_names(p)
self.assertEqual({"a", "b"}, actual)

d = AnyElement(qname="d")
e = AnyElement(qname="e")

actual = ElementMapper.sequential_names([a, b, a, c, d, e, d])
p = AnyElement(children=[a, b, a, c, d, e, d])
actual = ElementMapper.sequential_names(p)
self.assertEqual({"a", "b", "d", "e"}, actual)
2 changes: 1 addition & 1 deletion tests/codegen/test_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def test_initialize(self):
"AttributeTypeHandler",
"AttributeMergeHandler",
"AttributeMixedContentHandler",
"AttributeDefaultValidateHandler",
"AttributeNormalizeHandler",
],
20: [
"AttributeEffectiveChoiceHandler",
Expand Down
8 changes: 4 additions & 4 deletions tests/fixtures/artists/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ class Meta:
"type": "Attribute",
}
)
value: Optional[str] = field(
default=None
value: str = field(
default=""
)


Expand Down Expand Up @@ -87,8 +87,8 @@ class Meta:
"type": "Attribute",
}
)
value: Optional[str] = field(
default=None
value: str = field(
default=""
)


Expand Down
12 changes: 12 additions & 0 deletions tests/fixtures/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@ class BaseB(BaseA):
class BaseC(BaseB):
z: str

@dataclass
class NillableType:
class Meta:
nillable = True

value: Optional[str] = field(default="abc")


@dataclass
class FixedType:
value: str = field(init=False, default="abc")


@dataclass
class ExtendedType:
Expand Down
7 changes: 5 additions & 2 deletions tests/fixtures/primer/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,11 @@ class Comment:
class Meta:
name = "comment"

value: Optional[str] = field(
default=None
value: str = field(
default="",
metadata={
"required": True,
}
)


Expand Down
Loading