Control: tags -1 + patch The attached patch was applied in Ubuntu.
From: Anshul Singh <anshul.si...@canonical.com> Date: Wed, 11 Dec 2024 14:05:50 +0530 Subject: Patch to stop generating nan values in tests to work with latest attrs
Origin: upstream, https://github.com/python-attrs/cattrs/commit/96ed9a1c972814c379f9ea8faa3413aacd4ce6cb, https://github.com/python-attrs/cattrs/commit/31eff823a7114f24b0c010af314b61e01cf099a9 Author: Tin Tvrtković Tinche Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-cattrs/+bug/2089134 Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1086614 --- tests/test_baseconverter.py | 23 ++++---- tests/test_converter.py | 38 ++++++++------ tests/test_gen_dict.py | 10 ++-- tests/typed.py | 125 +++++++++++++++++++++++++++++++------------- tests/untyped.py | 2 +- 5 files changed, 132 insertions(+), 66 deletions(-) diff --git a/tests/test_baseconverter.py b/tests/test_baseconverter.py index 6305701..17b891d 100644 --- a/tests/test_baseconverter.py +++ b/tests/test_baseconverter.py @@ -14,7 +14,7 @@ from .typed import nested_typed_classes, simple_typed_attrs, simple_typed_classe unstructure_strats = one_of(just(s) for s in UnstructureStrategy) -@given(simple_typed_classes(newtypes=False), unstructure_strats) +@given(simple_typed_classes(newtypes=False, allow_nan=False), unstructure_strats) def test_simple_roundtrip(cls_and_vals, strat): """ Simple classes with metadata can be unstructured and restructured. @@ -26,7 +26,10 @@ def test_simple_roundtrip(cls_and_vals, strat): assert inst == converter.structure(converter.unstructure(inst), cl) -@given(simple_typed_attrs(defaults=True, newtypes=False), unstructure_strats) +@given( + simple_typed_attrs(defaults=True, newtypes=False, allow_nan=False), + unstructure_strats, +) def test_simple_roundtrip_defaults(attr_and_strat, strat): """ Simple classes with metadata can be unstructured and restructured. @@ -42,7 +45,7 @@ def test_simple_roundtrip_defaults(attr_and_strat, strat): assert inst == converter.structure(converter.unstructure(inst), cl) -@given(nested_typed_classes(newtypes=False)) +@given(nested_typed_classes(newtypes=False, allow_nan=False)) def test_nested_roundtrip(cls_and_vals): """ Nested classes with metadata can be unstructured and restructured. @@ -54,7 +57,7 @@ def test_nested_roundtrip(cls_and_vals): assert inst == converter.structure(converter.unstructure(inst), cl) -@given(nested_typed_classes(kw_only=False, newtypes=False)) +@given(nested_typed_classes(kw_only=False, newtypes=False, allow_nan=False)) def test_nested_roundtrip_tuple(cls_and_vals): """ Nested classes with metadata can be unstructured and restructured. @@ -69,8 +72,8 @@ def test_nested_roundtrip_tuple(cls_and_vals): @settings(suppress_health_check=[HealthCheck.filter_too_much, HealthCheck.too_slow]) @given( - simple_typed_classes(defaults=False, newtypes=False), - simple_typed_classes(defaults=False, newtypes=False), + simple_typed_classes(defaults=False, newtypes=False, allow_nan=False), + simple_typed_classes(defaults=False, newtypes=False, allow_nan=False), unstructure_strats, ) def test_union_field_roundtrip(cl_and_vals_a, cl_and_vals_b, strat): @@ -112,8 +115,8 @@ def test_union_field_roundtrip(cl_and_vals_a, cl_and_vals_b, strat): @pytest.mark.skipif(not is_py310_plus, reason="3.10+ union syntax") @settings(suppress_health_check=[HealthCheck.filter_too_much, HealthCheck.too_slow]) @given( - simple_typed_classes(defaults=False, newtypes=False), - simple_typed_classes(defaults=False, newtypes=False), + simple_typed_classes(defaults=False, newtypes=False, allow_nan=False), + simple_typed_classes(defaults=False, newtypes=False, allow_nan=False), unstructure_strats, ) def test_310_union_field_roundtrip(cl_and_vals_a, cl_and_vals_b, strat): @@ -152,7 +155,7 @@ def test_310_union_field_roundtrip(cl_and_vals_a, cl_and_vals_b, strat): assert inst == converter.structure(converter.unstructure(inst), C) -@given(simple_typed_classes(defaults=False, newtypes=False)) +@given(simple_typed_classes(defaults=False, newtypes=False, allow_nan=False)) def test_optional_field_roundtrip(cl_and_vals): """ Classes with optional fields can be unstructured and structured. @@ -174,7 +177,7 @@ def test_optional_field_roundtrip(cl_and_vals): @pytest.mark.skipif(not is_py310_plus, reason="3.10+ union syntax") -@given(simple_typed_classes(defaults=False, newtypes=False)) +@given(simple_typed_classes(defaults=False, newtypes=False, allow_nan=False)) def test_310_optional_field_roundtrip(cl_and_vals): """ Classes with optional fields can be unstructured and structured. diff --git a/tests/test_converter.py b/tests/test_converter.py index 6e0563b..3178bcd 100644 --- a/tests/test_converter.py +++ b/tests/test_converter.py @@ -37,7 +37,10 @@ from .typed import ( unstructure_strats = one_of(just(s) for s in UnstructureStrategy) -@given(simple_typed_classes() | simple_typed_dataclasses(), booleans()) +@given( + simple_typed_classes(allow_nan=False) | simple_typed_dataclasses(allow_nan=False), + booleans(), +) def test_simple_roundtrip(cls_and_vals, detailed_validation): """ Simple classes with metadata can be unstructured and restructured. @@ -51,8 +54,8 @@ def test_simple_roundtrip(cls_and_vals, detailed_validation): @given( - simple_typed_classes(kw_only=False, newtypes=False) - | simple_typed_dataclasses(newtypes=False), + simple_typed_classes(kw_only=False, newtypes=False, allow_nan=False) + | simple_typed_dataclasses(newtypes=False, allow_nan=False), booleans(), ) def test_simple_roundtrip_tuple(cls_and_vals, dv: bool): @@ -69,7 +72,7 @@ def test_simple_roundtrip_tuple(cls_and_vals, dv: bool): assert inst == converter.structure(unstructured, cl) -@given(simple_typed_attrs(defaults=True)) +@given(simple_typed_attrs(defaults=True, allow_nan=False)) def test_simple_roundtrip_defaults(attr_and_vals): """ Simple classes with metadata can be unstructured and restructured. @@ -84,7 +87,9 @@ def test_simple_roundtrip_defaults(attr_and_vals): assert inst == converter.structure(converter.unstructure(inst), cl) -@given(simple_typed_attrs(defaults=True, kw_only=False, newtypes=False)) +@given( + simple_typed_attrs(defaults=True, kw_only=False, newtypes=False, allow_nan=False) +) def test_simple_roundtrip_defaults_tuple(attr_and_vals): """ Simple classes with metadata can be unstructured and restructured. @@ -100,7 +105,8 @@ def test_simple_roundtrip_defaults_tuple(attr_and_vals): @given( - simple_typed_classes(newtypes=False) | simple_typed_dataclasses(newtypes=False), + simple_typed_classes(newtypes=False, allow_nan=False) + | simple_typed_dataclasses(newtypes=False, allow_nan=False), unstructure_strats, ) def test_simple_roundtrip_with_extra_keys_forbidden(cls_and_vals, strat): @@ -199,7 +205,7 @@ def test_forbid_extra_keys_nested_override(): assert cve.value.exceptions[0].extra_fields == {"b"} -@given(nested_typed_classes(defaults=True, min_attrs=1), booleans()) +@given(nested_typed_classes(defaults=True, min_attrs=1, allow_nan=False), booleans()) def test_nested_roundtrip(cls_and_vals, omit_if_default): """ Nested classes with metadata can be unstructured and restructured. @@ -213,7 +219,9 @@ def test_nested_roundtrip(cls_and_vals, omit_if_default): @given( - nested_typed_classes(defaults=True, min_attrs=1, kw_only=False, newtypes=False), + nested_typed_classes( + defaults=True, min_attrs=1, kw_only=False, newtypes=False, allow_nan=False + ), booleans(), ) def test_nested_roundtrip_tuple(cls_and_vals, omit_if_default: bool): @@ -232,8 +240,8 @@ def test_nested_roundtrip_tuple(cls_and_vals, omit_if_default: bool): @settings(suppress_health_check=[HealthCheck.filter_too_much, HealthCheck.too_slow]) @given( - simple_typed_classes(defaults=False, newtypes=False), - simple_typed_classes(defaults=False, newtypes=False), + simple_typed_classes(defaults=False, newtypes=False, allow_nan=False), + simple_typed_classes(defaults=False, newtypes=False, allow_nan=False), unstructure_strats, ) def test_union_field_roundtrip(cl_and_vals_a, cl_and_vals_b, strat): @@ -277,8 +285,8 @@ def test_union_field_roundtrip(cl_and_vals_a, cl_and_vals_b, strat): @pytest.mark.skipif(not is_py310_plus, reason="3.10+ union syntax") @settings(suppress_health_check=[HealthCheck.filter_too_much, HealthCheck.too_slow]) @given( - simple_typed_classes(defaults=False, newtypes=False), - simple_typed_classes(defaults=False, newtypes=False), + simple_typed_classes(defaults=False, newtypes=False, allow_nan=False), + simple_typed_classes(defaults=False, newtypes=False, allow_nan=False), unstructure_strats, ) def test_310_union_field_roundtrip(cl_and_vals_a, cl_and_vals_b, strat): @@ -319,7 +327,7 @@ def test_310_union_field_roundtrip(cl_and_vals_a, cl_and_vals_b, strat): assert inst == converter.structure(unstructured, C) -@given(simple_typed_classes(defaults=False)) +@given(simple_typed_classes(defaults=False, allow_nan=False)) def test_optional_field_roundtrip(cl_and_vals): """ Classes with optional fields can be unstructured and structured. @@ -341,7 +349,7 @@ def test_optional_field_roundtrip(cl_and_vals): @pytest.mark.skipif(not is_py310_plus, reason="3.10+ union syntax") -@given(simple_typed_classes(defaults=False)) +@given(simple_typed_classes(defaults=False, allow_nan=False)) def test_310_optional_field_roundtrip(cl_and_vals): """ Classes with optional fields can be unstructured and structured. @@ -362,7 +370,7 @@ def test_310_optional_field_roundtrip(cl_and_vals): assert inst == converter.structure(unstructured, C) -@given(simple_typed_classes(defaults=True)) +@given(simple_typed_classes(defaults=True, allow_nan=False)) def test_omit_default_roundtrip(cl_and_vals): """ Omit default on the converter works. diff --git a/tests/test_gen_dict.py b/tests/test_gen_dict.py index 0a4e851..446c789 100644 --- a/tests/test_gen_dict.py +++ b/tests/test_gen_dict.py @@ -163,9 +163,9 @@ def test_individual_overrides(converter_cls, cl_and_vals): @given( - cl_and_vals=nested_typed_classes() - | simple_typed_classes() - | simple_typed_dataclasses(), + cl_and_vals=nested_typed_classes(allow_nan=False) + | simple_typed_classes(allow_nan=False) + | simple_typed_dataclasses(allow_nan=False), dv=..., ) def test_unmodified_generated_structuring(cl_and_vals, dv: bool): @@ -187,7 +187,9 @@ def test_unmodified_generated_structuring(cl_and_vals, dv: bool): @given( - simple_typed_classes(min_attrs=1) | simple_typed_dataclasses(min_attrs=1), data() + simple_typed_classes(min_attrs=1, allow_nan=False) + | simple_typed_dataclasses(min_attrs=1, allow_nan=False), + data(), ) def test_renaming(cl_and_vals, data): converter = Converter() diff --git a/tests/typed.py b/tests/typed.py index 6bed20d..2cd4db2 100644 --- a/tests/typed.py +++ b/tests/typed.py @@ -1,4 +1,5 @@ """Strategies for attributes with types and classes using them.""" + import sys from collections import OrderedDict from collections.abc import MutableSequence as AbcMutableSequence @@ -33,6 +34,7 @@ from hypothesis.strategies import ( DrawFn, SearchStrategy, booleans, + characters, composite, dictionaries, fixed_dictionaries, @@ -58,7 +60,14 @@ T = TypeVar("T") def simple_typed_classes( - defaults=None, min_attrs=0, frozen=False, kw_only=None, newtypes=True + defaults=None, + min_attrs=0, + frozen=False, + kw_only=None, + newtypes=True, + text_codec: str = "utf8", + allow_infinity=None, + allow_nan=True, ) -> SearchStrategy[Tuple[Type, PosArgs, KwArgs]]: """Yield tuples of (class, values).""" return lists_of_typed_attrs( @@ -67,10 +76,15 @@ def simple_typed_classes( for_frozen=frozen, kw_only=kw_only, newtypes=newtypes, + text_codec=text_codec, + allow_infinity=allow_infinity, + allow_nan=allow_nan, ).flatmap(partial(_create_hyp_class, frozen=frozen)) -def simple_typed_dataclasses(defaults=None, min_attrs=0, frozen=False, newtypes=True): +def simple_typed_dataclasses( + defaults=None, min_attrs=0, frozen=False, newtypes=True, allow_nan=True +): """Yield tuples of (class, values).""" return lists_of_typed_attrs( defaults, @@ -78,15 +92,20 @@ def simple_typed_dataclasses(defaults=None, min_attrs=0, frozen=False, newtypes= for_frozen=frozen, allow_mutable_defaults=False, newtypes=newtypes, + allow_nan=allow_nan, ).flatmap(partial(_create_dataclass, frozen=frozen)) def simple_typed_classes_and_strats( - defaults=None, min_attrs=0, kw_only=None, newtypes=True + defaults=None, min_attrs=0, kw_only=None, newtypes=True, allow_nan=True ) -> SearchStrategy[Tuple[Type, SearchStrategy[PosArgs], SearchStrategy[KwArgs]]]: """Yield tuples of (class, (strategies)).""" return lists_of_typed_attrs( - defaults, min_size=min_attrs, kw_only=kw_only, newtypes=newtypes + defaults, + min_size=min_attrs, + kw_only=kw_only, + newtypes=newtypes, + allow_nan=allow_nan, ).flatmap(_create_hyp_class_and_strat) @@ -97,6 +116,9 @@ def lists_of_typed_attrs( allow_mutable_defaults=True, kw_only=None, newtypes=True, + text_codec="utf8", + allow_infinity=None, + allow_nan=True, ) -> SearchStrategy[List[Tuple[_CountingAttr, SearchStrategy[PosArg]]]]: # Python functions support up to 255 arguments. return lists( @@ -106,6 +128,9 @@ def lists_of_typed_attrs( allow_mutable_defaults=allow_mutable_defaults, kw_only=kw_only, newtypes=newtypes, + text_codec=text_codec, + allow_infinity=allow_infinity, + allow_nan=allow_nan, ), min_size=min_size, max_size=50, @@ -122,13 +147,16 @@ def simple_typed_attrs( allow_mutable_defaults=True, kw_only=None, newtypes=True, + text_codec="utf8", + allow_infinity=None, + allow_nan=True, ) -> SearchStrategy[Tuple[_CountingAttr, SearchStrategy[PosArgs]]]: if not is_39_or_later: res = ( - bare_typed_attrs(defaults, kw_only) + any_typed_attrs(defaults, kw_only) | int_typed_attrs(defaults, kw_only) - | str_typed_attrs(defaults, kw_only) - | float_typed_attrs(defaults, kw_only) + | str_typed_attrs(defaults, kw_only, text_codec) + | float_typed_attrs(defaults, kw_only, allow_infinity, allow_nan) | frozenset_typed_attrs(defaults, legacy_types_only=True, kw_only=kw_only) | homo_tuple_typed_attrs(defaults, legacy_types_only=True, kw_only=kw_only) | path_typed_attrs(defaults, kw_only=kw_only) @@ -170,10 +198,10 @@ def simple_typed_attrs( ) else: res = ( - bare_typed_attrs(defaults, kw_only) + any_typed_attrs(defaults, kw_only) | int_typed_attrs(defaults, kw_only) - | str_typed_attrs(defaults, kw_only) - | float_typed_attrs(defaults, kw_only) + | str_typed_attrs(defaults, kw_only, text_codec) + | float_typed_attrs(defaults, kw_only, allow_infinity, allow_nan) | frozenset_typed_attrs(defaults, kw_only=kw_only) | homo_tuple_typed_attrs(defaults, kw_only=kw_only) | path_typed_attrs(defaults, kw_only=kw_only) @@ -274,12 +302,18 @@ def _create_dataclass( make_dataclass( "HypDataclass", [ - (n, a.type) - if a._default is NOTHING - else ( - (n, a.type, dc_field(default=a._default)) - if not isinstance(a._default, Factory) - else (n, a.type, dc_field(default_factory=a._default.factory)) + ( + (n, a.type) + if a._default is NOTHING + else ( + (n, a.type, dc_field(default=a._default)) + if not isinstance(a._default, Factory) + else ( + n, + a.type, + dc_field(default_factory=a._default.factory), + ) + ) ) for n, a in zip(gen_attr_names(), attrs) ], @@ -316,11 +350,10 @@ def _create_hyp_class_and_strat( @composite -def bare_typed_attrs(draw, defaults=None, kw_only=None): - """ - Generate a tuple of an attribute and a strategy that yields values - appropriate for that attribute. - """ +def any_typed_attrs( + draw: DrawFn, defaults=None, kw_only=None +) -> Tuple[_CountingAttr, SearchStrategy[None]]: + """Attributes typed as `Any`, having values of `None`.""" default = NOTHING if defaults is True or (defaults is None and draw(booleans())): default = None @@ -354,7 +387,7 @@ def int_typed_attrs(draw, defaults=None, kw_only=None): @composite -def str_typed_attrs(draw, defaults=None, kw_only=None): +def str_typed_attrs(draw, defaults=None, kw_only=None, codec: str = "utf8"): """ Generate a tuple of an attribute and a strategy that yields strs for that attribute. @@ -368,26 +401,28 @@ def str_typed_attrs(draw, defaults=None, kw_only=None): default=default, kw_only=draw(booleans()) if kw_only is None else kw_only, ), - text(), + text(characters(codec=codec)), ) @composite -def float_typed_attrs(draw, defaults=None, kw_only=None): +def float_typed_attrs( + draw, defaults=None, kw_only=None, allow_infinity=None, allow_nan=True +): """ Generate a tuple of an attribute and a strategy that yields floats for that attribute. """ default = NOTHING if defaults is True or (defaults is None and draw(booleans())): - default = draw(floats()) + default = draw(floats(allow_infinity=allow_infinity, allow_nan=allow_nan)) return ( field( type=float, default=default, kw_only=draw(booleans()) if kw_only is None else kw_only, ), - floats(), + floats(allow_infinity=allow_infinity, allow_nan=allow_nan), ) @@ -638,9 +673,11 @@ def mutable_seq_typed_attrs( return ( field( - type=AbcMutableSequence[float] - if not legacy_types_only - else MutableSequence[float], + type=( + AbcMutableSequence[float] + if not legacy_types_only + else MutableSequence[float] + ), default=default, kw_only=draw(booleans()) if kw_only is None else kw_only, ), @@ -802,7 +839,7 @@ def dict_of_class( def _create_hyp_nested_strategy( - simple_class_strategy: SearchStrategy, kw_only=None, newtypes=True + simple_class_strategy: SearchStrategy, kw_only=None, newtypes=True, allow_nan=True ) -> SearchStrategy[Tuple[Type, SearchStrategy[PosArgs], SearchStrategy[KwArgs]]]: """ Create a recursive attrs class. @@ -817,7 +854,8 @@ def _create_hyp_nested_strategy( attrs_and_classes: SearchStrategy[ Tuple[List[Tuple[_CountingAttr, PosArgs]], Tuple[Type, SearchStrategy[PosArgs]]] ] = tuples( - lists_of_typed_attrs(kw_only=kw_only, newtypes=newtypes), simple_class_strategy + lists_of_typed_attrs(kw_only=kw_only, newtypes=newtypes, allow_nan=allow_nan), + simple_class_strategy, ) return nested_classes(attrs_and_classes) @@ -861,22 +899,37 @@ def nested_classes( def nested_typed_classes_and_strat( - defaults=None, min_attrs=0, kw_only=None, newtypes=True + defaults=None, min_attrs=0, kw_only=None, newtypes=True, allow_nan=True ) -> SearchStrategy[Tuple[Type, SearchStrategy[PosArgs]]]: return recursive( simple_typed_classes_and_strats( - defaults=defaults, min_attrs=min_attrs, kw_only=kw_only, newtypes=newtypes + defaults=defaults, + min_attrs=min_attrs, + kw_only=kw_only, + newtypes=newtypes, + allow_nan=allow_nan, + ), + partial( + _create_hyp_nested_strategy, + kw_only=kw_only, + newtypes=newtypes, + allow_nan=allow_nan, ), - partial(_create_hyp_nested_strategy, kw_only=kw_only, newtypes=newtypes), max_leaves=20, ) @composite -def nested_typed_classes(draw, defaults=None, min_attrs=0, kw_only=None, newtypes=True): +def nested_typed_classes( + draw, defaults=None, min_attrs=0, kw_only=None, newtypes=True, allow_nan=True +): cl, strat, kwarg_strat = draw( nested_typed_classes_and_strat( - defaults=defaults, min_attrs=min_attrs, kw_only=kw_only, newtypes=newtypes + defaults=defaults, + min_attrs=min_attrs, + kw_only=kw_only, + newtypes=newtypes, + allow_nan=allow_nan, ) ) return cl, draw(strat), draw(kwarg_strat) diff --git a/tests/untyped.py b/tests/untyped.py index e155397..c2b0519 100644 --- a/tests/untyped.py +++ b/tests/untyped.py @@ -351,7 +351,7 @@ def float_attrs(draw, defaults=None, kw_only=None): """ default = NOTHING if defaults is True or (defaults is None and draw(st.booleans())): - default = draw(st.floats()) + default = draw(st.floats(allow_nan=False)) return ( attr.ib( default=default, kw_only=draw(st.booleans()) if kw_only is None else kw_only