attrs icon indicating copy to clipboard operation
attrs copied to clipboard

24.2.0: not ready for `pyupgrade --py39-plus`

Open kloczek opened this issue 1 year ago • 0 comments

I've been trying to upgrade attrs code to drop python 3.8 support (on top of https://github.com/python-attrs/attrs/pull/1270) and found that after that pytest fails in few units

+ /usr/bin/pytest -ra -m 'not network'
============================= test session starts ==============================
platform linux -- Python 3.10.14, pytest-8.2.2, pluggy-1.5.0
rootdir: /home/tkloczko/rpmbuild/BUILD/attrs-24.2.0
configfile: pyproject.toml
testpaths: tests
plugins: hypothesis-6.100.0
collected 1335 items

tests/test_3rd_party.py .                                                [  0%]
tests/test_abc.py ....                                                   [  0%]
tests/test_annotations.py ..F.ss...............ss........FFFF..FFF...... [  3%]
.                                                                        [  3%]
tests/test_cmp.py ...................................................... [  7%]
........................                                                 [  9%]
tests/test_compat.py ...                                                 [  9%]
tests/test_config.py ....                                                [ 10%]
tests/test_converters.py .................................               [ 12%]
tests/test_dunders.py .................................................. [ 16%]
.................................................                        [ 20%]
tests/test_filters.py .................................                  [ 22%]
tests/test_funcs.py ...................................................  [ 26%]
tests/test_functional.py ............................................... [ 29%]
........................................................................ [ 35%]
........................................................................ [ 40%]
........................................................................ [ 46%]
........................................................................ [ 51%]
........................................................................ [ 56%]
                                                                         [ 56%]
tests/test_hooks.py ..........                                           [ 57%]
tests/test_import.py .                                                   [ 57%]
tests/test_init_subclass.py ....                                         [ 58%]
tests/test_make.py ..................................................... [ 62%]
........................................................................ [ 67%]
........................................................................ [ 72%]
..........................................................s.......       [ 77%]
tests/test_next_gen.py .................................                 [ 80%]
tests/test_packaging.py ......                                           [ 80%]
tests/test_pattern_matching.py .....                                     [ 81%]
tests/test_pyright.py ss                                                 [ 81%]
tests/test_setattr.py ..................x..................              [ 83%]
tests/test_slots.py .............s...................................... [ 87%]
.                                                                        [ 87%]
tests/test_utils.py ..                                                   [ 88%]
tests/test_validators.py ............................................... [ 91%]
........................................................................ [ 97%]
..................................                                       [ 99%]
tests/test_version_info.py ......                                        [100%]

=================================== FAILURES ===================================
___________________ TestAnnotations.test_typing_annotations ____________________

self = <tests.test_annotations.TestAnnotations object at 0x7fd4a4cb4d90>

    def test_typing_annotations(self):
        """
        Sets the `Attribute.type` attr from typing annotations.
        """

        @attr.resolve_types
        @attr.s
        class C:
            x: list[int] = attr.ib()
            y = attr.ib(type=typing.Optional[str])

>       assert list[int] is attr.fields(C).x.type
E       AssertionError: assert list[int] is list[int]
E        +  where list[int] = Attribute(name='x', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash... metadata=mappingproxy({}), type=list[int], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='x').type
E        +    where Attribute(name='x', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash... metadata=mappingproxy({}), type=list[int], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='x') = (Attribute(name='x', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, has...ppingproxy({}), type=typing.Optional[str], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='y')).x
E        +      where (Attribute(name='x', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, has...ppingproxy({}), type=typing.Optional[str], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='y')) = <function fields at 0x7fd4a4d276d0>(<class 'tests.test_annotations.TestAnnotations.test_typing_annotations.<locals>.C'>)
E        +        where <function fields at 0x7fd4a4d276d0> = attr.fields

tests/test_annotations.py:78: AssertionError
_____________ TestAnnotations.test_resolve_types_auto_attrib[True] _____________

