Skip to content

Default value for Generic self is erased by @dataclass #14382

Not planned
@zmievsa

Description

@zmievsa

Note that pyright has a similar behavior so I am not sure whether it is a bug per se but I do believe that it is an incorrect behavior.

Bug Report

It is possible to specify the default value for a generic self using __init__ parameter type hint or __new__ return type hint. However, when a @dataclass decorator is applied to the class and when it generates __init__, the __new__ definition gets erased.

To Reproduce

https://mypy-play.net/?mypy=latest&python=3.11&gist=13b4e50085788299b532d56cf70c3eb8

Expected Behavior
Mypy must show:

main.py:22: note: Revealed type is "__main__.User[Literal['NotLoggedIn']]"

And it does show it if the @dataclass gets commented out, init=False is passed to the dataclass, or if the following __init__ is added to the class:

def __init__(self: "User[Literal['NotLoggedIn']]", name: str) -> None:
    self.name = name

Actual Behavior

main.py:21: error: Need type annotation for "f"  [var-annotated]
main.py:22: note: Revealed type is "__main__.User[Any]"

Your Environment

  • Mypy version used: 0.991
  • Python version used: 3.11

Relevant links

#4236

Activity

erictraut

erictraut commented on Jan 3, 2023

@erictraut

FWIW, I closed this issue in the pyright issue tracker because I don't consider this a bug in pyright. The mechanism by which a constructor's specialized type is influenced by an annotated self parameter within an __init__ mechanism has never been specified or documented. If this is something that we want to officially support in the Python type system, someone should write a PEP, and we should specify and discuss how it should work in detail. Until then, I consider this a narrowly-scoped hack that works around a very specific issue.

zmievsa

zmievsa commented on Jan 3, 2023

@zmievsa
Author

@erictraut do I understand correctly that your work with @JelleZijlstra on PEP 696 is supposed to solve this issue in its entirety?

erictraut

erictraut commented on Jan 3, 2023

@erictraut

@Ovsyanka83, no, this issue has nothing to do with PEP 696 (or PEP 695).

zmievsa

zmievsa commented on Jan 3, 2023

@zmievsa
Author

@erictraut I decided to go back and check whether I'll be able to use typevardefaults with the latest pyright and succeeded.

And it does solve my problem in its entirety. Take a look at the modified code that reveals the expected type:

from dataclasses import dataclass
from typing import Generic, TypeVar, cast

from typing_extensions import TYPE_CHECKING, Literal

if TYPE_CHECKING:
    T = TypeVar("T", bound=Literal["LoggedIn", "NotLoggedIn"], default=Literal["NotLoggedIn"])


@dataclass
class User(Generic[T]):
    name: str

    def log_in(self) -> "User[Literal['LoggedIn']]":
        return cast(User[Literal["LoggedIn"]], self)

    def scream(self: "User[Literal['LoggedIn']]") -> None:
        print(f"{self.name} screams!")


f = User("admin")
reveal_type(f)

I apologize for disturbing you and I am really grateful for your work on PEP 696 in pyright.

JelleZijlstra

JelleZijlstra commented on Jan 3, 2023

@JelleZijlstra
Member

(Also, to be clear, PEP 696 is @Gobot1234's work, not mine; I am just the PEP sponsor.)

hauntsaninja

hauntsaninja commented on Jan 3, 2023

@hauntsaninja
Collaborator

Closing in favour of whatever issue is tracking 696

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrong

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @JelleZijlstra@erictraut@hauntsaninja@zmievsa

        Issue actions

          Default value for Generic self is erased by @dataclass · Issue #14382 · python/mypy