Skip to content

include_subclasses fails for classes with generics. AttributeError: __subclasses__. Did you mean: '__subclasscheck__'? #648

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
zhukovgreen opened this issue May 21, 2025 · 4 comments · May be fixed by #650

Comments

@zhukovgreen
Copy link

zhukovgreen commented May 21, 2025

When a parent class with a generics passed to include_subclasses, then:

  1. You need to specify the generic type, otherwise getting
E   cattrs.errors.StructureHandlerNotFoundError: Missing type for generic argument T, specify it when structuring.
  1. If generic type is specified, you get the following issue
__________________________________________ ERROR collecting test_demo_bug.py __________________________________________
test_demo_bug.py:23: in <module>
    include_subclasses(Parent[str], c)
.venv/lib/python3.13/site-packages/cattrs/strategies/_subclasses.py:72: in include_subclasses
    parent_subclass_tree = tuple(_make_subclasses_tree(cl))
.venv/lib/python3.13/site-packages/cattrs/strategies/_subclasses.py:15: in _make_subclasses_tree
    sscl for scl in cl.__subclasses__() for sscl in _make_subclasses_tree(scl)
/Users/.pyenv/versions/3.13.3/lib/python3.13/typing.py:1366: in __getattr__
    raise AttributeError(attr)
E   AttributeError: __subclasses__. Did you mean: '__subclasscheck__'?

Quick reproducer:

import abc
from typing import Generic, TypeVar

import attrs
import cattrs
from cattrs.strategies import include_subclasses

T = TypeVar("T")


@attrs.define()
class Parent(
    Generic[T],
    metaclass=abc.ABCMeta,
):
    arg: T


class Child(Parent[str]): ...


c = cattrs.Converter()
# include_subclasses(Parent, c) - this fails with  cattrs.errors.StructureHandlerNotFoundError  Missing type for generic argument T, specify it when structuring.
include_subclasses(Parent[str], c)


def test_structuring():
    assert c.structure({"arg": 5}, Child) == Child("5")
@zhukovgreen
Copy link
Author

I will be happy to work on a fix here, just would like to make sure there is no similar issue/work already ongoing

@Tinche
Copy link
Member

Tinche commented May 21, 2025

Good catch. Feel free to try for a fix. Not sure it should work for c.structure({...}, Child) though, shouldn't it be Child[str]?

@zhukovgreen
Copy link
Author

zhukovgreen commented May 21, 2025

OK, great. I'll try to fix then. Regarding Child[str] - tried - the same issue. Also I updated the snippet. More typical definition of child is like this, when you have generic parent with not generic child:

@attrs.define()
class Parent(
    Generic[T],
    metaclass=abc.ABCMeta,
):
    arg: T


class Child(Parent[str]): ...

zhukovgreen added a commit to zhukovgreen/cattrs that referenced this issue May 22, 2025
Resolves python-attrs#648

The strategy was using parent_cls.__subclasses__() to get the
list of subclasses. In case of generics, this method
is unavailable.

The fix applies sanitizing the cl and getting its origin
class for getting the sublcasses tree.

The class itself remains generic in the tree.
@zhukovgreen
Copy link
Author

@Tinche pls approve the CI workflow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants