Python 3.13.0b2: 4 tests failuires
befeleme opened this issue · comments
- cattrs version: 23.2.3
- Python version: 3.13.0b2
- Operating System: Fedora Linux 41
Description
I'm trying to build cattrs as RPM for Fedora Linux 41 with Python 3.13.0b2.
4 tests fail with two types of failures. See traceback below.
What I Did
_________________ test_collection_unstructure_override_mapping _________________
[gw9] linux -- Python 3.13.0 /usr/bin/python3
@pytest.mark.skipif(not is_py39_plus, reason="Requires Python 3.9+")
def test_collection_unstructure_override_mapping():
"""Test overriding unstructuring mappings."""
# Using Counter
c = Converter(unstruct_collection_overrides={Counter: Map})
assert c.unstructure(Counter({1: 2})) == Map({1: 2})
> assert c.unstructure(Counter({1: 2}), unstructure_as=Counter[int]) == Map({1: 2})
E assert Counter({1: 2}) == immutables.Map({1: 2})
E Use -v to get more diff
c = <cattrs.converters.Converter object at 0xffff8413d620>
tests/test_unstructure_collections.py:168: AssertionError
________________________________ test_renaming _________________________________
[gw5] linux -- Python 3.13.0 /usr/bin/python3
@given(
> simple_typed_classes(min_attrs=1) | simple_typed_dataclasses(min_attrs=1), data()
)
f = <function given.<locals>.run_test_as_given.<locals>.wrapped_test at 0xffff7c5dbba0>
tests/test_gen_dict.py:190:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
cl_and_vals = (<class 'tests.typed.HypDataclass'>, (nan,), {}), data = data(...)
@given(
simple_typed_classes(min_attrs=1) | simple_typed_dataclasses(min_attrs=1), data()
)
def test_renaming(cl_and_vals, data):
converter = Converter()
cl, vals, kwargs = cl_and_vals
attrs = fields(cl)
to_replace = data.draw(sampled_from(attrs))
u_fn = make_dict_unstructure_fn(
cl, converter, **{to_replace.name: override(rename="class")}
)
s_fn = make_dict_structure_fn(
cl, converter, **{to_replace.name: override(rename="class")}
)
converter.register_structure_hook(cl, s_fn)
converter.register_unstructure_hook(cl, u_fn)
inst = cl(*vals, **kwargs)
raw = converter.unstructure(inst)
assert "class" in raw
new_inst = converter.structure(raw, cl)
> assert inst == new_inst
E AssertionError: assert HypDataclass(a=nan) == HypDataclass(a=nan)
E
E Differing attributes:
E ['a']
E
E Drill down into differing attribute a:
E a: nan != nan
E Falsifying example: test_renaming(
E cl_and_vals=(tests.typed.HypDataclass, (nan,), {}), # Saw 1 signaling NaN
E data=data(...),
E )
E Draw 1: Field(name='a',type=<class 'float'>,default=<dataclasses._MISSING_TYPE object at 0xffff7f1c2660>,default_factory=<dataclasses._MISSING_TYPE object at 0xffff7f1c2660>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD)
E Explanation:
E These lines were always and only run by failing examples:
E /usr/lib/python3.13/site-packages/_pytest/assertion/util.py:456
attrs = (Field(name='a',type=<class 'float'>,default=<dataclasses._MISSING_TYPE object at 0xffff7f1c2660>,default_factory=<dat...xffff7f1c2660>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD),)
cl = <class 'tests.typed.HypDataclass'>
cl_and_vals = (<class 'tests.typed.HypDataclass'>, (nan,), {})
converter = <cattrs.converters.Converter object at 0xffff72bc2de0>
data = data(...)
inst = HypDataclass(a=nan)
kwargs = {}
new_inst = HypDataclass(a=nan)
raw = {'class': nan}
s_fn = <function structure_HypDataclass at 0xffff72bd7b00>
to_replace = Field(name='a',type=<class 'float'>,default=<dataclasses._MISSING_TYPE object at 0xffff7f1c2660>,default_factory=<data... 0xffff7f1c2660>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD)
u_fn = <function unstructure_HypDataclass at 0xffff77496f20>
vals = (nan,)
tests/test_gen_dict.py:217: AssertionError
_________________________ test_simple_roundtrip_tuple __________________________
[gw6] linux -- Python 3.13.0 /usr/bin/python3
@given(
> simple_typed_classes(kw_only=False, newtypes=False)
| simple_typed_dataclasses(newtypes=False),
booleans(),
)
f = <function given.<locals>.run_test_as_given.<locals>.wrapped_test at 0xffff7c35a980>
tests/test_converter.py:54:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
cls_and_vals = (<class 'tests.typed.HypDataclass'>, (nan,), {}), dv = False
@given(
simple_typed_classes(kw_only=False, newtypes=False)
| simple_typed_dataclasses(newtypes=False),
booleans(),
)
def test_simple_roundtrip_tuple(cls_and_vals, dv: bool):
"""
Simple classes with metadata can be unstructured and restructured.
"""
converter = Converter(
unstruct_strat=UnstructureStrategy.AS_TUPLE, detailed_validation=dv
)
cl, vals, _ = cls_and_vals
inst = cl(*vals)
unstructured = converter.unstructure(inst)
assert "Hyp" not in repr(unstructured)
> assert inst == converter.structure(unstructured, cl)
E AssertionError: assert HypDataclass(a=nan) == HypDataclass(a=nan)
E
E Differing attributes:
E ['a']
E
E Drill down into differing attribute a:
E a: nan != nan
E Falsifying example: test_simple_roundtrip_tuple(
E cls_and_vals=(tests.typed.HypDataclass, (nan,), {}), # Saw 1 signaling NaN
E dv=False, # or any other generated value
E )
E Explanation:
E These lines were always and only run by failing examples:
E /usr/lib/python3.13/site-packages/_pytest/assertion/util.py:456
_ = {}
cl = <class 'tests.typed.HypDataclass'>
cls_and_vals = (<class 'tests.typed.HypDataclass'>, (nan,), {})
converter = <cattrs.converters.Converter object at 0xffff725bc4a0>
dv = False
inst = HypDataclass(a=nan)
unstructured = (nan,)
vals = (nan,)
tests/test_converter.py:69: AssertionError
_______________ test_simple_roundtrip_with_extra_keys_forbidden ________________
[gw6] linux -- Python 3.13.0 /usr/bin/python3
@given(
> simple_typed_classes(newtypes=False) | simple_typed_dataclasses(newtypes=False),
unstructure_strats,
)
f = <function given.<locals>.run_test_as_given.<locals>.wrapped_test at 0xffff7c35b920>
tests/test_converter.py:103:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
cls_and_vals = (<class 'tests.typed.HypDataclass'>, (nan,), {})
strat = <UnstructureStrategy.AS_DICT: 'asdict'>
@given(
simple_typed_classes(newtypes=False) | simple_typed_dataclasses(newtypes=False),
unstructure_strats,
)
def test_simple_roundtrip_with_extra_keys_forbidden(cls_and_vals, strat):
"""
Simple classes can be unstructured and restructured with forbid_extra_keys=True.
"""
converter = Converter(unstruct_strat=strat, forbid_extra_keys=True)
cl, vals, kwargs = cls_and_vals
assume(strat is UnstructureStrategy.AS_DICT or not kwargs)
inst = cl(*vals, **kwargs)
unstructured = converter.unstructure(inst)
assert "Hyp" not in repr(unstructured)
> assert inst == converter.structure(unstructured, cl)
E AssertionError: assert HypDataclass(a=nan) == HypDataclass(a=nan)
E
E Differing attributes:
E ['a']
E
E Drill down into differing attribute a:
E a: nan != nan
E Falsifying example: test_simple_roundtrip_with_extra_keys_forbidden(
E cls_and_vals=(tests.typed.HypDataclass, (nan,), {}), # Saw 1 signaling NaN
E strat=UnstructureStrategy.AS_DICT, # or any other generated value
E )
E Explanation:
E These lines were always and only run by failing examples:
E /usr/lib/python3.13/site-packages/_pytest/assertion/util.py:456
E /usr/lib/python3.13/site-packages/_pytest/assertion/util.py:468
E /usr/lib64/python3.13/reprlib.py:23
cl = <class 'tests.typed.HypDataclass'>
cls_and_vals = (<class 'tests.typed.HypDataclass'>, (nan,), {})
converter = <cattrs.converters.Converter object at 0xffff63042ca0>
inst = HypDataclass(a=nan)
kwargs = {}
strat = <UnstructureStrategy.AS_DICT: 'asdict'>
unstructured = {'a': nan}
vals = (nan,)
tests/test_converter.py:116: AssertionError
=========================== short test summary info ============================
FAILED tests/test_unstructure_collections.py::test_collection_unstructure_override_mapping
FAILED tests/test_gen_dict.py::test_renaming - AssertionError: assert HypData...
FAILED tests/test_converter.py::test_simple_roundtrip_tuple - AssertionError:...
FAILED tests/test_converter.py::test_simple_roundtrip_with_extra_keys_forbidden
= 4 failed, 576 passed, 1 skipped, 15 xfailed, 24 warnings in 215.73s (0:03:35) =
Unfortunately 3.13 testing for the main
branch is currently blocked by some of our dependencies, so I'm waiting on that to proceed.
I don't plan to test 23.2.3 on 3.12, it'll be 24.1.0.
Here's the PR: #543
3/4 of the failures are likely caused by this difference between Python 3.12 and 3.13:
>>> from dataclasses import dataclass
>>> @dataclass
... class a:
... a: float
...
>>> nan = float('nan')
>>> nan == nan # on both
False
>>> a(nan) == a(nan) # on 3.12
True
>>> a(nan) == a(nan) # on 3.13
False
I believe this might be related to python/cpython#104904
I've reported python/cpython#120645
The new behavior looks more correct to me.
Interestingly, on 3.12:
(nan,) == (nan,)
Very surprised by this.
I think I fixed the nan issues on the tin/3.13
branch.