Skip to content

Class __get__ method is not working for generic specialization overload cases  #15921

Closed

Description

@uriyyo

Bug Report

Class __get__ method is not working for generic specialization overload cases

To Reproduce

from __future__ import annotations

from typing import Sequence, Generic, TypeVar, TYPE_CHECKING, Any, overload, Mapping, Type, Optional

from typing_extensions import reveal_type

T_co = TypeVar("T_co", covariant=True)
TSequence_co = TypeVar("TSequence_co", bound=Sequence[Any], covariant=True)


class Field(Generic[T_co]):
    type: Optional[Type[T_co]]

    if TYPE_CHECKING:
        @overload
        def __get__(self: Field[bool], instance: None, owner: Any) -> BoolField:
            pass

        @overload
        def __get__(self: Field[int], instance: None, owner: Any) -> NumField:
            pass

        @overload
        def __get__(self: Field[str], instance: None, owner: Any) -> StrField:
            pass

        @overload
        def __get__(self: Field[TSequence_co], instance: None, owner: Any) -> SeqField[TSequence_co]:
            pass

        @overload
        def __get__(self: Field[Any], instance: None, owner: Any) -> AnyField[T_co]:
            pass

        @overload
        def __get__(self, instance: Any, owner: Any) -> T_co:
            pass

        def __get__(self, instance: Any, owner: Any) -> Any:
            pass


class BoolField(Field[bool]):
    pass


class NumField(Field[int]):
    pass


class StrField(Field[str]):
    pass


class SeqField(Field[TSequence_co]):
    pass


class AnyField(Field[T_co]):
    pass


class Fields:
    bool_f: Field[bool]
    cmp_f: Field[int]
    str_f: Field[str]
    seq_f: Field[Sequence[int]]
    any_f: Field[Mapping[int, int]]


reveal_type(Fields.bool_f)
reveal_type(Fields.cmp_f)
reveal_type(Fields.str_f)
reveal_type(Fields.seq_f)
reveal_type(Fields.any_f)

Expected Behavior

reveal_type show correct types based on a __get__ overloads.

pyright produce correct output:

temp.py
  temp.py:71:13 - information: Type of "Fields.bool_f" is "BoolField"
  temp.py:72:13 - information: Type of "Fields.cmp_f" is "NumField"
  temp.py:73:13 - information: Type of "Fields.str_f" is "StrField"
  temp.py:74:13 - information: Type of "Fields.seq_f" is "SeqField[Sequence[int]]"
  temp.py:75:13 - information: Type of "Fields.any_f" is "AnyField[Mapping[int, int]]"
0 errors, 0 warnings, 5 informations 

Actual Behavior

mypy resolves all types as BoolField:

temp.py:71:13: note: Revealed type is "temp.BoolField"
temp.py:72:13: note: Revealed type is "temp.BoolField"
temp.py:73:13: note: Revealed type is "temp.BoolField"
temp.py:74:13: note: Revealed type is "temp.BoolField"
temp.py:75:13: note: Revealed type is "temp.BoolField"
Success: no issues found in 1 source file

Your Environment

  • Mypy version used: 1.5.1
  • Mypy command-line flags: -
  • Mypy configuration options from mypy.ini (and other config files): -
  • Python version used: python 3.8.17

Activity

  1. ilevkivskyi commented on Feb 11, 2025

    @ilevkivskyi
    Member

    This now works as expected, except Fields.cmp_f is mapped to BoolField. This looks like a more subtle issue, not sure why this happens.

  2. Dr-Irv commented on Mar 11, 2025

    @Dr-Irv

    Here's a related example:

    from __future__ import annotations
    from typing import Any, Generic, TypeVar, overload
    
    T = TypeVar("T", int, str)
    
    
    class _PropMethod:
        @overload
        def __get__(self, instance: Foo[int], owner: Any) -> int: ...
        @overload
        def __get__(self, instance: Foo[str], owner: Any) -> str: ...
        @overload
        def __get__(self, instance: Foo, owner: Any) -> list: ...
        def __get__(self, instance: Foo, owner: Any) -> int | str | list:
            if instance._type == int:
                return 4
            elif instance._type == str:
                return "abc"
            else:
                return [1, 2, 3]
    
    
    class Foo(Generic[T]):
        def __init__(self, x: int | str | list) -> None:
            self._type = type(x)
    
        theprop: _PropMethod
    
    
    def outer(f: Foo[Any]):
        reveal_type(f.theprop)

    mypy 1.5.0 reports: genericany.py:32: note: Revealed type is "Any"
    pyright 1.1.396 reports: genericany.py:31:17 - information: Type of "f.theprop" is "int" (which is what I would expect)

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

      Issue actions