diff --git a/stdlib/@tests/test_cases/check_types.py b/stdlib/@tests/test_cases/check_types.py index d17a8b176fc8..271b0e95d226 100644 --- a/stdlib/@tests/test_cases/check_types.py +++ b/stdlib/@tests/test_cases/check_types.py @@ -8,6 +8,36 @@ _T = TypeVar("_T") +# test `types.MethodType` + + +class MethodTypeTest: + def method(self, x: int) -> int: + return x + + +def fake_method(self: MethodTypeTest, /, x: int) -> int: + return x + + +bound_method1 = MethodTypeTest().method +assert isinstance(bound_method1, types.MethodType) +# Needs special handling by the type checker +assert_type(bound_method1(42), int) # type: ignore + +bound_method2 = types.MethodType(MethodTypeTest.method, MethodTypeTest()) +assert isinstance(bound_method2, types.MethodType) +assert_type(bound_method2(42), int) + +# The first argument must be unbound +bound_method3 = types.MethodType(MethodTypeTest().method, MethodTypeTest()) +assert isinstance(bound_method3, types.MethodType) +bound_method3(42) # type: ignore + +bound_method4 = types.MethodType(fake_method, MethodTypeTest()) +assert isinstance(bound_method4, types.MethodType) +bound_method4(42) + # test `types.SimpleNamespace` # Valid: diff --git a/stdlib/types.pyi b/stdlib/types.pyi index 457cf609da8c..77f80dd7d8f8 100644 --- a/stdlib/types.pyi +++ b/stdlib/types.pyi @@ -17,7 +17,7 @@ from collections.abc import ( ValuesView, ) from importlib.machinery import ModuleSpec -from typing import Any, ClassVar, Literal, ParamSpec, TypeVar, final, overload +from typing import Any, ClassVar, Concatenate, Generic, Literal, ParamSpec, TypeVar, final, overload from typing_extensions import Self, TypeAliasType, TypeVarTuple, deprecated, disjoint_base if sys.version_info >= (3, 14): @@ -70,8 +70,13 @@ if sys.version_info >= (3, 15): _T1 = TypeVar("_T1") _T2 = TypeVar("_T2") +_P = ParamSpec("_P") +_P_default = ParamSpec("_P_default", default=...) +_R = TypeVar("_R") +_R_default = TypeVar("_R_default", default=Any) _KT_co = TypeVar("_KT_co", covariant=True) _VT_co = TypeVar("_VT_co", covariant=True) +_Fn = TypeVar("_Fn", bound=Callable[..., object]) # Make sure this class definition stays roughly in line with `builtins.function` @final @@ -461,7 +466,7 @@ class CoroutineType(Coroutine[_YieldT_co, _SendT_nd_contra, _ReturnT_nd_co]): def __class_getitem__(cls, item: Any, /) -> Any: ... @final -class MethodType: +class MethodType(Generic[_P_default, _R_default]): @property def __closure__(self) -> tuple[CellType, ...] | None: ... # inherited from the added function @property @@ -476,8 +481,9 @@ class MethodType: def __name__(self) -> str: ... # inherited from the added function @property def __qualname__(self) -> str: ... # inherited from the added function - def __new__(cls, func: Callable[..., Any], instance: object, /) -> Self: ... - def __call__(self, *args: Any, **kwargs: Any) -> Any: ... + # First func argument is usually "self". + def __new__(cls, func: Callable[Concatenate[Any, _P_default], _R_default], instance: object, /) -> Self: ... + def __call__(self, *args: _P_default.args, **kwargs: _P_default.kwargs) -> _R_default: ... if sys.version_info >= (3, 13): def __get__(self, instance: object, owner: type | None = None, /) -> Self: ... @@ -672,10 +678,6 @@ class DynamicClassAttribute(property): def setter(self, fset: Callable[[Any, Any], object]) -> DynamicClassAttribute: ... def deleter(self, fdel: Callable[[Any], object]) -> DynamicClassAttribute: ... -_Fn = TypeVar("_Fn", bound=Callable[..., object]) -_R = TypeVar("_R") -_P = ParamSpec("_P") - # it's not really an Awaitable, but can be used in an await expression. Real type: Generator & Awaitable @overload def coroutine(func: Callable[_P, Generator[Any, Any, _R]]) -> Callable[_P, Awaitable[_R]]: ...