Accept nesting Final in ClassVar for dataclassesΒ #18235
Description
Bug Report
[Python 3.13] mypy
does not accept nesting ClassVar
and Final
in class body
The official docs of the typing
module say:
Changed in version 3.13:
ClassVar
can now be nested inFinal
and vice versa.
The typing specs on qualifiers are more specific:
Final
may be wrapped only by other type qualifiers (e.g.ClassVar
or
Annotation
).
In particular, nesting ClassVar
and Final
is a special case for dataclasses:
Type checkers should infer a final attribute that is initialized in a class
body as being a class variable, except in the case ofdataclasses
, where
x: Final[int] = 3
creates a dataclass field and instance-level final
attributex
with default value3
;x: ClassVar[Final[int]] = 3
is
necessary to create a final class variable with value3
. In
non-dataclasses, combiningClassVar
andFinal
is redundant, and type
checkers may choose to warn or error on the redundancy.
To Reproduce
In both classes below the foo
statement is not accepted by mypy
:
from dataclasses import dataclass
from typing import ClassVar, Final
@dataclass
class Bar:
foo: ClassVar[Final[int]] = 0
@dataclass
class Baz:
foo: Final[ClassVar[int]] = 0
Note that the docs only mention ClassVar[Final[int]]
and not Final[ClassVar[int]]
. In fact, the latter is not considered a class variable by the current dataclasses
implementation:
>>> Bar(foo=1) # foo is a class variable
Traceback (most recent call last):
File "<python-input-2>", line 1, in <module>
Bar(foo=1)
~~~^^^^^^^
TypeError: Bar.__init__() got an unexpected keyword argument 'foo'
>>> Baz(foo=1) # foo is an instance variable
Baz(foo=1)
Expected Behavior
[Python 3.13] mypy
should accept ClassVar[Final[<type>]]
in the body of a dataclass
The form ClassVar[Final[int]]
conforms with the official typing specs and behaves as expected. I think it should be accepted as part of Python 3.13 support.
The typing specs allow for a warning or error when ClassVar
and Final
are nested outside of a dataclass body. I think this should result in an error since it is redundant, perhaps only in strict mode.
For the form Final[ClassVar[int]]
I am not sure. It could either result in an error or simply be parsed as a final instance variable.
Actual Behavior
The form ClassVar[Final[int]]
results in:
error: Final can be only used as an outermost qualifier in a variable annotation [valid-type]
The form Final[ClassVar[int]]
results in:
error: Variable should not be annotated with both ClassVar and Final [misc]
The behavior is the same with or without the @dataclass
decorator.
Your Environment
- Mypy version used: 1.13.0
- Mypy command-line flags:
- Mypy configuration options from
mypy.ini
(and other config files):strict = true
- Python version used: 3.13.0
Notes
The typing specs were changed in python/typing#1669
Related discussion is found in python/cpython#89547
The difference between ClassVar[Final[int]]
and Final[ClassVar[int]]
was noticed in microsoft/pyright#8676 (comment)
This issue is about Python 3.13 so could be added to #17264
This issue is related to #12061