Skip to content

Commit

Permalink
Stop generating nan values in tests to work with latest attrs (#576)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tinche committed Sep 11, 2024
1 parent a3773b2 commit 9d406c8
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 123 deletions.
172 changes: 96 additions & 76 deletions pdm.lock

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ lint = [
"ruff>=0.0.277",
]
test = [
"hypothesis>=6.79.4",
"pytest>=7.4.0",
"hypothesis>=6.111.2",
"pytest>=8.3.2",
"pytest-benchmark>=4.0.0",
"immutables>=0.20",
"typing-extensions>=4.7.1",
"coverage>=7.4.0",
"pytest-xdist>=3.4.0",
"coverage>=7.6.1",
"pytest-xdist>=3.6.1",
]
docs = [
"sphinx>=5.3.0",
Expand Down
18 changes: 9 additions & 9 deletions tests/test_baseconverter.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
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.
Expand Down Expand Up @@ -43,7 +43,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.
Expand All @@ -55,7 +55,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.
Expand All @@ -70,8 +70,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):
Expand Down Expand Up @@ -113,8 +113,8 @@ def handler(obj, _):
@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):
Expand Down Expand Up @@ -153,7 +153,7 @@ def handler(obj, _):
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.
Expand All @@ -175,7 +175,7 @@ class C:


@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.
Expand Down
38 changes: 23 additions & 15 deletions tests/test_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@
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.
Expand All @@ -54,8 +57,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):
Expand All @@ -72,7 +75,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.
Expand All @@ -87,7 +90,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.
Expand All @@ -103,7 +108,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):
Expand Down Expand Up @@ -200,7 +206,7 @@ class A:
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.
Expand All @@ -214,7 +220,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):
Expand All @@ -233,8 +241,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):
Expand Down Expand Up @@ -278,8 +286,8 @@ def handler(obj, _):
@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):
Expand Down Expand Up @@ -320,7 +328,7 @@ def handler(obj, _):
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.
Expand All @@ -342,7 +350,7 @@ class C:


@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.
Expand All @@ -363,7 +371,7 @@ class C:
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.
Expand Down
10 changes: 6 additions & 4 deletions tests/test_gen_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,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):
Expand All @@ -185,7 +185,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()
Expand Down
51 changes: 37 additions & 14 deletions tests/typed.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def simple_typed_classes(
newtypes=True,
text_codec: str = "utf8",
allow_infinity=None,
allow_nan=None,
allow_nan=True,
) -> SearchStrategy[Tuple[Type, PosArgs, KwArgs]]:
"""Yield tuples of (class, values)."""
return lists_of_typed_attrs(
Expand All @@ -82,23 +82,30 @@ def simple_typed_classes(
).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,
min_size=min_attrs,
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)


Expand All @@ -111,7 +118,7 @@ def lists_of_typed_attrs(
newtypes=True,
text_codec="utf8",
allow_infinity=None,
allow_nan=None,
allow_nan=True,
) -> SearchStrategy[List[Tuple[_CountingAttr, SearchStrategy[PosArg]]]]:
# Python functions support up to 255 arguments.
return lists(
Expand Down Expand Up @@ -142,7 +149,7 @@ def simple_typed_attrs(
newtypes=True,
text_codec="utf8",
allow_infinity=None,
allow_nan=None,
allow_nan=True,
) -> SearchStrategy[Tuple[_CountingAttr, SearchStrategy[PosArgs]]]:
if not is_39_or_later:
res = (
Expand Down Expand Up @@ -400,7 +407,7 @@ def str_typed_attrs(draw, defaults=None, kw_only=None, codec: str = "utf8"):

@composite
def float_typed_attrs(
draw, defaults=None, kw_only=None, allow_infinity=None, allow_nan=None
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
Expand Down Expand Up @@ -832,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.
Expand All @@ -847,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)
Expand Down Expand Up @@ -891,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)
2 changes: 1 addition & 1 deletion tests/untyped.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,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
Expand Down

0 comments on commit 9d406c8

Please sign in to comment.