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

Incorrect returned type of access descriptors on unions of types #16603

Closed
md384 opened this issue Dec 1, 2023 · 1 comment · Fixed by #16604
Closed

Incorrect returned type of access descriptors on unions of types #16603

md384 opened this issue Dec 1, 2023 · 1 comment · Fixed by #16604
Labels
bug mypy got something wrong

Comments

@md384
Copy link
Contributor

md384 commented Dec 1, 2023

Bug Report

Mypy will return the incorrect type for access descriptors on unions of types. It returns the types of the attributes on the instance of the type rather than the type itself.

See sqlalchemy/sqlalchemy#9419 for a concrete example with sqlalchemy.

To Reproduce

# Ideally, a small sample program that demonstrates the problem.
# Or even better, a reproducible playground link https://mypy-play.net/ (use the "Gist" button)

from typing import overload, Generic, Any, TypeVar, List, Optional, Union, Type

_T_co = TypeVar("_T_co", bound=Any, covariant=True)

class Mapped(Generic[_T_co]):
    def __init__(self, value: _T_co):
        self.value = value

    @overload
    def __get__(
        self, instance: None, owner: Any
    ) -> List[_T_co]:
        ...

    @overload
    def __get__(self, instance: object, owner: Any) -> _T_co:
        ...

    def __get__(
        self, instance: Optional[object], owner: Any
    ) -> Union[List[_T_co], _T_co]:
        return self.value

class A:
    field_1: Mapped[int] = Mapped(1)
    field_2: Mapped[str] = Mapped('1')

class B:
    field_1: Mapped[int] = Mapped(2)
    field_2: Mapped[str] = Mapped('2')

Expected Behavior

mix: Union[Type[A], Type[B]] = A
reveal_type(mix)  # N: Revealed type is "Union[Type[__main__.A], Type[__main__.B]]"
reveal_type(mix.field_1)  # N: Revealed type is "builtins.list[builtins.int]"
reveal_type(mix().field_1)  # N: Revealed type is "builtins.int"

Actual Behavior

mix: Union[Type[A], Type[B]] = A
reveal_type(mix)  # N: Revealed type is "Union[Type[__main__.A], Type[__main__.B]]"
reveal_type(mix.field_1)  # N: Revealed type is "builtins.int"
reveal_type(mix().field_1)  # N: Revealed type is "builtins.int"

reveal_type(mix.field_1) is incorrectly builtins.int, should be builtins.list[builtins.int].

Your Environment

  • Mypy version used: master (1.7)
  • Mypy command-line flags: None
  • Mypy configuration options from mypy.ini (and other config files): None
  • Python version used: 3.11
@md384
Copy link
Contributor Author

md384 commented Dec 1, 2023

This relates to #5570 - it fixes some of the examples but not the initial example in the bug report (although now gives a different error), so this report is for a different (but similar) bug.

hauntsaninja pushed a commit that referenced this issue Apr 16, 2024
…#16604)

Fixes #16603

This change maps over union types when determining the types of access
descriptors. Previously, the because [this
conditional](https://github.com/md384/mypy/blob/c2a55afcef32ecb11a4c76c4c79539f6ba36d55c/mypy/checkmember.py#L697-L701)
would fall through to the `else` case because instance type was not a
singular `TypeType` (it was a Union), so we'd end up with an instance
value being passed to `__get__` instead of `None`.
svalentin pushed a commit that referenced this issue Apr 16, 2024
…#16604)

Fixes #16603

This change maps over union types when determining the types of access
descriptors. Previously, the because [this
conditional](https://github.com/md384/mypy/blob/c2a55afcef32ecb11a4c76c4c79539f6ba36d55c/mypy/checkmember.py#L697-L701)
would fall through to the `else` case because instance type was not a
singular `TypeType` (it was a Union), so we'd end up with an instance
value being passed to `__get__` instead of `None`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant