Skip to content

Incorrect treatment of Final class variables when auto_attribs=True #784

@dargueta

Description

@dargueta

attrs appears to treat an initialized final class variable as an instance variable. I'd expect that this would follow PEP 591, specifically this sentence in the "Semantics and Examples" section:

Type checkers should infer a final attribute that is initialized in a class body as being a class variable. Variables should not be annotated with both ClassVar and Final.

However, that doesn't appear to be the case. Suppose we have the following code, where x is intended to be an "immutable" class variable and y is an instance variable:

@attr.s(auto_attribs=True)
class A:
    x: Final[int] = 123
    y: int

The code crashes with an error:

ValueError: No mandatory attributes allowed after an attribute with a default value or factory.  Attribute in question: Attribute(name='y', default=NOTHING, validator=None, repr=True, eq=True, order=True, hash=None, init=True, metadata=mappingproxy({}), type=<class 'int'>, converter=None, kw_only=False, inherited=False, on_setattr=None)

This indicates that x isn't being properly treated as a class variable and skipped over. Indeed, if we reverse the order of the declarations, it "works" in the sense that it doesn't crash, but it generates a class with incorrect behavior.

>>> @attr.s(auto_attribs=True) 
... class A: 
...     y: int 
...     x: Final[int] = 123                                                                                                                      

>>> A(y=1, x=2)                                                                                                                                  
A(y=1, x=2)

As you can see, x is unexpectedly an instance variable. If we're conforming to PEP 591, the correct behavior here would be to build a class equivalent to the following:

@attr.s(auto_attribs=True)
class A:
    x: ClassVar[int] = 123
    y: int

Environment:

  • CPython 3.8.8
  • attrs==20.3.0
  • OS: MacOS 11.2.3

Metadata

Metadata

Assignees

No one assigned

    Labels

    TypingTyping/stub/Mypy/PyRight related bugs.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions