python-attrs / cattrs

Composable custom class converters for attrs, dataclasses and friends.
https://catt.rs
MIT License
791 stars 110 forks source link

include_subclasses does not support dataclasses #426

Closed eltoder closed 8 months ago

eltoder commented 11 months ago

Description

According to the documentation, include_subclasses() supports both attrs and dataclasses. However, when I change the example to use dataclasses, I get an error:

from dataclasses import dataclass
from cattrs.strategies import include_subclasses
from cattrs import Converter

@dataclass
class Parent:
    a: int

@dataclass
class Child(Parent):
    b: str

converter = Converter()
include_subclasses(Parent, converter)

produces

Traceback (most recent call last):
  File "/home/eltoder/dev/scratch/cattrs_dc.py", line 14, in <module>
    include_subclasses(Parent, converter)
  File "/home/eltoder/.local/share/ext-python/python-3.9.16.146/lib/python3.9/site-packages/cattrs/strategies/_subclasses.py", line 73, in include_subclasses
    _include_subclasses_without_union_strategy(
  File "/home/eltoder/.local/share/ext-python/python-3.9.16.146/lib/python3.9/site-packages/cattrs/strategies/_subclasses.py", line 111, in _include_subclasses_without_union_strategy
    dis_fn = converter._get_dis_func(subclass_union)
  File "/home/eltoder/.local/share/ext-python/python-3.9.16.146/lib/python3.9/site-packages/cattrs/converters.py", line 756, in _get_dis_func
    return create_uniq_field_dis_func(*union_types)
  File "/home/eltoder/.local/share/ext-python/python-3.9.16.146/lib/python3.9/site-packages/cattrs/disambiguators.py", line 20, in create_uniq_field_dis_func
    cls_and_attrs = [
  File "/home/eltoder/.local/share/ext-python/python-3.9.16.146/lib/python3.9/site-packages/cattrs/disambiguators.py", line 21, in <listcomp>
    (cl, set(at.name for at in fields(get_origin(cl) or cl))) for cl in classes
  File "/home/eltoder/.local/share/ext-python/python-3.9.16.146/lib/python3.9/site-packages/attr/_make.py", line 1944, in fields
    raise NotAnAttrsClassError(f"{cls!r} is not an attrs-decorated class.")
attr.exceptions.NotAnAttrsClassError: <class '__main__.Child'> is not an attrs-decorated class.

It does work if I use union_strategy:

from cattrs.strategies import configure_tagged_union
include_subclasses(Parent, converter, union_strategy=configure_tagged_union)

Additionally, when union_strategy=configure_tagged_union is used, it would be nice to avoid using __subclasses__ so that new classes can be added dynamically. Let me know if you want me to open a separate issue for this.

aha79 commented 11 months ago

I guess this is because the automatic union disambiguation (based on unique fields) is exclusively for attrs classes (see https://catt.rs/en/stable/structuring.html#automatic-disambiguation).

Tinche commented 11 months ago

Ah, yeah. The easiest fix here would be to just support dataclasses for the default disambiguator. I think it's an easy and useful change. But not in the next release, since it's getting bloated already.

As for the other issue, feel free to open a new ticket and we can triage it.