Skip to content

Python 3.13 regression: Recursive dataclasses fail to ==: RecursionError: maximum recursion depth exceeded #116647

Closed
@hroncok

Description

@hroncok

Bug report

Bug description:

There is a regression in comparing recursive dataclasses for equality in Python 3.13.0 from the first alpha until at least a4.

Python 3.12

>>> from dataclasses import dataclass
>>> @dataclass
... class C:
...     recursive: object = ...
... 
>>> c1 = C()
>>> c1.recursive = c1
>>> c1 == c1
True

Python 3.13

>>> from dataclasses import dataclass
>>> @dataclass
... class C:
...     recursive: object = ...
... 
>>> c1 = C()
>>> c1.recursive = c1
>>> c1 == c1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    c1 == c1
  File "<string>", line 4, in __eq__
  File "<string>", line 4, in __eq__
  File "<string>", line 4, in __eq__
  [Previous line repeated 996 more times]
RecursionError: maximum recursion depth exceeded

This has started happening since 18cfc1e.

Previously, tuples were compared via ==, which skips calling __eq__ for items with the same identity. Now it skips the identity check and compares items directly with __eq__, causing RecursionError.

This breaks sip-build; hence, we cannot build pyqt5 or pyqt6 in Fedora with Python 3.13 to test the entire stack. For details, see this Fedora bugzilla.

sip-build: An internal error occurred...
Traceback (most recent call last):
  File "/usr/bin/sip-build", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/usr/lib64/python3.13/site-packages/sipbuild/tools/build.py", line 37, in main
    handle_exception(e)
  File "/usr/lib64/python3.13/site-packages/sipbuild/exceptions.py", line 81, in handle_exception
    raise e
  File "/usr/lib64/python3.13/site-packages/sipbuild/tools/build.py", line 34, in main
    project.build()
  File "/usr/lib64/python3.13/site-packages/sipbuild/project.py", line 245, in build
    self.builder.build()
  File "/usr/lib64/python3.13/site-packages/sipbuild/builder.py", line 48, in build
    self._generate_bindings()
  File "/usr/lib64/python3.13/site-packages/sipbuild/builder.py", line 280, in _generate_bindings
    buildable = bindings.generate()
                ^^^^^^^^^^^^^^^^^^^
  File "/builddir/build/BUILD/PyQt5-5.15.9/project.py", line 619, in generate
    buildable = super().generate()
                ^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.13/site-packages/sipbuild/bindings.py", line 214, in generate
    output_pyi(spec, project, pyi_path)
  File "/usr/lib64/python3.13/site-packages/sipbuild/generator/outputs/pyi.py", line 53, in output_pyi
    _module(pf, spec, module)
  File "/usr/lib64/python3.13/site-packages/sipbuild/generator/outputs/pyi.py", line 132, in _module
    _class(pf, spec, module, klass, defined)
  File "/usr/lib64/python3.13/site-packages/sipbuild/generator/outputs/pyi.py", line 267, in _class
    _class(pf, spec, module, nested, defined, indent)
  File "/usr/lib64/python3.13/site-packages/sipbuild/generator/outputs/pyi.py", line 289, in _class
    _callable(pf, spec, module, member, klass.overloads,
  File "/usr/lib64/python3.13/site-packages/sipbuild/generator/outputs/pyi.py", line 485, in _callable
    _overload(pf, spec, module, overload, overloaded, first_overload,
  File "/usr/lib64/python3.13/site-packages/sipbuild/generator/outputs/pyi.py", line 575, in _overload
    signature = _python_signature(spec, module, py_signature, defined,
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.13/site-packages/sipbuild/generator/outputs/pyi.py", line 599, in _python_signature
    as_str = _argument(spec, module, arg, defined, arg_nr=arg_nr)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.13/site-packages/sipbuild/generator/outputs/pyi.py", line 676, in _argument
    s += _type(spec, module, arg, defined, out=out)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.13/site-packages/sipbuild/generator/outputs/pyi.py", line 710, in _type
    return ArgumentFormatter(spec, arg).as_type_hint(module, out, defined)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.13/site-packages/sipbuild/generator/outputs/formatters/argument.py", line 327, in as_type_hint
    s += TypeHintManager(self.spec).as_type_hint(hint, out, context,
         ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.13/site-packages/sipbuild/generator/outputs/type_hints.py", line 107, in __new__
    manager = cls._spec_manager_map[spec]
              ~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "/usr/lib64/python3.13/weakref.py", line 415, in __getitem__
    return self.data[ref(key)]
           ~~~~~~~~~^^^^^^^^^^
  File "<string>", line 4, in __eq__
  File "<string>", line 4, in __eq__
  File "<string>", line 4, in __eq__
  [Previous line repeated 495 more times]
RecursionError: maximum recursion depth exceeded in comparison

cc @rhettinger @ericvsmith

Thanks to @swt2c for bisecting the problem. Thanks to @encukou who gave me a hint of what to look for when finding the smaller reproducer.

CPython versions tested on:

3.13

Operating systems tested on:

Linux

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    stdlibPython modules in the Lib dirtype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions