typing
typing copied to clipboard
Interaction between ClassVar/Final and Annotated
The documentation (typing
, PEP 526, PEP 591, PEP 593) is silent on how Annotated
and ClassVar
/Final
should be nested.
Intuitively, Annotated
should appear on the outside, as the annotation applies to the whole ClassVar
/Final
variable, and as it's easiest to strip from that point.
In fact, this is not accepted by the Python runtime:
from typing import (
Annotated,
ClassVar,
)
class Foo:
a: Annotated[ClassVar[int], 'hello'] = 42
# raises: TypeError: typing.ClassVar[int] is not valid as
# type argument
b: ClassVar[Annotated[int, 'hello']] = 42
# accepted by the Python runtime
My intuition says that this behaviour is undesirable.
Even if this behaviour is correct, in my humble opinion, the documentation should be amended to speak to how Annotated
and ClassVar
/Final
interact.
FWIW this first case was actually relaxed in python 3.11 so both forms work now.
% python3.11
Python 3.11.0b2 (v3.11.0b2:72f00f420a, May 31 2022, 01:35:28) [Clang 13.0.0 (clang-1300.0.29.30)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import typing
>>> typing.Annotated[typing.ClassVar[int], ""]
typing.Annotated[typing.ClassVar[int], '']
>>> typing.ClassVar[typing.Annotated[int, ""]]
typing.ClassVar[typing.Annotated[int, '']]
Thanks, @Gobot1234. If that's the case, I think this issue can be considered resolved.
As @Gobot1234 said the runtime restriction will be relaxed in 3.11. My general goal is to keep the runtime permissive, so it's easy for type checkers and users to experiment with new forms of type annotations.
That said, it's not unreasonable for type checkers to reject this form. Annotated is meant to be used on types, and ClassVar/Final are type modifiers, not types themselves. So you could make the argument that Annotated should not be used outside ClassVar/Final. I'm fine with leaving that up to individual type checker authors for now.
Per this gist, what's happening today with mypy isn't maximally permissive, but it is consistent with the runtime:
from typing import (
ClassVar,
)
from typing_extensions import (
Annotated,
)
class Foo:
a: Annotated[ClassVar[int], 'hello'] = 42
# accepted by the Python runtime (3.8, 3.11)
# mypy error:
# Invalid type: ClassVar nested inside other type [valid-type]
b: ClassVar[Annotated[int, 'hello']] = 42
# accepted by the Python runtime (3.8, 3.11)
# accepted by mypy
If others are agreed, I think it's reasonable to close this issue.