Skip to content

Commit

Permalink
Stubtest: check that the stub is abstract if the runtime is, even whe…
Browse files Browse the repository at this point in the history
…n the stub is an overloaded method (#14955)

#13323 means that stubtest will currently emit an error if the runtime
is decorated with `@abstractmethod` but the stub is not, but _only_ if
the stub is not an overloaded function, as overloaded functions have a
different internal representation in mypy. This PR fixes that
discrepancy.
  • Loading branch information
AlexWaygood committed Mar 24, 2023
1 parent 764ff6f commit 2c6e43e
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 3 deletions.
11 changes: 11 additions & 0 deletions mypy/stubtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,17 @@ def verify_overloadedfuncdef(
if not callable(runtime):
return

# mypy doesn't allow overloads where one overload is abstract but another isn't,
# so it should be okay to just check whether the first overload is abstract or not.
#
# TODO: Mypy *does* allow properties where e.g. the getter is abstract but the setter is not;
# and any property with a setter is represented as an OverloadedFuncDef internally;
# not sure exactly what (if anything) we should do about that.
first_part = stub.items[0]
if isinstance(first_part, nodes.Decorator) and first_part.is_overload:
for msg in _verify_abstract_status(first_part.func, runtime):
yield Error(object_path, msg, stub, runtime)

for message in _verify_static_class_methods(stub, runtime, object_path):
yield Error(object_path, "is inconsistent, " + message, stub, runtime)

Expand Down
58 changes: 55 additions & 3 deletions mypy/test/teststubtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1475,7 +1475,10 @@ def test_metaclass_abcmeta(self) -> Iterator[Case]:
@collect_cases
def test_abstract_methods(self) -> Iterator[Case]:
yield Case(
stub="from abc import abstractmethod",
stub="""
from abc import abstractmethod
from typing import overload
""",
runtime="from abc import abstractmethod",
error=None,
)
Expand Down Expand Up @@ -1504,15 +1507,64 @@ def some(self) -> None: ...
""",
error=None,
)
# Runtime can miss `@abstractmethod`:
yield Case(
stub="""
class A3:
@overload
def some(self, other: int) -> str: ...
@overload
def some(self, other: str) -> int: ...
""",
runtime="""
class A3:
@abstractmethod
def some(self, other) -> None: ...
""",
error="A3.some",
)
yield Case(
stub="""
class A4:
@overload
@abstractmethod
def some(self, other: int) -> str: ...
@overload
@abstractmethod
def some(self, other: str) -> int: ...
""",
runtime="""
class A4:
@abstractmethod
def some(self, other) -> None: ...
""",
error=None,
)
yield Case(
stub="""
class A5:
@abstractmethod
@overload
def some(self, other: int) -> str: ...
@abstractmethod
@overload
def some(self, other: str) -> int: ...
""",
runtime="""
class A5:
@abstractmethod
def some(self, other) -> None: ...
""",
error=None,
)
# Runtime can miss `@abstractmethod`:
yield Case(
stub="""
class A6:
@abstractmethod
def some(self) -> None: ...
""",
runtime="""
class A3:
class A6:
def some(self) -> None: ...
""",
error=None,
Expand Down

0 comments on commit 2c6e43e

Please sign in to comment.