diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 36cd0a213d4d..d0b4fbd9d3c2 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -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)) @@ -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 ( @@ -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 ( @@ -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__" + 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)