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
53 changes: 49 additions & 4 deletions mypy/stubtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def __init__(
self.stub_desc = stub_desc or str(getattr(stub_object, "type", stub_object))

if runtime_desc is None:
runtime_sig = safe_inspect_signature(runtime_object)
runtime_sig = safe_inspect_signature(runtime_object, object_path)
if runtime_sig is None:
self.runtime_desc = _truncate(repr(runtime_object), 100)
else:
Expand Down Expand Up @@ -1036,7 +1036,7 @@ def verify_funcitem(
for message in _verify_static_class_methods(stub, runtime, static_runtime, object_path):
yield Error(object_path, "is inconsistent, " + message, stub, runtime)

signature = safe_inspect_signature(runtime)
signature = safe_inspect_signature(runtime, object_path)
runtime_is_coroutine = inspect.iscoroutinefunction(runtime)

if signature:
Expand Down Expand Up @@ -1164,7 +1164,7 @@ def verify_overloadedfuncdef(
# TODO: Should call _verify_final_method here,
# but overloaded final methods in stubs cause a stubtest crash: see #14950

signature = safe_inspect_signature(runtime)
signature = safe_inspect_signature(runtime, object_path)
if not signature:
return

Expand Down Expand Up @@ -1526,7 +1526,52 @@ def is_read_only_property(runtime: object) -> bool:
return isinstance(runtime, property) and runtime.fset is None


def safe_inspect_signature(runtime: Any) -> inspect.Signature | None:
def safe_inspect_signature(
runtime: Any, object_path: list[str] | None = None
) -> 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 usually has a better signature,
# which we can convert into an __init__ signature by adding in the
# self parameter.
runtime_objclass = None
if runtime.__objclass__ is object:
# When __objclass__ is object, use the object_path to look up
# the actual class that this __init__ method came from.
if object_path:
module_name = ".".join(object_path[:-2])
module = silent_import_module(module_name)
if module:
runtime_objclass = getattr(module, object_path[-2], None)
if not (
runtime_objclass is not None
and hasattr(runtime_objclass, "__text_signature__")
and runtime_objclass.__text_signature__ is not None
):
runtime_objclass = None
else:
runtime_objclass = runtime.__objclass__

if runtime_objclass is not None:
try:
s = inspect.signature(runtime_objclass)
return s.replace(
parameters=[
inspect.Parameter("self", inspect.Parameter.POSITIONAL_OR_KEYWORD),
*s.parameters.values(),
]
)
except Exception:
pass

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