Skip to content
68 changes: 68 additions & 0 deletions mypy/stubtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,7 @@ def from_overloadedfuncdef(stub: nodes.OverloadedFuncDef) -> Signature[nodes.Arg
or arg.pos_only
or assume_positional_only
or arg.variable.name.strip("_") == "self"
or (index == 0 and arg.variable.name.strip("_") == "cls")
else arg.variable.name
)
all_args.setdefault(name, []).append((arg, index))
Expand Down Expand Up @@ -939,6 +940,7 @@ def _verify_signature(
and not stub_arg.pos_only
and not stub_arg.variable.name.startswith("__")
and stub_arg.variable.name.strip("_") != "self"
and stub_arg.variable.name.strip("_") != "cls"
and not is_dunder(function_name, exclude_special=True) # noisy for dunder methods
):
yield (
Expand All @@ -950,6 +952,7 @@ def _verify_signature(
and (stub_arg.pos_only or stub_arg.variable.name.startswith("__"))
and not runtime_arg.name.startswith("__")
and stub_arg.variable.name.strip("_") != "self"
and stub_arg.variable.name.strip("_") != "cls"
and not is_dunder(function_name, exclude_special=True) # noisy for dunder methods
):
yield (
Expand Down Expand Up @@ -1593,6 +1596,71 @@ def is_read_only_property(runtime: object) -> bool:


def safe_inspect_signature(runtime: Any) -> inspect.Signature | None:
if (
hasattr(runtime, "__name__")
and runtime.__name__ == "__init__"
and hasattr(runtime, "__text_signature__")
and runtime.__text_signature__ == "($self, /, *args, **kwargs)"
and hasattr(runtime, "__objclass__")
and hasattr(runtime.__objclass__, "__text_signature__")
and runtime.__objclass__.__text_signature__ is not None
):
# This is an __init__ method with the generic C-class signature.
# In this case, the underlying class often has a better signature,
# which we can convert into an __init__ signature by adding in the
# self parameter.
try:
s = inspect.signature(runtime.__objclass__)

parameter_kind: inspect._ParameterKind = inspect.Parameter.POSITIONAL_OR_KEYWORD
if s.parameters:
first_parameter = next(iter(s.parameters.values()))
if first_parameter.kind == inspect.Parameter.POSITIONAL_ONLY:
parameter_kind = inspect.Parameter.POSITIONAL_ONLY
return s.replace(
parameters=[inspect.Parameter("self", parameter_kind), *s.parameters.values()]
)
except Exception:
pass

if (
hasattr(runtime, "__name__")
and runtime.__name__ == "__new__"
and hasattr(runtime, "__text_signature__")
and runtime.__text_signature__ == "($type, *args, **kwargs)"
and hasattr(runtime, "__self__")
and hasattr(runtime.__self__, "__text_signature__")
and runtime.__self__.__text_signature__ is not None
):
# This is a __new__ method with the generic C-class signature.
# In this case, the underlying class often has a better signature,
# which we can convert into a __new__ signature by adding in the
# cls parameter.

# If the attached class has a valid __init__, skip recovering a
# signature for this __new__ method.
has_init = False
if (
hasattr(runtime.__self__, "__init__")
and hasattr(runtime.__self__.__init__, "__objclass__")
and runtime.__self__.__init__.__objclass__ is runtime.__self__
):
has_init = True

if not has_init:
try:
s = inspect.signature(runtime.__self__)
parameter_kind = inspect.Parameter.POSITIONAL_OR_KEYWORD
if s.parameters:
first_parameter = next(iter(s.parameters.values()))
if first_parameter.kind == inspect.Parameter.POSITIONAL_ONLY:
parameter_kind = inspect.Parameter.POSITIONAL_ONLY
return s.replace(
parameters=[inspect.Parameter("cls", parameter_kind), *s.parameters.values()]
)
except Exception:
pass

try:
try:
return inspect.signature(runtime)
Expand Down