From cbf9486e7f413b18380bd9700f7fc80c5842bdba Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 27 Nov 2019 12:57:57 +0000 Subject: [PATCH 1/2] Make logic around bind_self() consistent in different code paths --- mypy/checkmember.py | 7 +-- test-data/unit/check-generics.test | 12 ++--- test-data/unit/check-selftype.test | 78 +++++++++++++++++++++++++++++- 3 files changed, 87 insertions(+), 10 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 71bdd3fafc40..20013d389f09 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -829,9 +829,7 @@ class B(A[str]): pass variables of the class callable on which the method was accessed. """ # TODO: verify consistency between Q and T - if is_classmethod: - assert isuper is not None - t = get_proper_type(expand_type_by_instance(t, isuper)) + # We add class type variables if the class method is accessed on class object # without applied type arguments, this matches the behavior of __init__(). # For example (continuing the example in docstring): @@ -847,7 +845,10 @@ class B(A[str]): pass if isinstance(t, CallableType): tvars = original_vars if original_vars is not None else [] if is_classmethod: + t = freshen_function_type_vars(t) t = bind_self(t, original_type, is_classmethod=True) + assert isuper is not None + t = get_proper_type(expand_type_by_instance(t, isuper)) return t.copy_modified(variables=tvars + t.variables) elif isinstance(t, Overloaded): return Overloaded([cast(CallableType, add_class_tvars(item, itype, isuper, is_classmethod, diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index b795a36aaa4f..846c2ebf99a6 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -2062,7 +2062,7 @@ class Base(Generic[T]): return (cls(item),) return cls(item) -reveal_type(Base.make_some) # N: Revealed type is 'Overload(def [T] (item: T`1) -> __main__.Base*[T`1], def [T] (item: T`1, n: builtins.int) -> builtins.tuple[__main__.Base*[T`1]])' +reveal_type(Base.make_some) # N: Revealed type is 'Overload(def [T] (item: T`1) -> __main__.Base[T`1], def [T] (item: T`1, n: builtins.int) -> builtins.tuple[__main__.Base[T`1]])' reveal_type(Base.make_some(1)) # N: Revealed type is '__main__.Base[builtins.int*]' reveal_type(Base.make_some(1, 1)) # N: Revealed type is 'builtins.tuple[__main__.Base[builtins.int*]]' @@ -2100,11 +2100,11 @@ class A(Generic[T]): class B(A[T], Generic[T, S]): def meth(self) -> None: - reveal_type(A[T].foo) # N: Revealed type is 'def () -> Tuple[T`1, __main__.A*[T`1]]' + reveal_type(A[T].foo) # N: Revealed type is 'def () -> Tuple[T`1, __main__.A[T`1]]' @classmethod def other(cls) -> None: - reveal_type(cls.foo) # N: Revealed type is 'def () -> Tuple[T`1, __main__.B*[T`1, S`2]]' -reveal_type(B.foo) # N: Revealed type is 'def [T, S] () -> Tuple[T`1, __main__.B*[T`1, S`2]]' + reveal_type(cls.foo) # N: Revealed type is 'def () -> Tuple[T`1, __main__.B[T`1, S`2]]' +reveal_type(B.foo) # N: Revealed type is 'def [T, S] () -> Tuple[T`1, __main__.B[T`1, S`2]]' [builtins fixtures/classmethod.pyi] [case testGenericClassAlternativeConstructorPrecise] @@ -2171,8 +2171,8 @@ class C(Generic[T]): class D(C[str]): ... -reveal_type(D.get()) # N: Revealed type is 'builtins.str' -reveal_type(D.get(42)) # N: Revealed type is 'builtins.tuple[builtins.str]' +reveal_type(D.get()) # N: Revealed type is 'builtins.str*' +reveal_type(D.get(42)) # N: Revealed type is 'builtins.tuple[builtins.str*]' [builtins fixtures/classmethod.pyi] [case testGenericClassMethodAnnotation] diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index f4483edec9bb..d99ad2282735 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -959,7 +959,7 @@ class A(Generic[T]): t: Type[Union[A[int], A[str]]] x = t.meth() -reveal_type(x) # N: Revealed type is 'Union[__main__.A*[builtins.int], __main__.A*[builtins.str]]' +reveal_type(x) # N: Revealed type is 'Union[__main__.A[builtins.int], __main__.A[builtins.str]]' [builtins fixtures/classmethod.pyi] [case testSelfTypeClassMethodOnUnionList] @@ -1055,3 +1055,79 @@ class Concrete(Blah): def something(self) -> None: ... Concrete() # OK + +[case testSelfTypeGenericClassNoClashInstanceMethod] +from typing import TypeVar, Generic + +M = TypeVar("M") +T = TypeVar("T") +S = TypeVar("S") + +class Descriptor(Generic[M]): ... + +class BaseWrapper(Generic[M]): + def create_wrapper(self: T, metric_descriptor: Descriptor[M]) -> T: ... +class SubWrapper(BaseWrapper[M]): ... + +def build_wrapper(descriptor: Descriptor[M]) -> BaseWrapper[M]: + wrapper: BaseWrapper[M] + return wrapper.create_wrapper(descriptor) + +def build_sub_wrapper(descriptor: Descriptor[S]) -> SubWrapper[S]: + wrapper: SubWrapper[S] + x = wrapper.create_wrapper(descriptor) + reveal_type(x) # N: Revealed type is '__main__.SubWrapper[S`-1]' + return x + +[case testSelfTypeGenericClassNoClashClassMethod] +from typing import TypeVar, Generic, Type + +M = TypeVar("M") +T = TypeVar("T") +S = TypeVar("S") + +class Descriptor(Generic[M]): ... + +class BaseWrapper(Generic[M]): + @classmethod + def create_wrapper(cls: Type[T], metric_descriptor: Descriptor[M]) -> T: ... +class SubWrapper(BaseWrapper[M]): ... + +def build_wrapper(descriptor: Descriptor[M]) -> BaseWrapper[M]: + wrapper_cls: Type[BaseWrapper[M]] + return wrapper_cls.create_wrapper(descriptor) + +def build_sub_wrapper(descriptor: Descriptor[S]) -> SubWrapper[S]: + wrapper_cls: Type[SubWrapper[S]] + x = wrapper_cls.create_wrapper(descriptor) + reveal_type(x) # N: Revealed type is '__main__.SubWrapper[S`-1]' + return x +[builtins fixtures/classmethod.pyi] + +[case testSelfTypeGenericClassNoClashClassMethodClassObject] +from typing import TypeVar, Generic, Type + +M = TypeVar("M") +T = TypeVar("T") + +class Descriptor(Generic[M]): ... + +class BaseWrapper(Generic[M]): + @classmethod + def create_wrapper(cls: Type[T], metric_descriptor: Descriptor[M]) -> T: ... +class SubWrapper(BaseWrapper[M]): ... + +def build_wrapper(descriptor: Descriptor[M]) -> BaseWrapper[M]: + return BaseWrapper.create_wrapper(descriptor) + +def build_sub_wrapper(descriptor: Descriptor[M]) -> SubWrapper[M]: + x = SubWrapper.create_wrapper(descriptor) + reveal_type(x) # N: Revealed type is '__main__.SubWrapper[M`-1]' + return x + +def build_wrapper_non_gen(descriptor: Descriptor[int]) -> BaseWrapper[str]: + return BaseWrapper.create_wrapper(descriptor) # E: Argument 1 to "create_wrapper" of "BaseWrapper" has incompatible type "Descriptor[int]"; expected "Descriptor[str]" + +def build_sub_wrapper_non_gen(descriptor: Descriptor[int]) -> SubWrapper[str]: + return SubWrapper.create_wrapper(descriptor) # E: Argument 1 to "create_wrapper" of "BaseWrapper" has incompatible type "Descriptor[int]"; expected "Descriptor[str]" +[builtins fixtures/classmethod.pyi] From c8044c7d47fd0919dba0006efda7ecbf9e896976 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 27 Nov 2019 14:02:27 +0000 Subject: [PATCH 2/2] Fix self-check --- mypy/checkmember.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 20013d389f09..7d82fd89e737 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -848,7 +848,7 @@ class B(A[str]): pass t = freshen_function_type_vars(t) t = bind_self(t, original_type, is_classmethod=True) assert isuper is not None - t = get_proper_type(expand_type_by_instance(t, isuper)) + t = cast(CallableType, expand_type_by_instance(t, isuper)) return t.copy_modified(variables=tvars + t.variables) elif isinstance(t, Overloaded): return Overloaded([cast(CallableType, add_class_tvars(item, itype, isuper, is_classmethod, 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