self = <tests.test_annotations.TestAnnotations object at 0x7fd4a4348850>
slots = True

    def test_resolve_types_auto_attrib(self, slots):
        """
        Types can be resolved even when strings are involved.
        """

        @attr.s(slots=slots, auto_attribs=True)
        class A:
            a: list[int]
            b: list["int"]
            c: "typing.List[int]"

        # Note: I don't have to pass globals and locals here because
        # int is a builtin and will be available in any scope.
        attr.resolve_types(A)

        assert list[int] == attr.fields(A).a.type
>       assert list[int] == attr.fields(A).b.type
E       AssertionError: assert list[int] == list['int']
E        +  where list['int'] = Attribute(name='b', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash...etadata=mappingproxy({}), type=list['int'], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='b').type
E        +    where Attribute(name='b', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash...etadata=mappingproxy({}), type=list['int'], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='b') = (Attribute(name='a', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, has...a=mappingproxy({}), type=typing.List[int], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='c')).b
E        +      where (Attribute(name='a', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, has...a=mappingproxy({}), type=typing.List[int], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='c')) = <function fields at 0x7fd4a4d276d0>(<class 'tests.test_annotations.TestAnnotations.test_resolve_types_auto_attrib.<locals>.A'>)
E        +        where <function fields at 0x7fd4a4d276d0> = attr.fields

tests/test_annotations.py:569: AssertionError
____________ TestAnnotations.test_resolve_types_auto_attrib[False] _____________

self = <tests.test_annotations.TestAnnotations object at 0x7fd4a43486a0>
slots = False

    def test_resolve_types_auto_attrib(self, slots):
        """
        Types can be resolved even when strings are involved.
        """

        @attr.s(slots=slots, auto_attribs=True)
        class A:
            a: list[int]
            b: list["int"]
            c: "typing.List[int]"

        # Note: I don't have to pass globals and locals here because
        # int is a builtin and will be available in any scope.
        attr.resolve_types(A)

        assert list[int] == attr.fields(A).a.type
>       assert list[int] == attr.fields(A).b.type
E       AssertionError: assert list[int] == list['int']
E        +  where list['int'] = Attribute(name='b', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash...etadata=mappingproxy({}), type=list['int'], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='b').type
E        +    where Attribute(name='b', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash...etadata=mappingproxy({}), type=list['int'], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='b') = (Attribute(name='a', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, has...a=mappingproxy({}), type=typing.List[int], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='c')).b
E        +      where (Attribute(name='a', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, has...a=mappingproxy({}), type=typing.List[int], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='c')) = <function fields at 0x7fd4a4d276d0>(<class 'tests.test_annotations.TestAnnotations.test_resolve_types_auto_attrib.<locals>.A'>)
E        +        where <function fields at 0x7fd4a4d276d0> = attr.fields

tests/test_annotations.py:569: AssertionError
______________ TestAnnotations.test_resolve_types_decorator[True] ______________

self = <tests.test_annotations.TestAnnotations object at 0x7fd4a4348cd0>
slots = True

    def test_resolve_types_decorator(self, slots):
        """
        Types can be resolved using it as a decorator.
        """

        @attr.resolve_types
        @attr.s(slots=slots, auto_attribs=True)
        class A:
            a: list[int]
            b: list["int"]
            c: "typing.List[int]"

        assert list[int] == attr.fields(A).a.type
>       assert list[int] == attr.fields(A).b.type
E       AssertionError: assert list[int] == list['int']
E        +  where list['int'] = Attribute(name='b', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash...etadata=mappingproxy({}), type=list['int'], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='b').type
E        +    where Attribute(name='b', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash...etadata=mappingproxy({}), type=list['int'], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='b') = (Attribute(name='a', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, has...a=mappingproxy({}), type=typing.List[int], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='c')).b
E        +      where (Attribute(name='a', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, has...a=mappingproxy({}), type=typing.List[int], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='c')) = <function fields at 0x7fd4a4d276d0>(<class 'tests.test_annotations.TestAnnotations.test_resolve_types_decorator.<locals>.A'>)
E        +        where <function fields at 0x7fd4a4d276d0> = attr.fields

tests/test_annotations.py:585: AssertionError
_____________ TestAnnotations.test_resolve_types_decorator[False] ______________

self = <tests.test_annotations.TestAnnotations object at 0x7fd4a434a740>
slots = False

    def test_resolve_types_decorator(self, slots):
        """
        Types can be resolved using it as a decorator.
        """

        @attr.resolve_types
        @attr.s(slots=slots, auto_attribs=True)
        class A:
            a: list[int]
            b: list["int"]
            c: "typing.List[int]"

        assert list[int] == attr.fields(A).a.type
>       assert list[int] == attr.fields(A).b.type
E       AssertionError: assert list[int] == list['int']
E        +  where list['int'] = Attribute(name='b', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash...etadata=mappingproxy({}), type=list['int'], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='b').type
E        +    where Attribute(name='b', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash...etadata=mappingproxy({}), type=list['int'], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='b') = (Attribute(name='a', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, has...a=mappingproxy({}), type=typing.List[int], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='c')).b
E        +      where (Attribute(name='a', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, has...a=mappingproxy({}), type=typing.List[int], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='c')) = <function fields at 0x7fd4a4d276d0>(<class 'tests.test_annotations.TestAnnotations.test_resolve_types_decorator.<locals>.A'>)
E        +        where <function fields at 0x7fd4a4d276d0> = attr.fields

tests/test_annotations.py:585: AssertionError
_________________ TestAnnotations.test_forward_reference[True] _________________

self = <tests.test_annotations.TestAnnotations object at 0x7fd4a437ffa0>
slots = True

    def test_forward_reference(self, slots):
        """
        Forward references can be resolved.
        """
        if PY_3_14_PLUS and not slots:
            pytest.xfail("Forward references are changing a lot in 3.14.")

        @attr.s(slots=slots, auto_attribs=True)
        class A:
            a: list["B"]  # will resolve below -- noqa: F821

        @attr.s(slots=slots, auto_attribs=True)
        class B:
            a: A

        attr.resolve_types(A, globals(), locals())
        attr.resolve_types(B, globals(), locals())

>       assert list[B] == attr.fields(A).a.type
E       AssertionError: assert list[tests.test_annotations.TestAnnotations.test_forward_reference.<locals>.B] == list['B']
E        +  where list['B'] = Attribute(name='a', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash... metadata=mappingproxy({}), type=list['B'], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='a').type
E        +    where Attribute(name='a', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash... metadata=mappingproxy({}), type=list['B'], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='a') = (Attribute(name='a', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, has...etadata=mappingproxy({}), type=list['B'], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='a'),).a
E        +      where (Attribute(name='a', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, has...etadata=mappingproxy({}), type=list['B'], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='a'),) = <function fields at 0x7fd4a4d276d0>(<class 'tests.test_annotations.TestAnnotations.test_forward_reference.<locals>.A'>)
E        +        where <function fields at 0x7fd4a4d276d0> = attr.fields

tests/test_annotations.py:623: AssertionError
________________ TestAnnotations.test_forward_reference[False] _________________

self = <tests.test_annotations.TestAnnotations object at 0x7fd4a4d083d0>
slots = False

    def test_forward_reference(self, slots):
        """
        Forward references can be resolved.
        """
        if PY_3_14_PLUS and not slots:
            pytest.xfail("Forward references are changing a lot in 3.14.")

        @attr.s(slots=slots, auto_attribs=True)
        class A:
            a: list["B"]  # will resolve below -- noqa: F821

        @attr.s(slots=slots, auto_attribs=True)
        class B:
            a: A

        attr.resolve_types(A, globals(), locals())
        attr.resolve_types(B, globals(), locals())

>       assert list[B] == attr.fields(A).a.type
E       AssertionError: assert list[tests.test_annotations.TestAnnotations.test_forward_reference.<locals>.B] == list['B']
E        +  where list['B'] = Attribute(name='a', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash... metadata=mappingproxy({}), type=list['B'], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='a').type
E        +    where Attribute(name='a', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash... metadata=mappingproxy({}), type=list['B'], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='a') = (Attribute(name='a', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, has...etadata=mappingproxy({}), type=list['B'], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='a'),).a
E        +      where (Attribute(name='a', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, has...etadata=mappingproxy({}), type=list['B'], converter=None, kw_only=False, inherited=False, on_setattr=None, alias='a'),) = <function fields at 0x7fd4a4d276d0>(<class 'tests.test_annotations.TestAnnotations.test_forward_reference.<locals>.A'>)
E        +        where <function fields at 0x7fd4a4d276d0> = attr.fields

tests/test_annotations.py:623: AssertionError
_____________________ TestAnnotations.test_init_type_hints _____________________

self = <tests.test_annotations.TestAnnotations object at 0x7fd4a4a6ff40>

    def test_init_type_hints(self):
        """
        Forward references in __init__ can be automatically resolved.
        """

        @attr.s
        class C:
            x = attr.ib(type="typing.List[int]")

>       assert_init_annotations(C, x=list[int])
E       AssertionError: assert {'return': <c...x': list[int]} == {'return': <c...ing.List[int]}
E
E         Omitting 1 identical items, use -vv to show
E         Differing items:
E         {'x': list[int]} != {'x': typing.List[int]}
E         Use -v to get more diff

tests/test_annotations.py:638: AssertionError
================================== XFAILURES ===================================
______________________ TestSetAttr.test_slotted_confused _______________________

self = <tests.test_setattr.TestSetAttr object at 0x7fd4a335ee90>

    @pytest.mark.xfail(raises=attr.exceptions.FrozenAttributeError)
    def test_slotted_confused(self):
        """
        If we have a in-between non-attrs class, setattr reset detection
        should still work, but currently doesn't.

        It works with dict classes because we can look the finished class and
        patch it.  With slotted classes we have to deduce it ourselves.
        """

        @attr.s(slots=True)
        class A:
            x = attr.ib(on_setattr=setters.frozen)

        class B(A):
            pass

        @attr.s(slots=True)
        class C(B):
            x = attr.ib()

>       C(1).x = 2

tests/test_setattr.py:330:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
<attrs generated init tests.test_setattr.TestSetAttr.test_slotted_confused.<locals>.C>:2: in __init__
    self.x = x
../../BUILDROOT/python-attrs-24.2.0-2.fc37.x86_64/usr/lib/python3.10/site-packages/attr/_make.py:1052: in __setattr__
    nval = hook(self, a, val)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

_ = <[AttributeError("'C' object has no attribute 'x'") raised in repr()] C object at 0x7fd4987cdd00>
__ = Attribute(name='x', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash..., type=None, converter=None, kw_only=False, inherited=False, on_setattr=<function frozen at 0x7fd4a4d25990>, alias='x')
___ = 1

    def frozen(_, __, ___):
        """
        Prevent an attribute to be modified.

        .. versionadded:: 20.1.0
        """
>       raise FrozenAttributeError()
E       attr.exceptions.FrozenAttributeError

../../BUILDROOT/python-attrs-24.2.0-2.fc37.x86_64/usr/lib/python3.10/site-packages/attr/setters.py:35: FrozenAttributeError
=========================== short test summary info ============================
SKIPPED [2] tests/test_annotations.py:96: Incompatible behavior on older Pythons
SKIPPED [2] tests/test_annotations.py:392: Incompatible behavior on older Pythons
SKIPPED [1] tests/test_make.py:2563: Pre-3.10 only.
SKIPPED [1] tests/test_pyright.py:35: Requires pyright.
SKIPPED [1] tests/test_pyright.py:85: Requires pyright.
SKIPPED [1] tests/test_slots.py:503: slots without weakref_slot should only work on PyPy
XFAIL tests/test_setattr.py::TestSetAttr::test_slotted_confused
FAILED tests/test_annotations.py::TestAnnotations::test_typing_annotations - ...
FAILED tests/test_annotations.py::TestAnnotations::test_resolve_types_auto_attrib[True]
FAILED tests/test_annotations.py::TestAnnotations::test_resolve_types_auto_attrib[False]
FAILED tests/test_annotations.py::TestAnnotations::test_resolve_types_decorator[True]
FAILED tests/test_annotations.py::TestAnnotations::test_resolve_types_decorator[False]
FAILED tests/test_annotations.py::TestAnnotations::test_forward_reference[True]
FAILED tests/test_annotations.py::TestAnnotations::test_forward_reference[False]
FAILED tests/test_annotations.py::TestAnnotations::test_init_type_hints - Ass...
============ 8 failed, 1318 passed, 8 skipped, 1 xfailed in 24.08s =============

kloczek avatar Sep 21 '24 10:09 kloczek