In Python, attributes that are declared at class toplevel but are uninitialized there cannot be directly accessed from the class itself at runtime. For instance,
class Foo:
x: int
Foo.x # Runtime crash! type object 'Foo' has no attribute 'x'
This makes sense because at runtime, the bare x: int
“declaration” on Foo
toplevel is just an annotated assignment statement without a RHS, which semantically is a no-op. Nothing actually gets added to the class Foo
. So attribute-accessing x
from class Foo
directly is almost always a runtime crash.
But if we try to assign an initial value to the attribute at class toplevel, x
will be there:
class Bar:
x: int = 42
Bar.x # OK!
Currnetly, neither mypy
and pyright
checks this kind of issue. But in pyrefly
(and in ty
according to Alex), we are trying to emit type errors when we detect that the user tries to access uninitialized attributes from class names directly. Our experience has been that this check rarely misfires for classes defined in user-written .py
source files, but it misfires every now and then for classes that are defined in stub files – since stub files don’t quite pay much attention to differentiating between the initialized and uninitialized cases. In particular, Typeshed currently follows the following convention, according to Sebastian:
So far in typeshed, we’ve treated
x: int
andx: int = ...
not only as identical, but the latter form even as outdated.
As an example, builtins.object
is declared to have 4 uninitialized class-level attributes, but at runtime all these 4 attributes are always initialized on the object
class. This means that every time someone attribute-accesses __doc__
or __dict__
etc. on any classes, Pyrefly is going to complain that these attributes are not initialized:
Such typeshed convention quite limits the usability of Pyrefly’s uninitialized-class-attribute check at the moment so I’m inclined to change it. But before I make my attempt I’d also like to see what folks think about this idea: is differentiating between initialized and uninitialized class-level attribute considered beneficial enough? What’s the chance of elevating the behavior/convention to the typing spec?