attrs icon indicating copy to clipboard operation
attrs copied to clipboard

Accessing @attr.s() decorator parameter values

Open energizah opened this issue 4 years ago • 6 comments

In #python today someone wanted to find out if their class was frozen. Cls.__dataclass_params__ keeps the @dataclasses.dataclass() arguments.

_DataclassParams(init=True,repr=True,eq=True,order=False,unsafe_hash=False,frozen=True)

Is there a way to get that data in attrs?

energizah avatar Dec 02 '19 00:12 energizah

That was me. looking at source or attr make, it seems like this metadata is not currently maintained.

The reason I want this is because I am using attr (or dataclasses) as a template to generate an object that - among other things - performs runtime checks (type validation, recursive frozen check etc). This has some more context on what I am shooting for.

I was able to detect at runtime if a dataclass is frozen but was not able to do the same for attr classes.

omry avatar Dec 02 '19 01:12 omry

This seems to work, but it's very fragile:

def is_attr_frozen(type_):
    import attr

    return type_.__setattr__ == attr._make._frozen_setattrs

It works if I import the class normally (from foo.bar import FrozenClass), but does not work when I import using import_module.

FrozenClass = import_module("foo.bar").FrozenClass

It would be great if there was a supported API to determine that (and possibly attr parameters).

omry avatar Dec 02 '19 01:12 omry

FWIW, I find exposing more attrs-specific stuff onto classes to be problematic. That I use attrs is, to me, an implementation detail, and I don't intend "this is an attrs-built class" to be part of the interface to my classes.

The more attrs-specific introspection we add, the more locked-in to attrs classes become.

wsanchez avatar Dec 02 '19 19:12 wsanchez

@wsanchez, I agree with the sentiment, but it should still be possible for someone who wants to do this introspection to do it reliably if they choose.

attrs and dataclasses have a pretty good feature parity already, so the any lock-in is minimal. I am planning to support both in my library. this specific thing is one of the things I feel more uncomfortable with right now.

def is_dataclass_frozen(type_):
    return type_.__dataclass_params__.frozen


def is_attr_frozen(type_):
    import attr

    # This is very hacky and probably fragile as well.
    # Unfortunately currently there isn't an official API in attr that can detect that.
    return type_.__setattr__ == attr._make._frozen_setattrs

omry avatar Dec 02 '19 19:12 omry

Hmmm. So the thing is that I was kinda planning to expose class settings as a class internally anyway, because the amount of arguments is getting out of control.

However I plan too many internal changes soon-ish that I don't want to lo ourselves in rn.

Good to know about __dataclass_params__ – we should follow that naming convention if/once we implement it.


Wilfredo's argument is true but we always insisted on being extensible and allow introspection into attributes. It seems like a glaring omission to not allow introspection into the class itself.


I think I'll just pinky promise to not break your use case until we have an official API. ;)

hynek avatar Dec 05 '19 10:12 hynek

Thanks @hynek. Glad to hear you are planning on adding an official API for this. I totally understand the desire to keep things as is until you have had some time to think and figure out what the best API is, especially with a major internal change in the horizon.

Thanks for pinky promising not to break my implementation until then! :)

omry avatar Dec 05 '19 20:12 omry