attrs icon indicating copy to clipboard operation
attrs copied to clipboard

distinguish a class that inherits from an attrs class from a true (decorated) attrs class

Open Darkdragon84 opened this issue 1 year ago • 4 comments

This question is related to issue https://github.com/python-attrs/attrs/issues/1332, where I needed an __init_subclass__ to be run only once for subclasses of an attrs base class. The issue with using built-in __init_subclass__ is that it doesn't play well with decorated classes, such as attrs or dataclass classes. Hence the addition of a new __attrs_init_subclass__ classmethod.

However, this method (rightfully) only gets called for subclasses that are explicitly decorated as attrs classes. I need a way to run code in an __init_subclass__ method of an attrs class for both attrs and plain old Python (POP) subclasses. I first thought of checking attrs.has on the subclass to see if it is an attrs or POP class. But of course the subclass inherits the __attrs_attrs__ field from the attrs base class, so this can't be used to make the distinction.

My question is thus: How can I distinguish a class that inherits from an attrs class from a true (decorated) attrs class? Some search suggests one way to achieve this is to check whether __attrs_attrs__ is in the class dict of the subclass.

from attrs import frozen


def is_directly_decorated(cls) -> bool:
    return "__attrs_attrs__" in cls.__dict__


def print_if_directly_decorated(cls):
    print(
        f"{cls.__name__} "
        + ("is" if is_directly_decorated(cls) else "isn't")
        + " directly decorated"
    )


@frozen
class BaseAttr: ...


class Sub(BaseAttr): ...


@frozen
class SubAttr(BaseAttr): ...


print_if_directly_decorated(Sub)
print_if_directly_decorated(SubAttr)

this gives

Sub isn't directly decorated
SubAttr is directly decorated

Is this a safe way to do this? Or can I exploit some built-in or attrs functions/methods to make this distinction?

Darkdragon84 avatar Aug 12 '24 09:08 Darkdragon84

Checking the class's __dict__ is how we check for direct attributes since 24.1, too.

hynek avatar Aug 13 '24 04:08 hynek

thanks for confirming 👍 would it make sense to add anything along those lines to attr.has?

Darkdragon84 avatar Aug 16 '24 10:08 Darkdragon84

I think it makes sense to expand has a bit (also maybe accept instances), but it would’ve to be gated behind an option I’m afraid. :|

hynek avatar Aug 17 '24 10:08 hynek

oh that's totally fine! Thanks for considering this :pray:

Darkdragon84 avatar Aug 19 '24 08:08 Darkdragon84