-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Make overloads support classmethod and staticmethod #5224
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
Changes from 1 commit
61f7f2a
4947e9f
f0a12e9
81dfa69
bfaba34
563748a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
This commit modifies mypy to use the `is_static` and `is_class` fields of OverloadedFuncDef as appropriate. I found the code snippets to modify by asking PyCharm for all instances of code using those two fields and modified the surrounding code as appropriate.
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1290,8 +1290,8 @@ def check_override(self, override: FunctionLike, original: FunctionLike, | |
fail = True | ||
|
||
if isinstance(original, CallableType) and isinstance(override, CallableType): | ||
if (isinstance(original.definition, FuncItem) and | ||
isinstance(override.definition, FuncItem)): | ||
if (isinstance(original.definition, FuncBase) and | ||
isinstance(override.definition, FuncBase)): | ||
if ((original.definition.is_static or original.definition.is_class) and | ||
not (override.definition.is_static or override.definition.is_class)): | ||
fail = True | ||
|
@@ -3923,8 +3923,6 @@ def is_untyped_decorator(typ: Optional[Type]) -> bool: | |
def is_static(func: Union[FuncBase, Decorator]) -> bool: | ||
if isinstance(func, Decorator): | ||
return is_static(func.func) | ||
elif isinstance(func, OverloadedFuncDef): | ||
return any(is_static(item) for item in func.items) | ||
elif isinstance(func, FuncItem): | ||
elif isinstance(func, FuncBase): | ||
return func.is_static | ||
return False | ||
raise AssertionError("Unexpected func type: {}".format(type(func))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you please reformat this as |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2937,3 +2937,322 @@ def add_proxy(x, y): | |
tup = (1, '2') | ||
reveal_type(foo(lambda (x, y): add_proxy(x, y), tup)) # E: Revealed type is 'builtins.str*' | ||
[builtins fixtures/primitives.pyi] | ||
|
||
[case testOverloadWithClassMethods] | ||
from typing import overload | ||
|
||
class Wrapper: | ||
@overload | ||
@classmethod | ||
def foo(cls, x: int) -> int: ... | ||
@overload | ||
@classmethod | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One more suggestion: could you please add to your TODO list adding few tests to check that overloads work well with self-types (both instance, and class methods using |
||
def foo(cls, x: str) -> str: ... | ||
@classmethod | ||
def foo(cls, x): pass | ||
|
||
reveal_type(Wrapper.foo(3)) # E: Revealed type is 'builtins.int' | ||
reveal_type(Wrapper.foo("foo")) # E: Revealed type is 'builtins.str' | ||
|
||
[builtins fixtures/classmethod.pyi] | ||
|
||
[case testOverloadWithInconsistentClassMethods] | ||
from typing import overload | ||
|
||
class Wrapper1: | ||
@overload # E: Overload does not consistently use the "@classmethod" decorator on all function signatures. | ||
@classmethod | ||
def foo(cls, x: int) -> int: ... | ||
@overload | ||
@classmethod | ||
def foo(cls, x: str) -> str: ... | ||
def foo(cls, x): pass | ||
|
||
class Wrapper2: | ||
@overload # E: Overload does not consistently use the "@classmethod" decorator on all function signatures. | ||
@classmethod | ||
def foo(cls, x: int) -> int: ... | ||
@overload | ||
def foo(cls, x: str) -> str: ... | ||
@classmethod | ||
def foo(cls, x): pass | ||
|
||
class Wrapper3: | ||
@overload # E: Overload does not consistently use the "@classmethod" decorator on all function signatures. | ||
def foo(cls, x: int) -> int: ... | ||
@overload | ||
def foo(cls, x: str) -> str: ... | ||
@classmethod | ||
def foo(cls, x): pass | ||
|
||
[builtins fixtures/classmethod.pyi] | ||
|
||
[case testOverloadWithSwappedDecorators] | ||
from typing import overload | ||
|
||
class Wrapper1: | ||
@classmethod | ||
@overload | ||
def foo(cls, x: int) -> int: ... | ||
|
||
@classmethod | ||
@overload | ||
def foo(cls, x: str) -> str: ... | ||
|
||
@classmethod | ||
def foo(cls, x): pass | ||
|
||
class Wrapper2: | ||
@classmethod | ||
@overload | ||
def foo(cls, x: int) -> int: ... | ||
|
||
@overload | ||
@classmethod | ||
def foo(cls, x: str) -> str: ... | ||
|
||
@classmethod | ||
def foo(cls, x): pass | ||
|
||
class Wrapper3: | ||
@classmethod # E: Overload does not consistently use the "@classmethod" decorator on all function signatures. | ||
@overload | ||
def foo(cls, x: int) -> int: ... | ||
|
||
@overload | ||
def foo(cls, x: str) -> str: ... | ||
|
||
def foo(cls, x): pass | ||
|
||
reveal_type(Wrapper1.foo(3)) # E: Revealed type is 'builtins.int' | ||
reveal_type(Wrapper2.foo(3)) # E: Revealed type is 'builtins.int' | ||
|
||
[builtins fixtures/classmethod.pyi] | ||
|
||
[case testOverloadFaultyClassMethodInheritance] | ||
from typing import overload | ||
|
||
class A: pass | ||
class B(A): pass | ||
class C(B): pass | ||
|
||
class Parent: | ||
@overload | ||
@classmethod | ||
def foo(cls, x: B) -> int: ... | ||
|
||
@overload | ||
@classmethod | ||
def foo(cls, x: str) -> str: ... | ||
|
||
@classmethod | ||
def foo(cls, x): pass | ||
|
||
class BadChild(Parent): | ||
@overload # E: Signature of "foo" incompatible with supertype "Parent" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe add few more tests to check overriding an overloaded instance method, with overloaded class methods and vice versa?
F438
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah whoops, I completely forgot to handle this case. Fixed. |
||
@classmethod | ||
def foo(cls, x: C) -> int: ... | ||
|
||
@overload | ||
@classmethod | ||
def foo(cls, x: str) -> str: ... | ||
|
||
@classmethod | ||
def foo(cls, x): pass | ||
|
||
class GoodChild(Parent): | ||
@overload | ||
@classmethod | ||
def foo(cls, x: A) -> int: ... | ||
|
||
@overload | ||
@classmethod | ||
def foo(cls, x: str) -> str: ... | ||
|
||
@classmethod | ||
def foo(cls, x): pass | ||
|
||
[builtins fixtures/classmethod.pyi] | ||
|
||
[case testOverloadClassMethodImplementation] | ||
from typing import overload, Union | ||
|
||
class Wrapper: | ||
@classmethod | ||
def other(cls) -> str: | ||
return "..." | ||
|
||
@overload | ||
@classmethod | ||
def foo(cls, x: int) -> int: ... | ||
|
||
@overload | ||
@classmethod | ||
def foo(cls, x: str) -> str: ... | ||
|
||
@classmethod # E: Overloaded function implementation cannot produce return type of signature 1 | ||
def foo(cls, x: Union[int, str]) -> str: | ||
reveal_type(cls) # E: Revealed type is 'def () -> __main__.Wrapper' | ||
reveal_type(cls.other()) # E: Revealed type is 'builtins.str' | ||
return "..." | ||
|
||
[builtins fixtures/classmethod.pyi] | ||
|
||
[case testOverloadWithStaticMethods] | ||
from typing import overload | ||
|
||
class Wrapper: | ||
@overload | ||
@staticmethod | ||
def foo(x: int) -> int: ... | ||
@overload | ||
@staticmethod | ||
def foo(x: str) -> str: ... | ||
@staticmethod | ||
def foo(x): pass | ||
|
||
reveal_type(Wrapper.foo(3)) # E: Revealed type is 'builtins.int' | ||
reveal_type(Wrapper.foo("foo")) # E: Revealed type is 'builtins.str' | ||
|
||
[builtins fixtures/staticmethod.pyi] | ||
|
||
[case testOverloadWithInconsistentStaticMethods] | ||
from typing import overload, Union | ||
|
||
class Wrapper1: | ||
@overload # E: Overload does not consistently use the "@staticmethod" decorator on all function signatures. | ||
@staticmethod | ||
def foo(x: int) -> int: ... | ||
@overload | ||
@staticmethod | ||
def foo(x: str) -> str: ... | ||
def foo(x): pass | ||
|
||
class Wrapper2: | ||
@overload # E: Overload does not consistently use the "@staticmethod" decorator on all function signatures. | ||
@staticmethod | ||
def foo(x: int) -> int: ... | ||
@overload | ||
def foo(x: str) -> str: ... # E: Self argument missing for a non-static method (or an invalid type for self) | ||
@staticmethod | ||
def foo(x): pass | ||
|
||
class Wrapper3: | ||
@overload # E: Overload does not consistently use the "@staticmethod" decorator on all function signatures. | ||
@staticmethod | ||
def foo(x: int) -> int: ... | ||
@overload | ||
@staticmethod | ||
def foo(x: str) -> str: ... | ||
def foo(x: Union[int, str]): pass # E: Self argument missing for a non-static method (or an invalid type for self) | ||
[builtins fixtures/staticmethod.pyi] | ||
|
||
[case testOverloadWithSwappedDecorators] | ||
from typing import overload | ||
|
||
class Wrapper1: | ||
@staticmethod | ||
@overload | ||
def foo(x: int) -> int: ... | ||
|
||
@staticmethod | ||
@overload | ||
def foo(x: str) -> str: ... | ||
|
||
@staticmethod | ||
def foo(x): pass | ||
|
||
class Wrapper2: | ||
@staticmethod | ||
@overload | ||
def foo(x: int) -> int: ... | ||
|
||
@overload | ||
@staticmethod | ||
def foo(x: str) -> str: ... | ||
|
||
@staticmethod | ||
def foo(x): pass | ||
|
||
class Wrapper3: | ||
@staticmethod # E: Overload does not consistently use the "@staticmethod" decorator on all function signatures. | ||
@overload | ||
def foo(x: int) -> int: ... | ||
|
||
@overload | ||
def foo(x: str) -> str: ... # E: Self argument missing for a non-static method (or an invalid type for self) | ||
|
||
@staticmethod | ||
def foo(x): pass | ||
|
||
reveal_type(Wrapper1.foo(3)) # E: Revealed type is 'builtins.int' | ||
reveal_type(Wrapper2.foo(3)) # E: Revealed type is 'builtins.int' | ||
|
||
[builtins fixtures/staticmethod.pyi] | ||
|
||
[case testOverloadFaultyStaticMethodInheritance] | ||
from typing import overload | ||
|
||
class A: pass | ||
class B(A): pass | ||
class C(B): pass | ||
|
||
class Parent: | ||
@overload | ||
@staticmethod | ||
def foo(x: B) -> int: ... | ||
|
||
@overload | ||
@staticmethod | ||
def foo(x: str) -> str: ... | ||
|
||
@staticmethod | ||
def foo(x): pass | ||
|
||
class BadChild(Parent): | ||
@overload # E: Signature of "foo" incompatible with supertype "Parent" | ||
@staticmethod | ||
def foo(x: C) -> int: ... | ||
|
||
@overload | ||
@staticmethod | ||
def foo(x: str) -> str: ... | ||
|
||
@staticmethod | ||
def foo(x): pass | ||
|
||
class GoodChild(Parent): | ||
@overload | ||
@staticmethod | ||
def foo(x: A) -> int: ... | ||
|
||
@overload | ||
@staticmethod | ||
def foo(x: str) -> str: ... | ||
|
||
@staticmethod | ||
def foo(x): pass | ||
|
||
[builtins fixtures/staticmethod.pyi] | ||
|
||
[case testOverloadStaticMethodImplementation] | ||
from typing import overload, Union | ||
|
||
class Wrapper: | ||
@staticmethod | ||
def other() -> str: | ||
return "..." | ||
|
||
@overload | ||
@staticmethod | ||
def foo(x: int) -> int: ... | ||
|
||
@overload | ||
@staticmethod | ||
def foo(x: str) -> str: ... | ||
|
||
@staticmethod # E: Overloaded function implementation cannot produce return type of signature 1 | ||
def foo(x: Union[int, str]) -> str: | ||
return 3 # E: Incompatible return value type (got "int", expected "str") | ||
|
||
[builtins fixtures/staticmethod.pyi] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,3 +17,4 @@ class int: | |
class str: pass | ||
class unicode: pass | ||
class bytes: pass | ||
class ellipsis: pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this PR (in particular because of this change) may have conflicts with my type aliases refactoring. In turned out that the
type_override
(that I killed) was also used for some overload hacks.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I'm completely following -- is there now going to be some major difference between FuncItem and FuncBase?
(I also ended up streamlining this code to handle the "overriding a classmethod with an instance method" case more cleanly -- not sure if that change makes the potential conflict better or worse.)