Skip to content
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

stubtest: get better signatures for __init__ of C classes #18259

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
68 changes: 68 additions & 0 deletions mypy/stubtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,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 @@ -907,6 +908,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 @@ -917,6 +919,7 @@ def _verify_signature(
runtime_arg.kind != inspect.Parameter.POSITIONAL_ONLY
and (stub_arg.pos_only or 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 Down Expand Up @@ -1527,6 +1530,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__"
tungol marked this conversation as resolved.
Show resolved Hide resolved
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
Loading