diff --git a/tests/validators/test_models.py b/tests/validators/test_models.py index 47a6893f..744f94c2 100644 --- a/tests/validators/test_models.py +++ b/tests/validators/test_models.py @@ -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 @@ -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 ) @@ -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(""" + + + + + + + + """) + + 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): diff --git a/xmlschema/validators/groups.py b/xmlschema/validators/groups.py index b17c4d5c..d762bcc3 100644 --- a/xmlschema/validators/groups.py +++ b/xmlschema/validators/groups.py @@ -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 = [] @@ -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): diff --git a/xmlschema/validators/models.py b/xmlschema/validators/models.py index 4fbe4d58..b96f6f1a 100644 --- a/xmlschema/validators/models.py +++ b/xmlschema/validators/models.py @@ -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, \ @@ -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): @@ -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 @@ -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] @@ -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 @@ -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) @@ -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