cattrs icon indicating copy to clipboard operation
cattrs copied to clipboard

Recursive Attrs class generation

Open kyteware opened this issue 2 years ago • 3 comments

  • cattrs version: 22.1.0
  • Python version: 3.11.0-rc1
  • Operating System: Fedora Linux

Description

I would like an easy way to recursively generate attrs classes that have attributes of their type (eg Foo.foo: Foo). Obviously, you can't directly annotate attributes as being of the same type of the class, as the class is not yet generated when specifying attrs attributes. However, Python 3.11 introduces typing.Self, a shortcut to annotating the class. Cattrs could implement this, allowing users to avoid using __attrs_post_init__ to achieve this.

What I Did

# I would like to be able to do this:
@define
class Bar:
    baz: typing.Self = None

@define
class Foo:
    bar: Bar

structure({"bar": {"baz": {"baz": None}}}, Foo)

kyteware avatar Sep 06 '22 04:09 kyteware

Interesting, I wasn't aware attributes could be annotates as Self but according to PEP 673, that's valid!

We don't support it yet, so I'll leave this open as an enhancement request.

Your annotation is a little incorrect though, it should be Self | None.

Until we fix the issue, you can use a stringified annotation instead, like this:

from attrs import define

from cattrs import structure


@define
class Bar:
    baz: "Bar | None" = None


@define
class Foo:
    bar: Bar


structure({"bar": {"baz": {"baz": None}}}, Foo)

This should work.

Tinche avatar Aug 07 '23 22:08 Tinche

Related discussion about the None default adding making the type annotation implicitly Optional: https://github.com/python/cpython/issues/90353#issuecomment-1093941076. This behavior got fixed in Python 3.11+, but it pertains to previous versions.

PIG208 avatar Aug 08 '23 00:08 PIG208

Sounds good. I don't use cattrs anymore, but I'm happy that this feature is getting looked at.

kyteware avatar Aug 08 '23 16:08 kyteware