From 2a09a2bd6736df73c9a451847ff75984772c58aa Mon Sep 17 00:00:00 2001 From: George Ogden Date: Thu, 20 Nov 2025 16:19:28 +0000 Subject: [PATCH 1/2] Write small sample of specific operators for proof of concept --- stdlib/_operator.pyi | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/stdlib/_operator.pyi b/stdlib/_operator.pyi index cb1c1bcfc4aa..344ac4cdcfa5 100644 --- a/stdlib/_operator.pyi +++ b/stdlib/_operator.pyi @@ -7,6 +7,9 @@ from typing_extensions import ParamSpec, TypeAlias, TypeIs _R = TypeVar("_R") _T = TypeVar("_T") +_T_contra = TypeVar("_T_contra", contravariant=True) +_U = TypeVar("_U") +_U_co = TypeVar("_U_co", covariant=True) _T_co = TypeVar("_T_co", covariant=True) _K = TypeVar("_K") _V = TypeVar("_V") @@ -58,7 +61,31 @@ def truth(a: object, /) -> bool: ... def is_(a: object, b: object, /) -> bool: ... def is_not(a: object, b: object, /) -> bool: ... def abs(a: SupportsAbs[_T], /) -> _T: ... + +class _SupportsAdd(Protocol[_T_contra, _U_co]): + def __add__(self, other: _T_contra, /) -> _U_co: ... + +class _SupportsRAdd(Protocol[_T_contra, _U_co]): + def __radd__(self, other: _T_contra, /) -> _U_co: ... + +@overload +def add(a: _SupportsAdd[_T, _U], b: _T, /) -> _U: ... +@overload +def add(a: _T, b: _SupportsRAdd[_T, _U], /) -> _U: ... +@overload def add(a: Any, b: Any, /) -> Any: ... + +class _SupportsAnd(Protocol[_T_contra, _U_co]): + def __and__(self, other: _T_contra, /) -> _U_co: ... + +class _SupportsRAnd(Protocol[_T_contra, _U_co]): + def __rand__(self, other: _T_contra, /) -> _U_co: ... + +@overload +def and_(a: _SupportsAnd[_T, _U], b: _T, /) -> _U: ... +@overload +def and_(a: _T, b: _SupportsRAnd[_T, _U], /) -> _U: ... +@overload def and_(a: Any, b: Any, /) -> Any: ... def floordiv(a: Any, b: Any, /) -> Any: ... def index(a: SupportsIndex, /) -> int: ... @@ -66,6 +93,18 @@ def inv(a: _SupportsInversion[_T_co], /) -> _T_co: ... def invert(a: _SupportsInversion[_T_co], /) -> _T_co: ... def lshift(a: Any, b: Any, /) -> Any: ... def mod(a: Any, b: Any, /) -> Any: ... + +class _SupportsMul(Protocol[_T_contra, _U_co]): + def __mul__(self, other: _T_contra, /) -> _U_co: ... + +class _SupportsRMul(Protocol[_T_contra, _U_co]): + def __rmul__(self, other: _T_contra, /) -> _U_co: ... + +@overload +def mul(a: _SupportsMul[_T, _U], b: _T, /) -> _U: ... +@overload +def mul(a: _T, b: _SupportsRMul[_T, _U], /) -> _U: ... +@overload def mul(a: Any, b: Any, /) -> Any: ... def matmul(a: Any, b: Any, /) -> Any: ... def neg(a: _SupportsNeg[_T_co], /) -> _T_co: ... From 8e01f1b49c21f360b49c14086776b0dd565ad54a Mon Sep 17 00:00:00 2001 From: George Ogden Date: Thu, 20 Nov 2025 21:32:13 +0000 Subject: [PATCH 2/2] Update operator stubs with more specific annotations --- stdlib/_operator.pyi | 353 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 327 insertions(+), 26 deletions(-) diff --git a/stdlib/_operator.pyi b/stdlib/_operator.pyi index 344ac4cdcfa5..131a6fc2b0d6 100644 --- a/stdlib/_operator.pyi +++ b/stdlib/_operator.pyi @@ -1,6 +1,6 @@ import sys from _typeshed import SupportsGetItem -from collections.abc import Callable, Container, Iterable, MutableMapping, MutableSequence, Sequence +from collections.abc import Callable, Iterable, MutableMapping, MutableSequence, Sequence from operator import attrgetter as attrgetter, itemgetter as itemgetter, methodcaller as methodcaller from typing import Any, AnyStr, Protocol, SupportsAbs, SupportsIndex, TypeVar, overload, type_check_only from typing_extensions import ParamSpec, TypeAlias, TypeIs @@ -45,78 +45,281 @@ class _SupportsInversion(Protocol[_T_co]): class _SupportsNeg(Protocol[_T_co]): def __neg__(self) -> _T_co: ... +@type_check_only +class _SupportsAdd(Protocol[_T_contra, _U_co]): + def __add__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsRAdd(Protocol[_T_contra, _U_co]): + def __radd__(self, other: _T_contra, /) -> _U_co: ... + @type_check_only class _SupportsPos(Protocol[_T_co]): def __pos__(self) -> _T_co: ... +@type_check_only +class _SupportsAnd(Protocol[_T_contra, _U_co]): + def __and__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsRAnd(Protocol[_T_contra, _U_co]): + def __rand__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsConcat(SupportsGetItem[Any, Any], _SupportsAdd[_T_contra, _U_co]): ... + +@type_check_only +class _SupportsRConcat(SupportsGetItem[Any, Any], _SupportsRAdd[_T_contra, _U_co]): ... + +@type_check_only +class _SupportsFloorDiv(Protocol[_T_contra, _U_co]): + def __floordiv__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsRFloorDiv(Protocol[_T_contra, _U_co]): + def __rfloordiv__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsLShift(Protocol[_T_contra, _U_co]): + def __lshift__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsRLShift(Protocol[_T_contra, _U_co]): + def __rlshift__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsMod(Protocol[_T_contra, _U_co]): + def __mod__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsRMod(Protocol[_T_contra, _U_co]): + def __rmod__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsMul(Protocol[_T_contra, _U_co]): + def __mul__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsRMul(Protocol[_T_contra, _U_co]): + def __rmul__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsMatMul(Protocol[_T_contra, _U_co]): + def __matmul__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsRMatMul(Protocol[_T_contra, _U_co]): + def __rmatmul__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsOr(Protocol[_T_contra, _U_co]): + def __or__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsROr(Protocol[_T_contra, _U_co]): + def __ror__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsPow(Protocol[_T_contra, _U_co]): + def __pow__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsRPow(Protocol[_T_contra, _U_co]): + def __rpow__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsRShift(Protocol[_T_contra, _U_co]): + def __rshift__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsRRShift(Protocol[_T_contra, _U_co]): + def __rrshift__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsSub(Protocol[_T_contra, _U_co]): + def __sub__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsRSub(Protocol[_T_contra, _U_co]): + def __rsub__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsTrueDiv(Protocol[_T_contra, _U_co]): + def __truediv__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsRTrueDiv(Protocol[_T_contra, _U_co]): + def __rtruediv__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsXor(Protocol[_T_contra, _U_co]): + def __xor__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsRXor(Protocol[_T_contra, _U_co]): + def __rxor__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsContains(Protocol[_T_contra, _U_co]): + def __contains__(self, other: _T_contra, /) -> _U_co: ... + +@type_check_only +class _SupportsIAdd(Protocol[_T_contra, _U_co]): + def __iadd__(self, other: _T_contra, /) -> _U_co: ... # noqa: Y034 + +@type_check_only +class _SupportsIAnd(Protocol[_T_contra, _U_co]): + def __iand__(self, other: _T_contra, /) -> _U_co: ... # noqa: Y034 + +@type_check_only +class _SupportsIConcat(SupportsGetItem[Any, Any], _SupportsIAdd[_T_contra, _U_co]): ... + +@type_check_only +class _SupportsIFloorDiv(Protocol[_T_contra, _U_co]): + def __ifloordiv__(self, other: _T_contra, /) -> _U_co: ... # noqa: Y034 + +@type_check_only +class _SupportsILShift(Protocol[_T_contra, _U_co]): + def __ilshift__(self, other: _T_contra, /) -> _U_co: ... # noqa: Y034 + +@type_check_only +class _SupportsIMod(Protocol[_T_contra, _U_co]): + def __imod__(self, other: _T_contra, /) -> _U_co: ... # noqa: Y034 + +@type_check_only +class _SupportsIMul(Protocol[_T_contra, _U_co]): + def __imul__(self, other: _T_contra, /) -> _U_co: ... # noqa: Y034 + +@type_check_only +class _SupportsIMatMul(Protocol[_T_contra, _U_co]): + def __imatmul__(self, other: _T_contra, /) -> _U_co: ... # noqa: Y034 + +@type_check_only +class _SupportsIOr(Protocol[_T_contra, _U_co]): + def __ior__(self, other: _T_contra, /) -> _U_co: ... # noqa: Y034 + +@type_check_only +class _SupportsIPow(Protocol[_T_contra, _U_co]): + def __ipow__(self, other: _T_contra, /) -> _U_co: ... # noqa: Y034 + +@type_check_only +class _SupportsIRShift(Protocol[_T_contra, _U_co]): + def __irshift__(self, other: _T_contra, /) -> _U_co: ... # noqa: Y034 + +@type_check_only +class _SupportsISub(Protocol[_T_contra, _U_co]): + def __isub__(self, other: _T_contra, /) -> _U_co: ... # noqa: Y034 + +@type_check_only +class _SupportsITrueDiv(Protocol[_T_contra, _U_co]): + def __itruediv__(self, other: _T_contra, /) -> _U_co: ... # noqa: Y034 + +@type_check_only +class _SupportsIXor(Protocol[_T_contra, _U_co]): + def __ixor__(self, other: _T_contra, /) -> _U_co: ... # noqa: Y034 + # All four comparison functions must have the same signature, or we get false-positive errors def lt(a: _SupportsComparison, b: _SupportsComparison, /) -> Any: ... def le(a: _SupportsComparison, b: _SupportsComparison, /) -> Any: ... -def eq(a: object, b: object, /) -> Any: ... -def ne(a: object, b: object, /) -> Any: ... def ge(a: _SupportsComparison, b: _SupportsComparison, /) -> Any: ... def gt(a: _SupportsComparison, b: _SupportsComparison, /) -> Any: ... +def eq(a: object, b: object, /) -> Any: ... +def ne(a: object, b: object, /) -> Any: ... def not_(a: object, /) -> bool: ... def truth(a: object, /) -> bool: ... def is_(a: object, b: object, /) -> bool: ... def is_not(a: object, b: object, /) -> bool: ... def abs(a: SupportsAbs[_T], /) -> _T: ... - -class _SupportsAdd(Protocol[_T_contra, _U_co]): - def __add__(self, other: _T_contra, /) -> _U_co: ... - -class _SupportsRAdd(Protocol[_T_contra, _U_co]): - def __radd__(self, other: _T_contra, /) -> _U_co: ... - @overload def add(a: _SupportsAdd[_T, _U], b: _T, /) -> _U: ... @overload def add(a: _T, b: _SupportsRAdd[_T, _U], /) -> _U: ... @overload def add(a: Any, b: Any, /) -> Any: ... - -class _SupportsAnd(Protocol[_T_contra, _U_co]): - def __and__(self, other: _T_contra, /) -> _U_co: ... - -class _SupportsRAnd(Protocol[_T_contra, _U_co]): - def __rand__(self, other: _T_contra, /) -> _U_co: ... - @overload def and_(a: _SupportsAnd[_T, _U], b: _T, /) -> _U: ... @overload def and_(a: _T, b: _SupportsRAnd[_T, _U], /) -> _U: ... @overload def and_(a: Any, b: Any, /) -> Any: ... +@overload +def concat(a: _SupportsConcat[_T, _U], b: _T, /) -> _U: ... +@overload +def concat(a: _T, b: _SupportsRConcat[_T, _U], /) -> _U: ... +@overload +def concat(a: Any, b: Any, /) -> Any: ... +@overload +def floordiv(a: _SupportsFloorDiv[_T, _U], b: _T, /) -> _U: ... +@overload +def floordiv(a: _T, b: _SupportsRFloorDiv[_T, _U], /) -> _U: ... +@overload def floordiv(a: Any, b: Any, /) -> Any: ... def index(a: SupportsIndex, /) -> int: ... def inv(a: _SupportsInversion[_T_co], /) -> _T_co: ... def invert(a: _SupportsInversion[_T_co], /) -> _T_co: ... +@overload +def lshift(a: _SupportsLShift[_T, _U], b: _T, /) -> _U: ... +@overload +def lshift(a: _T, b: _SupportsRLShift[_T, _U], /) -> _U: ... +@overload def lshift(a: Any, b: Any, /) -> Any: ... +@overload +def mod(a: _SupportsMod[_T, _U], b: _T, /) -> _U: ... +@overload +def mod(a: _T, b: _SupportsRMod[_T, _U], /) -> _U: ... +@overload def mod(a: Any, b: Any, /) -> Any: ... - -class _SupportsMul(Protocol[_T_contra, _U_co]): - def __mul__(self, other: _T_contra, /) -> _U_co: ... - -class _SupportsRMul(Protocol[_T_contra, _U_co]): - def __rmul__(self, other: _T_contra, /) -> _U_co: ... - @overload def mul(a: _SupportsMul[_T, _U], b: _T, /) -> _U: ... @overload def mul(a: _T, b: _SupportsRMul[_T, _U], /) -> _U: ... @overload def mul(a: Any, b: Any, /) -> Any: ... +@overload +def matmul(a: _SupportsMatMul[_T, _U], b: _T, /) -> _U: ... +@overload +def matmul(a: _T, b: _SupportsRMatMul[_T, _U], /) -> _U: ... +@overload def matmul(a: Any, b: Any, /) -> Any: ... def neg(a: _SupportsNeg[_T_co], /) -> _T_co: ... +@overload +def or_(a: _SupportsOr[_T, _U], b: _T, /) -> _U: ... +@overload +def or_(a: _T, b: _SupportsROr[_T, _U], /) -> _U: ... +@overload def or_(a: Any, b: Any, /) -> Any: ... def pos(a: _SupportsPos[_T_co], /) -> _T_co: ... +@overload +def pow(a: _SupportsPow[_T, _U], b: _T, /) -> _U: ... +@overload +def pow(a: _T, b: _SupportsRPow[_T, _U], /) -> _U: ... +@overload def pow(a: Any, b: Any, /) -> Any: ... +@overload +def rshift(a: _SupportsRShift[_T, _U], b: _T, /) -> _U: ... +@overload +def rshift(a: _T, b: _SupportsRRShift[_T, _U], /) -> _U: ... +@overload def rshift(a: Any, b: Any, /) -> Any: ... +@overload +def sub(a: _SupportsSub[_T, _U], b: _T, /) -> _U: ... +@overload +def sub(a: _T, b: _SupportsRSub[_T, _U], /) -> _U: ... +@overload def sub(a: Any, b: Any, /) -> Any: ... +@overload +def truediv(a: _SupportsTrueDiv[_T, _U], b: _T, /) -> _U: ... +@overload +def truediv(a: _T, b: _SupportsRTrueDiv[_T, _U], /) -> _U: ... +@overload def truediv(a: Any, b: Any, /) -> Any: ... +@overload +def xor(a: _SupportsXor[_T, _U], b: _T, /) -> _U: ... +@overload +def xor(a: _T, b: _SupportsRXor[_T, _U], /) -> _U: ... +@overload def xor(a: Any, b: Any, /) -> Any: ... -def concat(a: Sequence[_T], b: Sequence[_T], /) -> Sequence[_T]: ... -def contains(a: Container[object], b: object, /) -> bool: ... +def contains(a: _SupportsContains[_T_contra, _U_co], b: _T_contra, /) -> _U_co: ... def countOf(a: Iterable[object], b: object, /) -> int: ... @overload def delitem(a: MutableSequence[Any], b: SupportsIndex, /) -> None: ... @@ -136,19 +339,117 @@ def setitem(a: MutableSequence[_T], b: slice, c: Sequence[_T], /) -> None: ... @overload def setitem(a: MutableMapping[_K, _V], b: _K, c: _V, /) -> None: ... def length_hint(obj: object, default: int = 0, /) -> int: ... +@overload +def iadd(a: _SupportsIAdd[_T, _U], b: _T, /) -> _U: ... +@overload +def iadd(a: _SupportsAdd[_T, _U], b: _T, /) -> _U: ... +@overload +def iadd(a: _T, b: _SupportsRAdd[_T, _U], /) -> _U: ... +@overload def iadd(a: Any, b: Any, /) -> Any: ... +@overload +def iand(a: _SupportsIAnd[_T, _U], b: _T, /) -> _U: ... +@overload +def iand(a: _SupportsAnd[_T, _U], b: _T, /) -> _U: ... +@overload +def iand(a: _T, b: _SupportsRAnd[_T, _U], /) -> _U: ... +@overload def iand(a: Any, b: Any, /) -> Any: ... +@overload +def iconcat(a: _SupportsIConcat[_T, _U], b: _T, /) -> _U: ... +@overload +def iconcat(a: _SupportsConcat[_T, _U], b: _T, /) -> _U: ... +@overload +def iconcat(a: _T, b: _SupportsRConcat[_T, _U], /) -> _U: ... +@overload def iconcat(a: Any, b: Any, /) -> Any: ... +@overload +def ifloordiv(a: _SupportsIFloorDiv[_T, _U], b: _T, /) -> _U: ... +@overload +def ifloordiv(a: _SupportsFloorDiv[_T, _U], b: _T, /) -> _U: ... +@overload +def ifloordiv(a: _T, b: _SupportsRFloorDiv[_T, _U], /) -> _U: ... +@overload def ifloordiv(a: Any, b: Any, /) -> Any: ... +@overload +def ilshift(a: _SupportsILShift[_T, _U], b: _T, /) -> _U: ... +@overload +def ilshift(a: _SupportsLShift[_T, _U], b: _T, /) -> _U: ... +@overload +def ilshift(a: _T, b: _SupportsRLShift[_T, _U], /) -> _U: ... +@overload def ilshift(a: Any, b: Any, /) -> Any: ... +@overload +def imod(a: _SupportsIMod[_T, _U], b: _T, /) -> _U: ... +@overload +def imod(a: _SupportsMod[_T, _U], b: _T, /) -> _U: ... +@overload +def imod(a: _T, b: _SupportsRMod[_T, _U], /) -> _U: ... +@overload def imod(a: Any, b: Any, /) -> Any: ... +@overload +def imul(a: _SupportsIMul[_T, _U], b: _T, /) -> _U: ... +@overload +def imul(a: _SupportsMul[_T, _U], b: _T, /) -> _U: ... +@overload +def imul(a: _T, b: _SupportsRMul[_T, _U], /) -> _U: ... +@overload def imul(a: Any, b: Any, /) -> Any: ... +@overload +def imatmul(a: _SupportsIMatMul[_T, _U], b: _T, /) -> _U: ... +@overload +def imatmul(a: _SupportsMatMul[_T, _U], b: _T, /) -> _U: ... +@overload +def imatmul(a: _T, b: _SupportsRMatMul[_T, _U], /) -> _U: ... +@overload def imatmul(a: Any, b: Any, /) -> Any: ... +@overload +def ior(a: _SupportsIOr[_T, _U], b: _T, /) -> _U: ... +@overload +def ior(a: _SupportsOr[_T, _U], b: _T, /) -> _U: ... +@overload +def ior(a: _T, b: _SupportsROr[_T, _U], /) -> _U: ... +@overload def ior(a: Any, b: Any, /) -> Any: ... +@overload +def ipow(a: _SupportsIPow[_T, _U], b: _T, /) -> _U: ... +@overload +def ipow(a: _SupportsPow[_T, _U], b: _T, /) -> _U: ... +@overload +def ipow(a: _T, b: _SupportsRPow[_T, _U], /) -> _U: ... +@overload def ipow(a: Any, b: Any, /) -> Any: ... +@overload +def irshift(a: _SupportsIRShift[_T, _U], b: _T, /) -> _U: ... +@overload +def irshift(a: _SupportsRShift[_T, _U], b: _T, /) -> _U: ... +@overload +def irshift(a: _T, b: _SupportsRRShift[_T, _U], /) -> _U: ... +@overload def irshift(a: Any, b: Any, /) -> Any: ... +@overload +def isub(a: _SupportsISub[_T, _U], b: _T, /) -> _U: ... +@overload +def isub(a: _SupportsSub[_T, _U], b: _T, /) -> _U: ... +@overload +def isub(a: _T, b: _SupportsRSub[_T, _U], /) -> _U: ... +@overload def isub(a: Any, b: Any, /) -> Any: ... +@overload +def itruediv(a: _SupportsITrueDiv[_T, _U], b: _T, /) -> _U: ... +@overload +def itruediv(a: _SupportsTrueDiv[_T, _U], b: _T, /) -> _U: ... +@overload +def itruediv(a: _T, b: _SupportsRTrueDiv[_T, _U], /) -> _U: ... +@overload def itruediv(a: Any, b: Any, /) -> Any: ... +@overload +def ixor(a: _SupportsIXor[_T, _U], b: _T, /) -> _U: ... +@overload +def ixor(a: _SupportsXor[_T, _U], b: _T, /) -> _U: ... +@overload +def ixor(a: _T, b: _SupportsRXor[_T, _U], /) -> _U: ... +@overload def ixor(a: Any, b: Any, /) -> Any: ... if sys.version_info >= (3, 11):