8000 Don't instantiate types in `__init__` based on `self` by A5rocks · Pull Request #16753 · python/mypy · GitHub
[go: up one dir, main page]

Skip to content

Don't instantiate types in __init__ based on self #16753

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

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions mypy/typeops.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,8 @@ class B(A): pass
# this special-casing looks not very principled, there is nothing meaningful we can infer
# from such definition, since it is inherently indefinitely recursive.
allow_callable = func.name is None or not func.name.startswith("__call__ of")
is_init = func.name is not None and func.name.startswith("__init__ of")

if func.variables and supported_self_type(self_param_type, allow_callable=allow_callable):
from mypy.infer import infer_type_arguments

Expand Down Expand Up @@ -338,9 +340,17 @@ class B(A): pass

# Update the method signature with the solutions found.
# Technically, some constraints might be unsolvable, make them Never.
to_apply = [t if t is not None else UninhabitedType() for t in typeargs]
func = expand_type(func, {tv.id: arg for tv, arg in zip(self_vars, to_apply)})
variables = [v for v in func.variables if v not in self_vars]
# (... unless this is `__init__`, because we're going to be returning
# the self-type anyways.)
to_apply = [
(tv, t) if t is not None else (tv, UninhabitedType())
for tv, t in zip(self_vars, typeargs)
if not is_init
or (t is not None and not isinstance(get_proper_type(t), UninhabitedType))
]
func = expand_type(func, {tv.id: arg for tv, arg in to_apply})
applied_vars = {tv for tv, _ in to_apply}
variables = [v for v in func.variables if v not in applied_vars]
else:
variables = func.variables

Expand Down
50 changes: 40 additions & 10 deletions test-data/unit/check-selftype.test
5440
Original file line number Diff line number Diff line change
Expand Up @@ -272,18 +272,18 @@ from typing import Any, Generic, Self, TypeVar, overload
T_co = TypeVar('T_co', covariant=True)

class Config(Generic[T_co]):
@overload
def get(self, instance: None) -> Self: ...
@overload
def get(self, instance: Any) -> T_co: ...
def get(self, *a, **kw): ...
@overload
def get(self, instance: None) -> Self: ...
@overload
def get(self, instance: Any) -> T_co: ...
def get(self, *a, **kw): ...

class MultiConfig(Config[T_co]):
@overload
def get(self, instance: None) -> Self: ...
@overload
def get(self, instance: Any) -> T_co: ...
def get(self, *a, **kw): ...
@overload
def get(self, instance: None) -> Self: ...
@overload
def get(self, instance: Any) -> T_co: ...
def get(self, *a, **kw): ...
[builtins fixtures/dict.pyi]

[case testSelfTypeSuper]
Expand Down Expand Up @@ -2071,3 +2071,33 @@ p: Partial
reveal_type(p()) # N: Revealed type is "Never"
p2: Partial2
reveal_type(p2(42)) # N: Revealed type is "builtins.int"

[case testSelfTypeInInit]
from typing import Generic, TypeVar, overload, Type

class A:
...

class B(A):
...

E = TypeVar("E", bound=A)
E2 = TypeVar("E2", bound=A)

class H(A, Generic[E]):
...

class RaisesGroup(Generic[E]):
@overload
def __init__(self, a: Type[E]):
...

@overload
def __init__(self: "RaisesGroup[H[E2]]", a: "RaisesGroup[E2]"):
...

def __init__(self, a: object):
...


reveal_type(RaisesGroup(RaisesGroup(B))) # N: Revealed type is "__main__.RaisesGroup[__main__.H[__main__.B]]"
0