Skip to content

Commit 14acaae

Browse files
committed
add validation of c-class __new__ methods
1 parent b833d09 commit 14acaae

File tree

1 file changed

+46
-35
lines changed

1 file changed

+46
-35
lines changed

mypy/stubtest.py

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def __init__(
116116
self.stub_desc = stub_desc or str(getattr(stub_object, "type", stub_object))
117117

118118
if runtime_desc is None:
119-
runtime_sig = safe_inspect_signature(runtime_object, object_path)
119+
runtime_sig = safe_inspect_signature(runtime_object)
120120
if runtime_sig is None:
121121
self.runtime_desc = _truncate(repr(runtime_object), 100)
122122
else:
@@ -837,6 +837,7 @@ def from_overloadedfuncdef(stub: nodes.OverloadedFuncDef) -> Signature[nodes.Arg
837837
or arg.pos_only
838838
or assume_positional_only
839839
or arg.variable.name.strip("_") == "self"
840+
or (index == 0 and arg.variable.name.strip("_") == "cls")
840841
else arg.variable.name
841842
)
842843
all_args.setdefault(name, []).append((arg, index))
@@ -907,6 +908,7 @@ def _verify_signature(
907908
and not stub_arg.pos_only
908909
and not stub_arg.variable.name.startswith("__")
909910
and stub_arg.variable.name.strip("_") != "self"
911+
and stub_arg.variable.name.strip("_") != "cls"
910912
and not is_dunder(function_name, exclude_special=True) # noisy for dunder methods
911913
):
912914
yield (
@@ -917,6 +919,7 @@ def _verify_signature(
917919
runtime_arg.kind != inspect.Parameter.POSITIONAL_ONLY
918920
and (stub_arg.pos_only or stub_arg.variable.name.startswith("__"))
919921
and stub_arg.variable.name.strip("_") != "self"
922+
and stub_arg.variable.name.strip("_") != "cls"
920923
and not is_dunder(function_name, exclude_special=True) # noisy for dunder methods
921924
):
922925
yield (
@@ -1036,7 +1039,7 @@ def verify_funcitem(
10361039
for message in _verify_static_class_methods(stub, runtime, static_runtime, object_path):
10371040
yield Error(object_path, "is inconsistent, " + message, stub, runtime)
10381041

1039-
signature = safe_inspect_signature(runtime, object_path)
1042+
signature = safe_inspect_signature(runtime)
10401043
runtime_is_coroutine = inspect.iscoroutinefunction(runtime)
10411044

10421045
if signature:
@@ -1164,7 +1167,7 @@ def verify_overloadedfuncdef(
11641167
# TODO: Should call _verify_final_method here,
11651168
# but overloaded final methods in stubs cause a stubtest crash: see #14950
11661169

1167-
signature = safe_inspect_signature(runtime, object_path)
1170+
signature = safe_inspect_signature(runtime)
11681171
if not signature:
11691172
return
11701173

@@ -1526,9 +1529,7 @@ def is_read_only_property(runtime: object) -> bool:
15261529
return isinstance(runtime, property) and runtime.fset is None
15271530

15281531

1529-
def safe_inspect_signature(
1530-
runtime: Any, object_path: list[str] | None = None
1531-
) -> inspect.Signature | None:
1532+
def safe_inspect_signature(runtime: Any) -> inspect.Signature | None:
15321533
if (
15331534
hasattr(runtime, "__name__")
15341535
and runtime.__name__ == "__init__"
@@ -1539,38 +1540,48 @@ def safe_inspect_signature(
15391540
and runtime.__objclass__.__text_signature__ is not None
15401541
):
15411542
# This is an __init__ method with the generic C-class signature.
1542-
# In this case, the underlying class usually has a better signature,
1543+
# In this case, the underlying class often has a better signature,
15431544
# which we can convert into an __init__ signature by adding in the
15441545
# self parameter.
1545-
runtime_objclass = None
1546-
if runtime.__objclass__ is object:
1547-
# When __objclass__ is object, use the object_path to look up
1548-
# the actual class that this __init__ method came from.
1549-
if object_path:
1550-
module_name = ".".join(object_path[:-2])
1551-
module = silent_import_module(module_name)
1552-
if module:
1553-
runtime_objclass = getattr(module, object_path[-2], None)
1554-
if not (
1555-
runtime_objclass is not None
1556-
and hasattr(runtime_objclass, "__text_signature__")
1557-
and runtime_objclass.__text_signature__ is not None
1558-
):
1559-
runtime_objclass = None
1560-
else:
1561-
runtime_objclass = runtime.__objclass__
1546+
try:
1547+
s = inspect.signature(runtime.__objclass__)
1548+
1549+
parameter_kind = inspect.Parameter.POSITIONAL_OR_KEYWORD
1550+
if s.parameters:
1551+
first_parameter = next(iter(s.parameters.values()))
1552+
if first_parameter.kind == inspect.Parameter.POSITIONAL_ONLY:
1553+
parameter_kind = inspect.Parameter.POSITIONAL_ONLY
1554+
return s.replace(
1555+
parameters=[inspect.Parameter("self", parameter_kind), *s.parameters.values()]
1556+
)
1557+
except Exception:
1558+
pass
15621559

1563-
if runtime_objclass is not None:
1564-
try:
1565-
s = inspect.signature(runtime_objclass)
1566-
return s.replace(
1567-
parameters=[
1568-
inspect.Parameter("self", inspect.Parameter.POSITIONAL_OR_KEYWORD),
1569-
*s.parameters.values(),
1570-
]
1571-
)
1572-
except Exception:
1573-
pass
1560+
if (
1561+
hasattr(runtime, "__name__")
1562+
and runtime.__name__ == "__new__"
1563+
and hasattr(runtime, "__text_signature__")
1564+
and runtime.__text_signature__ == "($type, *args, **kwargs)"
1565+
and hasattr(runtime, "__self__")
1566+
and hasattr(runtime.__self__, "__text_signature__")
1567+
and runtime.__self__.__text_signature__ is not None
1568+
):
1569+
# This is a __new__ method with the generic C-class signature.
1570+
# In this case, the underlying class often has a better signature,
1571+
# which we can convert into a __new__ signature by adding in the
1572+
# cls parameter.
1573+
try:
1574+
s = inspect.signature(runtime.__self__)
1575+
parameter_kind = inspect.Parameter.POSITIONAL_OR_KEYWORD
1576+
if s.parameters:
1577+
first_parameter = next(iter(s.parameters.values()))
1578+
if first_parameter.kind == inspect.Parameter.POSITIONAL_ONLY:
1579+
parameter_kind = inspect.Parameter.POSITIONAL_ONLY
1580+
return s.replace(
1581+
parameters=[inspect.Parameter("cls", parameter_kind), *s.parameters.values()]
1582+
)
1583+
except Exception:
1584+
pass
15741585

15751586
try:
15761587
try:

0 commit comments

Comments
 (0)