diff --git a/stdlib/@tests/test_cases/check_functools.py b/stdlib/@tests/test_cases/check_functools.py index 133ac8ccb456..e3b90ab5743f 100644 --- a/stdlib/@tests/test_cases/check_functools.py +++ b/stdlib/@tests/test_cases/check_functools.py @@ -1,6 +1,6 @@ from __future__ import annotations -from functools import cache, cached_property, lru_cache, update_wrapper, wraps +from functools import cache, cached_property, lru_cache, singledispatchmethod, update_wrapper, wraps from typing import Callable, ParamSpec, TypeVar from typing_extensions import assert_type @@ -181,3 +181,36 @@ class CachedChild(CachedParent): @cache def method(self) -> Child: return Child() + + +class Formatter: + @singledispatchmethod + def format(self, value: object) -> str: + return str(value) + + +formatter = Formatter() + + +@Formatter.format.register(int) +def format_int(self: Formatter, value: int) -> str: + return f"int:{value}" + + +@formatter.format.register(bytes) +def format_bytes(self: Formatter, value: bytes) -> str: + return value.decode("ascii") + + +assert_type(formatter.format(3), str) +assert_type(formatter.format(b"x"), str) + + +def format_float(self: Formatter, value: float) -> str: + return f"float:{value}" + + +assert_type(Formatter.format.register(float, format_float), Callable[..., str]) +assert_type(formatter.format.__name__, str) +assert_type(formatter.format.__qualname__, str) +assert_type(formatter.format.__wrapped__, Callable[..., str]) diff --git a/stdlib/functools.pyi b/stdlib/functools.pyi index 7e1f88b91987..23260ab90855 100644 --- a/stdlib/functools.pyi +++ b/stdlib/functools.pyi @@ -245,6 +245,20 @@ class _SingleDispatchCallable(Generic[_T]): def singledispatch(func: Callable[..., _T]) -> _SingleDispatchCallable[_T]: ... +@type_check_only +class _SingleDispatchMethodCallable(Generic[_T]): + __name__: str + __qualname__: str + __wrapped__: Callable[..., _T] + def __call__(self, /, *args: Any, **kwargs: Any) -> _T: ... + + @overload + def register(self, cls: _RegType, method: None = None) -> Callable[[Callable[..., _T]], Callable[..., _T]]: ... + @overload + def register(self, cls: Callable[..., _T], method: None = None) -> Callable[..., _T]: ... + @overload + def register(self, cls: _RegType, method: Callable[..., _T]) -> Callable[..., _T]: ... + class singledispatchmethod(Generic[_T]): dispatcher: _SingleDispatchCallable[_T] func: Callable[..., _T] @@ -259,7 +273,7 @@ class singledispatchmethod(Generic[_T]): @overload def register(self, cls: _RegType, method: Callable[..., _T]) -> Callable[..., _T]: ... - def __get__(self, obj: _S, cls: type[_S] | None = None) -> Callable[..., _T]: ... + def __get__(self, obj: _S, cls: type[_S] | None = None) -> _SingleDispatchMethodCallable[_T]: ... class cached_property(Generic[_T_co]): func: Callable[[Any], _T_co]