Skip to content

Commit

Permalink
stubtest: fix signature construction for overload + implicit classmet…
Browse files Browse the repository at this point in the history
…hod (python#9921)

__class_getitem__ and __init_subclass__ are always classmethods, even if
they aren't decorated. We previously only handled this correctly if
these methods weren't overloaded.

This came up in python/typeshed#4937

Co-authored-by: hauntsaninja <>
  • Loading branch information
hauntsaninja authored Jan 18, 2021
1 parent 92923b2 commit 734e4ad
Showing 1 changed file with 13 additions and 9 deletions.
22 changes: 13 additions & 9 deletions mypy/stubtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,15 @@ def _verify_arg_default_value(
)


def maybe_strip_cls(name: str, args: List[nodes.Argument]) -> List[nodes.Argument]:
if name in ("__init_subclass__", "__class_getitem__"):
# These are implicitly classmethods. If the stub chooses not to have @classmethod, we
# should remove the cls argument
if args[0].variable.name == "cls":
return args[1:]
return args


class Signature(Generic[T]):
def __init__(self) -> None:
self.pos = [] # type: List[T]
Expand Down Expand Up @@ -428,7 +437,8 @@ def get_desc(arg: Any) -> str:
@staticmethod
def from_funcitem(stub: nodes.FuncItem) -> "Signature[nodes.Argument]":
stub_sig = Signature() # type: Signature[nodes.Argument]
for stub_arg in stub.arguments:
stub_args = maybe_strip_cls(stub.name, stub.arguments)
for stub_arg in stub_args:
if stub_arg.kind in (nodes.ARG_POS, nodes.ARG_OPT):
stub_sig.pos.append(stub_arg)
elif stub_arg.kind in (nodes.ARG_NAMED, nodes.ARG_NAMED_OPT):
Expand Down Expand Up @@ -476,7 +486,8 @@ def from_overloadedfuncdef(stub: nodes.OverloadedFuncDef) -> "Signature[nodes.Ar
all_args = {} # type: Dict[str, List[Tuple[nodes.Argument, int]]]
for func in map(_resolve_funcitem_from_decorator, stub.items):
assert func is not None
for index, arg in enumerate(func.arguments):
args = maybe_strip_cls(stub.name, func.arguments)
for index, arg in enumerate(args):
# For positional-only args, we allow overloads to have different names for the same
# argument. To accomplish this, we just make up a fake index-based name.
name = (
Expand Down Expand Up @@ -658,13 +669,6 @@ def verify_funcitem(
# catch RuntimeError because of https://bugs.python.org/issue39504
return

if stub.name in ("__init_subclass__", "__class_getitem__"):
# These are implicitly classmethods. If the stub chooses not to have @classmethod, we
# should remove the cls argument
if stub.arguments[0].variable.name == "cls":
stub = copy.copy(stub)
stub.arguments = stub.arguments[1:]

stub_sig = Signature.from_funcitem(stub)
runtime_sig = Signature.from_inspect_signature(signature)

Expand Down

0 comments on commit 734e4ad

Please sign in to comment.