Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accept nesting Final in ClassVar for dataclasses #18235

Open
ncanceill opened this issue Dec 3, 2024 · 1 comment
Open

Accept nesting Final in ClassVar for dataclasses #18235

ncanceill opened this issue Dec 3, 2024 · 1 comment
Labels

Comments

@ncanceill
Copy link

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 in Final 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 of dataclasses, where
x: Final[int] = 3 creates a dataclass field and instance-level final
attribute x with default value 3; x: ClassVar[Final[int]] = 3 is
necessary to create a final class variable with value 3. In
non-dataclasses, combining ClassVar and Final 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

@ncanceill ncanceill added the bug mypy got something wrong label Dec 3, 2024
@erictraut
Copy link

For additional context, this case is covered by the typing spec conformance tests, and mypy does not currently pass this test case. This is reflected in the conformance test result summary page. (Scroll to the dataclasses_final line and hover over the mypy results in the table.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants