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

Final class variable in dataclass breaks __init__ signature #9550

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

Final class variable in dataclass breaks __init__ signature #9550

ncanceill opened this issue Dec 5, 2024 · 1 comment
Labels
addressed in next version Issue is fixed and will appear in next published version bug Something isn't working

Comments

@ncanceill
Copy link

Describe the bug
Python 3.13 allows ClassVar[Final[<type>]] annotations for dataclass fields. Pyright 1.1.390 accepts the annotation but gets the __init__() signature wrong.

When a dataclass field is annotated as ClassVar[Final[<type>]] it is not added as parameter to the generated __init__() method. The dataclass below has a final class variable bar with a default value followed by an instance variable baz without default value. Python 3.13 accepts this code and generates a method __init__(self, baz) as expected.

Pyright 1.1.390 seems to parse bar as a regular field and therefore:

  • does not accept baz because it comes after a field with a default value
  • computes a wrong signature __init__(self, bar = 0, baz)

Code or Screenshots

from dataclasses import dataclass
from typing import ClassVar, Final, reveal_type

@dataclass
class Foo:
    bar: ClassVar[Final[int]] = 0
    baz: int
    # Fields without default values cannot appear after fields with default values (reportGeneralTypeIssues)

Foo(1)
# Argument missing for parameter "baz" (reportCallIssue)

reveal_type(Foo.__init__)
# Type of "Foo.__init__" is "(self: Foo, bar: int = 0, baz: int) -> None"

Additional info

This passes Pyright but forgoes the Final annotation:

@dataclass
class Foo:
    bar: ClassVar[int] = 0
    baz: int

reveal_type(Foo.__init__)
# Type of "Foo.__init__" is "(self: Foo, baz: int) -> None"

This still gets the wrong signature:

@dataclass
class Foo:
    baz: int
    bar: ClassVar[Final[int]] = 0

reveal_type(Foo.__init__)
# Type of "Foo.__init__" is "(self: Foo, baz: int, bar: int = 0) -> None"

So does this:

@dataclass(kw_only=True)
class Foo:
    bar: ClassVar[Final[int]] = 0
    baz: int

reveal_type(Foo.__init__)
# Type of "Foo.__init__" is "(self: Foo, *, bar: int = 0, baz: int) -> None"

As noted in #8676 (comment) the Python 3.13.0 implementation does not treat dataclass fields annotated Final[ClassVar[<type>]] as class variables. As a result, Pyright is correct here:

@dataclass
class Foo:
    bar: Final[ClassVar[int]] = 0
    baz: int
    # Fields without default values cannot appear after fields with default values (reportGeneralTypeIssues)

Foo(1)
# Argument missing for parameter "baz" (reportCallIssue)

reveal_type(Foo.__init__)
# Type of "Foo.__init__" is "(self: Foo, bar: int = 0, baz: int) -> None"

VS Code extension or command-line
Command-line, Python 3.13.0, Pyright 1.1.390

@ncanceill ncanceill added the bug Something isn't working label Dec 5, 2024
@erictraut
Copy link
Collaborator

Thanks for reporting the issue. I agree it's a bug.

erictraut added a commit that referenced this issue Dec 6, 2024
…Final` qualifier are both used on the same dataclass attribute. This addresses #9550.
erictraut added a commit that referenced this issue Dec 6, 2024
…Final` qualifier are both used on the same dataclass attribute. This addresses #9550. (#9553)
@erictraut erictraut added the addressed in next version Issue is fixed and will appear in next published version label Dec 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
addressed in next version Issue is fixed and will appear in next published version bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants