Skip to content

Commit f136e29

Browse files
authored
Ignore invalid self types in constructor (python#7946)
This previously caused small troubles in Dropbox internal codebase C (because of weird annotations). For example if an `__init__()` of class `C` had its `self` argument annotated with one of its superclasses `B`, this made all calls to `C()` inferred as `B`, causing spurious errors. This PR also makes explicit types in `__new__()` and `__init__()` treated in a more similar way.
1 parent 648d99a commit f136e29

File tree

2 files changed

+53
-9
lines changed

2 files changed

+53
-9
lines changed

mypy/typeops.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def type_object_type_from_function(signature: FunctionLike,
7474
# see comment in class_callable below). This is mostly useful for annotating library
7575
# classes such as subprocess.Popen.
7676
default_self = fill_typevars(info)
77-
if not is_new and def_info == info and not info.is_newtype:
77+
if not is_new and not info.is_newtype:
7878
orig_self_types = [(it.arg_types[0] if it.arg_types and it.arg_types[0] != default_self
7979
and it.arg_kinds[0] == ARG_POS else None) for it in signature.items()]
8080
else:
@@ -122,16 +122,14 @@ def class_callable(init_type: CallableType, info: TypeInfo, type_type: Instance,
122122
init_ret_type = get_proper_type(init_type.ret_type)
123123
orig_self_type = get_proper_type(orig_self_type)
124124
default_ret_type = fill_typevars(info)
125+
explicit_type = init_ret_type if is_new else orig_self_type
125126
if (
126-
is_new
127-
and isinstance(init_ret_type, (Instance, TupleType))
128-
# Only use the return type from __new__ if it is actually returning
129-
# a subtype of what we would return otherwise.
130-
and is_subtype(init_ret_type, default_ret_type, ignore_type_params=True)
127+
isinstance(explicit_type, (Instance, TupleType))
128+
# Only use the declared return type from __new__ or declared self in __init__
129+
# if it is actually returning a subtype of what we would return otherwise.
130+
and is_subtype(explicit_type, default_ret_type, ignore_type_params=True)
131131
):
132-
ret_type = init_ret_type # type: Type
133-
elif isinstance(orig_self_type, (Instance, TupleType)):
134-
ret_type = orig_self_type
132+
ret_type = explicit_type # type: Type
135133
else:
136134
ret_type = default_ret_type
137135

test-data/unit/check-selftype.test

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,3 +1009,49 @@ def bar(x: AClass) -> None:
10091009
reveal_type(x.delete) # N: Revealed type is 'Overload(def (id: builtins.int, id2: builtins.int) -> builtins.int, def (id: __main__.AClass*, id2: None =) -> builtins.int)'
10101010
x.delete(10, 20)
10111011
[builtins fixtures/classmethod.pyi]
1012+
1013+
[case testSelfTypeBadTypeIgnoredInConstructor]
1014+
class Base: ...
1015+
class Sub(Base):
1016+
def __init__(self: Base) -> None: ...
1017+
1018+
reveal_type(Sub()) # N: Revealed type is '__main__.Sub'
1019+
1020+
[case testSelfTypeBadTypeIgnoredInConstructorGeneric]
1021+
from typing import Generic, TypeVar
1022+
1023+
T = TypeVar('T')
1024+
1025+
class Base(Generic[T]): ...
1026+
class Sub(Base[T]):
1027+
def __init__(self: Base[T], item: T) -> None: ...
1028+
1029+
reveal_type(Sub(42)) # N: Revealed type is '__main__.Sub[builtins.int*]'
1030+
1031+
[case testSelfTypeBadTypeIgnoredInConstructorOverload]
1032+
from typing import overload
1033+
1034+
class Base: ...
1035+
class Sub(Base):
1036+
@overload
1037+
def __init__(self: Sub, item: int) -> None: ...
1038+
@overload
1039+
def __init__(self: Base) -> None: ...
1040+
def __init__(self, item=None):
1041+
...
1042+
1043+
reveal_type(Sub) # N: Revealed type is 'Overload(def (item: builtins.int) -> __main__.Sub, def () -> __main__.Sub)'
1044+
1045+
[case testSelfTypeBadTypeIgnoredInConstructorAbstract]
1046+
from abc import abstractmethod
1047+
from typing import Protocol
1048+
1049+
class Blah(Protocol):
1050+
@abstractmethod
1051+
def something(self) -> None: ...
1052+
1053+
class Concrete(Blah):
1054+
def __init__(self: Blah) -> None: ...
1055+
def something(self) -> None: ...
1056+
1057+
Concrete() # OK

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy