From f15c352eb50225a6b5f0a3fab4454b28ee30b456 Mon Sep 17 00:00:00 2001 From: Sumit Date: Wed, 11 Nov 2026 20:02:03 +0530 Subject: [PATCH] [functools] Make `partial` covariant in its return type --- stdlib/@tests/test_cases/check_functools.py | 16 +++++++++++++++- stdlib/functools.pyi | 10 +++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/stdlib/@tests/test_cases/check_functools.py b/stdlib/@tests/test_cases/check_functools.py index d3aaf5ca1c4d..7e3e145a2e08 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, singledispatchmethod, update_wrapper, wraps +from functools import cache, cached_property, lru_cache, partial, singledispatchmethod, update_wrapper, wraps from typing import Callable, ParamSpec, TypeVar from typing_extensions import assert_type @@ -194,6 +194,20 @@ def method(self) -> Child: return Child() +def _returns_child() -> Child: + return Child() + + +def _accept_parent_partial(p: partial[Parent]) -> None: + assert_type(p(), Parent) + + +def check_partial_covariance() -> None: + p_child = partial(_returns_child) + assert_type(p_child, partial[Child]) + _accept_parent_partial(p_child) + + class Formatter: @singledispatchmethod def format(self, value: object) -> str: diff --git a/stdlib/functools.pyi b/stdlib/functools.pyi index 42041befd999..d084412810c8 100644 --- a/stdlib/functools.pyi +++ b/stdlib/functools.pyi @@ -183,20 +183,20 @@ def total_ordering(cls: type[_T]) -> type[_T]: ... def cmp_to_key(mycmp: Callable[[_T, _T], int]) -> Callable[[_T], SupportsAllComparisons]: ... @disjoint_base -class partial(Generic[_T]): +class partial(Generic[_T_co]): @property - def func(self) -> Callable[..., _T]: ... + def func(self) -> Callable[..., _T_co]: ... @property def args(self) -> tuple[Any, ...]: ... @property def keywords(self) -> dict[str, Any]: ... - def __new__(cls, func: Callable[..., _T], /, *args: Any, **kwargs: Any) -> Self: ... - def __call__(self, /, *args: Any, **kwargs: Any) -> _T: ... + def __new__(cls, func: Callable[..., _T_co], /, *args: Any, **kwargs: Any) -> Self: ... + def __call__(self, /, *args: Any, **kwargs: Any) -> _T_co: ... if sys.version_info >= (3, 14): @overload def __get__(self, instance: None, owner: type[Any] | None = None, /) -> Self: ... @overload - def __get__(self, instance: object, owner: type[Any] | None = None, /) -> Callable[..., _T]: ... + def __get__(self, instance: object, owner: type[Any] | None = None, /) -> Callable[..., _T_co]: ... def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...