Skip to content

Commit

Permalink
Remove unused optional argument from content sorting methods
Browse files Browse the repository at this point in the history
  - Deprecate ModelVisitor methods iter_unordered_content() and
    iter_collapsed_content()
  • Loading branch information
brunato committed Apr 17, 2024
1 parent 0fdaedf commit 39ad414
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 29 deletions.
69 changes: 59 additions & 10 deletions tests/validators/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"""Tests concerning model groups validation"""
import unittest
import os.path
import warnings
from textwrap import dedent
from typing import Any, Union, List, Optional

Expand Down Expand Up @@ -1076,22 +1077,12 @@ def test_iter_collapsed_content_with_optional_elements(self):
""")

group = schema.types['A_type'].content
model = ModelVisitor(group)

content = [('B3', 10), ('B4', None), ('B5', True), ('B6', 'alpha'), ('B7', 20)]
model.restart()
self.assertListEqual(
list(model.iter_collapsed_content(content)), content
)
self.assertListEqual(
list(iter_collapsed_content(content, group)), content
)

content = [('B3', 10), ('B5', True), ('B6', 'alpha'), ('B7', 20)] # Missing B4
model.restart()
self.assertListEqual(
list(model.iter_collapsed_content(content)), content
)
self.assertListEqual(
list(iter_collapsed_content(content, group)), content
)
Expand Down Expand Up @@ -1202,6 +1193,64 @@ def test_iter_collapsed_content_with_single_elements(self):
content = [('X', None), ('B1', 'abc'), ('B2', 10), ('B3', False)]
self.assertListEqual(list(iter_collapsed_content(content, group)), content)

def test_deprecated_methods(self):
schema = self.get_schema("""
<xs:element name="A" type="A_type" />
<xs:complexType name="A_type">
<xs:sequence maxOccurs="10">
<xs:element name="B1" minOccurs="0" />
<xs:element name="B2" minOccurs="0" />
</xs:sequence>
</xs:complexType>
""")

group = schema.types['A_type'].content
model = ModelVisitor(group)
default_namespace = 'http://xmlschema.test/ns'
content = [('B1', 1), ('B1', 2), ('B2', 3)]

with warnings.catch_warnings(record=True) as ctx:
warnings. simplefilter("always")
self.assertListEqual(
list(model.iter_collapsed_content(content)),
[('B1', 1), ('B2', 3), ('B1', 2)]
)
self.assertEqual(len(ctx), 1, "Expected one deprecation warning")
self.assertIn("use iter_collapsed_content()", str(ctx[0].message))
self.assertNotIn("Don't provide default_namespace", str(ctx[0].message))

with warnings.catch_warnings(record=True) as ctx:
warnings.simplefilter("always")
self.assertListEqual(
list(model.iter_collapsed_content(content, default_namespace)),
[('B1', 1), ('B2', 3), ('B1', 2)]
)
self.assertEqual(len(ctx), 1, "Expected one deprecation warning")
self.assertIn("use iter_collapsed_content()", str(ctx[0].message))
self.assertIn("Don't provide default_namespace", str(ctx[0].message))

content = [('B2', 1), ('B1', 2), ('B2', 3), ('B1', 4)]

with warnings.catch_warnings(record=True) as ctx:
warnings. simplefilter("always")
self.assertListEqual(
list(model.iter_unordered_content(content)),
[('B1', 2), ('B2', 1), ('B1', 4), ('B2', 3)]
)
self.assertEqual(len(ctx), 1, "Expected one deprecation warning")
self.assertIn("use iter_unordered_content()", str(ctx[0].message))
self.assertNotIn("Don't provide default_namespace", str(ctx[0].message))

with warnings.catch_warnings(record=True) as ctx:
warnings.simplefilter("always")
self.assertListEqual(
list(model.iter_unordered_content(content, default_namespace)),
[('B1', 2), ('B2', 1), ('B1', 4), ('B2', 3)]
)
self.assertEqual(len(ctx), 1, "Expected one deprecation warning")
self.assertIn("use iter_unordered_content()", str(ctx[0].message))
self.assertIn("Don't provide default_namespace", str(ctx[0].message))


class TestModelPaths(unittest.TestCase):

Expand Down
4 changes: 2 additions & 2 deletions xmlschema/validators/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -1137,7 +1137,7 @@ def iter_encode(self, obj: ElementData, validation: str = 'lax', **kwargs: Any)
if not obj.content:
content = []
elif isinstance(obj.content, MutableMapping) or kwargs.get('unordered'):
content = iter_unordered_content(obj.content, self, default_namespace)
content = iter_unordered_content(obj.content, self)
elif not isinstance(obj.content, MutableSequence):
wrong_content_type = True
content = []
Expand All @@ -1150,7 +1150,7 @@ def iter_encode(self, obj: ElementData, validation: str = 'lax', **kwargs: Any)
elif converter.losslessly:
content = obj.content
else:
content = iter_collapsed_content(obj.content, self, default_namespace)
content = iter_collapsed_content(obj.content, self)

for index, (name, value) in enumerate(content):
if isinstance(name, int):
Expand Down
42 changes: 25 additions & 17 deletions xmlschema/validators/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from operator import attrgetter
from typing import Any, Dict, Iterable, Iterator, List, \
MutableMapping, MutableSequence, Optional, Tuple, Union
import warnings

from ..exceptions import XMLSchemaValueError
from ..aliases import ModelGroupType, ModelParticleType, SchemaElementType, \
Expand Down Expand Up @@ -465,12 +466,26 @@ def _iter_all_model_errors(self, occurs: OccursCounterType) -> Iterator[AdvanceY
def iter_unordered_content(
self, content: EncodedContentType,
default_namespace: Optional[str] = None) -> Iterator[ContentItemType]:
return iter_unordered_content(content, self.root, default_namespace)

msg = f"{self.__class__.__name__}.iter_unordered_content() method will " \
"be removed in v4.0, use iter_unordered_content() function instead."
if default_namespace is not None:
msg += " Don't provide default_namespace argument, it's ignored."
warnings.warn(msg, DeprecationWarning, stacklevel=2)

return iter_unordered_content(content, self.root)

def iter_collapsed_content(
self, content: Iterable[ContentItemType],
default_namespace: Optional[str] = None) -> Iterator[ContentItemType]:
return iter_collapsed_content(content, self.root, default_namespace)

msg = f"{self.__class__.__name__}.iter_collapsed_content() method will " \
"be removed in v4.0, use iter_collapsed_content() function instead."
if default_namespace is not None:
msg += " Don't provide default_namespace argument, it's ignored."
warnings.warn(msg, DeprecationWarning, stacklevel=2)

return iter_collapsed_content(content, self.root)


class InterleavedModelVisitor(ModelVisitor):
Expand Down Expand Up @@ -555,10 +570,8 @@ def advance(self, match: bool = False) -> Iterator[AdvanceYieldedType]:
#
# Functions for manipulating encoded content

def iter_unordered_content(
content: EncodedContentType,
group: ModelGroupType,
default_namespace: Optional[str] = None) -> Iterator[ContentItemType]:
def iter_unordered_content(content: EncodedContentType, group: ModelGroupType) \
-> Iterator[ContentItemType]:
"""
Takes an unordered content stored in a dictionary of lists and yields the
content elements sorted with the ordering defined by the model group. Character
Expand All @@ -574,7 +587,6 @@ def iter_unordered_content(
dictionary the values must be lists where each item is the content \
of a single element.
:param group: the model group related to content.
:param default_namespace: the default namespace to apply for matching names.
"""
consumable_content: Dict[str, Any]

Expand Down Expand Up @@ -624,16 +636,13 @@ def iter_unordered_content(
yield cdata_content.pop()


def sort_content(content: EncodedContentType,
group: ModelGroupType,
default_namespace: Optional[str] = None) -> List[ContentItemType]:
return [x for x in iter_unordered_content(content, group, default_namespace)]
def sort_content(content: EncodedContentType, group: ModelGroupType) \
-> List[ContentItemType]:
return [x for x in iter_unordered_content(content, group)]


def iter_collapsed_content(
content: Iterable[ContentItemType],
group: ModelGroupType,
default_namespace: Optional[str] = None) -> Iterator[ContentItemType]:
def iter_collapsed_content(content: Iterable[ContentItemType], group: ModelGroupType) \
-> Iterator[ContentItemType]:
"""
Iterates a content stored in a sequence of couples *(name, value)*, yielding
items in the same order of the sequence, except for repetitions of the same
Expand All @@ -647,7 +656,6 @@ def iter_collapsed_content(
:param content: an iterable containing couples of names and values.
:param group: the model group related to content.
:param default_namespace: the default namespace to apply for matching names.
"""
prev_name = None
unordered_content: Dict[str, Any] = defaultdict(deque)
Expand Down Expand Up @@ -689,7 +697,7 @@ def iter_collapsed_content(
yield name, value
prev_name = name

# Add the remaining consumable content onto the end of the data.
# Yields the remaining consumable content after the end of the data.
for name, values in unordered_content.items():
for v in values:
yield name, v

0 comments on commit 39ad414

Please sign in to comment.