cattrs
cattrs copied to clipboard
attrs 23.1.0 breaks string annotated generics
- cattrs version: 23.1.2
- Python version: 3.10.7
- Operating System: Ubuntu
Description
When working with generic attrs types with cattrs, I updated my version of attrs and it caused failures. Having looked at the changelog in attrs:
https://www.attrs.org/en/stable/changelog.html#id1
This seems likely it was caused by:
attrs.has() and attrs.fields() now handle generic classes correctly. #1079
Changing the behaviour of: (converters.py 976-977)
attribs = fields(get_origin(cl) or cl if is_generic(cl) else cl)
if attrs_has(cl) and any(isinstance(a.type, str) for a in attribs):
in which case it probably can't be described as an attrs bug. (But I don't have high confidence in that).
(I confirmed that
resolve_types(cl)
failed on both versions of attrs, so the difference is somewhere in the if statement)
What I Did
from __future__ import annotations
from typing import TypeVar, Generic
import attrs
import cattrs
T = TypeVar('T')
@attrs.frozen
class A(Generic[T]):
a: T
converter = cattrs.Converter()
converter.gen_structure_attrs_fromdict(A[int]) # This fails with attrs 23.1.0
#And hence the lower calls won't work
cattrs.structure({'a': 1}, A[int]) # Succeeds with attrs 22.2.0
cattrs.structure({'a': 'one'}, A[int]) # Fails as expected with attrs 22.2.0
Interesting. It was me who implemented that change in attrs in response to a bug report, so 🙈
Looks like attrs.resolve_types breaks on generic attrs classes, because typing.get_type_hints breaks on generic attrs classes.
I can confirm there's an issue. It took me a while to figure out why it works on old attrs though. I need to talk to @hynek to figure out on which end do we want to fix this though.
In the meantime I think you can resolve the issue by calling attrs.resolve_types(A) by yourself before using the class.