Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitecfab6a

Browse files
authored
[PEP 695] Allow Self return types with contravariance (#17786)
Fix variance inference in this fragment from a typing conformance test:```class ClassA[T1, T2, T3](list[T1]): def method1(self, a: T2) -> None: ... def method2(self) -> T3: ...```Previously T2 was incorrectly inferred as invariant due to `list` havingmethods that return `Self`. Be more flexible with return types to allowinferring contravariance for type variables even if there are `Self`return types, in particular.We could probably make this even more lenient, but after thinking aboutthis for a while, I wasn't sure what the most general rule would be, soI decided to just make a tweak to support the likely most common usecase (which is probably actually not that common either).Link to conformance test:https://github.com/python/typing/blob/main/conformance/tests/generics_variance_inference.py#L15C1-L20C12
1 parent9518b6a commitecfab6a

File tree

3 files changed

+104
-1
lines changed

3 files changed

+104
-1
lines changed

‎mypy/subtypes.py‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2024,6 +2024,16 @@ def infer_variance(info: TypeInfo, i: int) -> bool:
20242024

20252025
typ=find_member(member,self_type,self_type)
20262026
iftyp:
2027+
# It's okay for a method in a generic class with a contravariant type
2028+
# variable to return a generic instance of the class, if it doesn't involve
2029+
# variance (i.e. values of type variables are propagated). Our normal rules
2030+
# would disallow this. Replace such return types with 'Any' to allow this.
2031+
#
2032+
# This could probably be more lenient (e.g. allow self type be nested, don't
2033+
# require all type arguments to be identical to self_type), but this will
2034+
# hopefully cover the vast majority of such cases, including Self.
2035+
typ=erase_return_self_types(typ,self_type)
2036+
20272037
typ2=expand_type(typ, {tvar.id:object_type})
20282038
ifnotis_subtype(typ,typ2):
20292039
co=False
@@ -2066,3 +2076,20 @@ def infer_class_variances(info: TypeInfo) -> bool:
20662076
ifnotinfer_variance(info,i):
20672077
success=False
20682078
returnsuccess
2079+
2080+
2081+
deferase_return_self_types(typ:Type,self_type:Instance)->Type:
2082+
"""If a typ is function-like and returns self_type, replace return type with Any."""
2083+
proper_type=get_proper_type(typ)
2084+
ifisinstance(proper_type,CallableType):
2085+
ret=get_proper_type(proper_type.ret_type)
2086+
ifisinstance(ret,Instance)andret==self_type:
2087+
returnproper_type.copy_modified(ret_type=AnyType(TypeOfAny.implementation_artifact))
2088+
elifisinstance(proper_type,Overloaded):
2089+
returnOverloaded(
2090+
[
2091+
cast(CallableType,erase_return_self_types(it,self_type))
2092+
foritinproper_type.items
2093+
]
2094+
)
2095+
returntyp

‎test-data/unit/check-python312.test‎

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ b: Invariant[int]
213213
if int():
214214
a = b # E: Incompatible types in assignment (expression has type "Invariant[int]", variable has type "Invariant[object]")
215215
if int():
216-
b = a # E: Incompatible types in assignment (expression has type "Invariant[object]", variable has type "Invariant[int]")
216+
b = a
217217

218218
c: Covariant[object]
219219
d: Covariant[int]
@@ -393,6 +393,62 @@ inv3_1: Invariant3[float] = Invariant3[int](1) # E: Incompatible types in assig
393393
inv3_2: Invariant3[int] = Invariant3[float](1) # E: Incompatible types in assignment (expression has type "Invariant3[float]", variable has type "Invariant3[int]")
394394
[builtins fixtures/property.pyi]
395395

396+
[case testPEP695InferVarianceWithInheritedSelf]
397+
from typing import overload, Self, TypeVar, Generic
398+
399+
T = TypeVar("T")
400+
S = TypeVar("S")
401+
402+
class C(Generic[T]):
403+
def f(self, x: T) -> Self: ...
404+
def g(self) -> T: ...
405+
406+
class D[T1, T2](C[T1]):
407+
def m(self, x: T2) -> None: ...
408+
409+
a1: D[int, int] = D[int, object]()
410+
a2: D[int, object] = D[int, int]() # E: Incompatible types in assignment (expression has type "D[int, int]", variable has type "D[int, object]")
411+
a3: D[int, int] = D[object, object]() # E: Incompatible types in assignment (expression has type "D[object, object]", variable has type "D[int, int]")
412+
a4: D[object, int] = D[int, object]() # E: Incompatible types in assignment (expression has type "D[int, object]", variable has type "D[object, int]")
413+
414+
[case testPEP695InferVarianceWithReturnSelf]
415+
from typing import Self, overload
416+
417+
class Cov[T]:
418+
def f(self) -> Self: ...
419+
420+
a1: Cov[int] = Cov[float]() # E: Incompatible types in assignment (expression has type "Cov[float]", variable has type "Cov[int]")
421+
a2: Cov[float] = Cov[int]()
422+
423+
class Contra[T]:
424+
def f(self) -> Self: ...
425+
def g(self, x: T) -> None: ...
426+
427+
b1: Contra[int] = Contra[float]()
428+
b2: Contra[float] = Contra[int]() # E: Incompatible types in assignment (expression has type "Contra[int]", variable has type "Contra[float]")
429+
430+
class Cov2[T]:
431+
@overload
432+
def f(self, x): ...
433+
@overload
434+
def f(self) -> Self: ...
435+
def f(self, x=None): ...
436+
437+
c1: Cov2[int] = Cov2[float]() # E: Incompatible types in assignment (expression has type "Cov2[float]", variable has type "Cov2[int]")
438+
c2: Cov2[float] = Cov2[int]()
439+
440+
class Contra2[T]:
441+
@overload
442+
def f(self, x): ...
443+
@overload
444+
def f(self) -> Self: ...
445+
def f(self, x=None): ...
446+
447+
def g(self, x: T) -> None: ...
448+
449+
d1: Contra2[int] = Contra2[float]()
450+
d2: Contra2[float] = Contra2[int]() # E: Incompatible types in assignment (expression has type "Contra2[int]", variable has type "Contra2[float]")
451+
396452
[case testPEP695InheritInvariant]
397453
class Invariant[T]:
398454
x: T

‎test-data/unit/pythoneval.test‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2196,3 +2196,23 @@ type K4 = None | B[int]
21962196

21972197
type L1 = Never
21982198
type L2 = list[Never]
2199+
2200+
[case testPEP695VarianceInferenceSpecialCaseWithTypeshed]
2201+
# flags: --python-version=3.12
2202+
class C1[T1, T2](list[T1]):
2203+
def m(self, a: T2) -> None: ...
2204+
2205+
def func1(p: C1[int, object]):
2206+
x: C1[int, int] = p
2207+
2208+
class C2[T1, T2, T3](dict[T2, T3]):
2209+
def m(self, a: T1) -> None: ...
2210+
2211+
def func2(p: C2[object, int, int]):
2212+
x: C2[int, int, int] = p
2213+
2214+
class C3[T1, T2](tuple[T1, ...]):
2215+
def m(self, a: T2) -> None: ...
2216+
2217+
def func3(p: C3[int, object]):
2218+
x: C3[int, int] = p

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp