diff --git a/README.md b/README.md index a622fff7..9aafe052 100644 --- a/README.md +++ b/README.md @@ -364,7 +364,7 @@ This is why these `optype` interfaces don't accept generic type arguments. do_int DoesInt __int__ - CanInt[+R: int = int] + CanInt bool(_) @@ -378,72 +378,28 @@ This is why these `optype` interfaces don't accept generic type arguments. do_bytes DoesBytes __bytes__ - CanBytes[+R: bytes = bytes] + CanBytes str(_) do_str DoesStr __str__ - CanStr[+R: str = str] - - - -> [!NOTE] -> The `Can*` interfaces of the types that can used as `typing.Literal` -> accept an optional type parameter `R`. -> This can be used to indicate a literal return type, -> for surgically precise typing, e.g. `None`, `True`, and `42` are -> instances of `CanBool[Literal[False]]`, `CanInt[Literal[1]]`, and -> `CanStr[Literal['42']]`, respectively. - -These formatting methods are allowed to return instances that are a subtype -of the `str` builtin. The same holds for the `__format__` argument. -So if you're a 10x developer that wants to hack Python's f-strings, but only -if your type hints are spot-on; `optype` is you friend. - - - - - - - - - - - - + - + - - -
operatoroperand
expressionfunctiontypemethodtypeCanStr
repr(_) do_repr DoesRepr __repr__CanRepr[+R:str = str]CanRepr
format(_, x) do_format DoesFormat __format__CanFormat[-T:str = str, +R:str = str]
- -Additionally, `optype` provides protocols for types with (custom) *hash* or -*index* methods: - - - - - - - - - - - - + @@ -460,7 +416,7 @@ Additionally, `optype` provides protocols for types with (custom) *hash* or - +
operatoroperand
expressionfunctiontypemethodtypeCanFormat
hash(_)do_index DoesIndex __index__CanIndex[+R: int = int]CanIndex
@@ -1434,7 +1390,7 @@ Additionally, there is `optype.CanAIterSelf[R]`, with both the do_len DoesLen __len__ - CanLen[+R:int=int] + CanLen @@ -1444,7 +1400,7 @@ Additionally, there is `optype.CanAIterSelf[R]`, with both the do_length_hint DoesLengthHint __length_hint__ - CanLengthHint[+R:int=int] + CanLengthHint _[k] @@ -1491,7 +1447,7 @@ Additionally, there is `optype.CanAIterSelf[R]`, with both the __reversed__ CanReversed[+R], or
- CanSequence[-I, +V, +N=int] + CanSequence[-I, +V] @@ -1526,7 +1482,7 @@ specific and flexible `collections.abc.Sequence[V]`. do_getattr DoesGetattr __getattr__ - CanGetattr[-K:str=str, +V=object] + CanGetattr[+V=object] @@ -1536,7 +1492,7 @@ specific and flexible `collections.abc.Sequence[V]`. do_setattr DoesSetattr __setattr__ - CanSetattr[-K:str=str, -V=object] + CanSetattr[-V=object] @@ -1546,14 +1502,14 @@ specific and flexible `collections.abc.Sequence[V]`. do_delattr DoesDelattr __delattr__ - CanDelattr[-K:str=str] + CanDelattr dir(_) do_dir DoesDir __dir__ - CanDir[+R:CanIter[CanIterSelf[str]]] + CanDir[+R: Iterable[str]] @@ -1683,7 +1639,7 @@ Interfaces for [descriptors](https://docs.python.org/3/howto/descriptor.html). class T: d = _ __set_name__ - CanSetName[-T, -N: str = str] + CanSetName[-T] @@ -1704,7 +1660,7 @@ Interfaces for emulating buffer types using the [buffer protocol][BP]. v = memoryview(_) __buffer__ - CanBuffer[-T: int = int] + CanBuffer del v diff --git a/optype/_core/_can.py b/optype/_core/_can.py index 911d4c76..4c7834c3 100644 --- a/optype/_core/_can.py +++ b/optype/_core/_can.py @@ -1,10 +1,7 @@ -# mypy: disable-error-code="override" -# ruff: noqa: PYI034 - import sys import types -from collections.abc import Generator -from typing import Never, Protocol, Self, SupportsIndex, TypeAlias, overload +from collections.abc import Generator, Iterable +from typing import Any, Never, Protocol, Self, SupportsIndex, TypeAlias, overload if sys.version_info >= (3, 13): from typing import ParamSpec, TypeVar, override, runtime_checkable @@ -203,8 +200,8 @@ def __dir__() -> list[str]: _Tss = ParamSpec("_Tss", default=...) _T = TypeVar("_T") -_T_contra = TypeVar("_T_contra", contravariant=True) _T_co = TypeVar("_T_co", covariant=True) +_T_contra = TypeVar("_T_contra", contravariant=True) _TT_co = TypeVar("_TT_co", covariant=True, default=_T_contra) _K_contra = TypeVar("_K_contra", contravariant=True) @@ -212,12 +209,7 @@ def __dir__() -> list[str]: _V_co = TypeVar("_V_co", covariant=True) _VV_co = TypeVar("_VV_co", default=_V_co, covariant=True) -_BoolT_co = TypeVar("_BoolT_co", bound=bool, default=bool, covariant=True) -_IntT_contra = TypeVar("_IntT_contra", bound=int, default=int, contravariant=True) -_IntT_co = TypeVar("_IntT_co", bound=int, default=int, covariant=True) -_BytesT_co = TypeVar("_BytesT_co", bound=bytes, default=bytes, covariant=True) -_StrT_contra = TypeVar("_StrT_contra", bound=str, default=str, contravariant=True) -_StrT_co = TypeVar("_StrT_co", bound=str, default=str, covariant=True) +_BoolT_co = TypeVar("_BoolT_co", default=bool, covariant=True) _ExcT = TypeVar("_ExcT", bound=BaseException) _T_object_contra = TypeVar("_T_object_contra", contravariant=True, default=object) @@ -230,6 +222,14 @@ def __dir__() -> list[str]: _T_Never_contra = TypeVar("_T_Never_contra", default=Never, contravariant=True) _T_Never_co = TypeVar("_T_Never_co", default=Never, covariant=True) +_IterT_str_co = TypeVar( + "_IterT_str_co", + # we use `Any` instead of `object` to side-step LSP errors in `CanDir` + bound=Iterable[Any], + default=Iterable[str], + covariant=True, +) + # we can't use `CanIndex` here, because of a recent regression in pyright 1.1.392 _IndexT_contra = TypeVar( "_IndexT_contra", @@ -257,8 +257,8 @@ def __bool__(self, /) -> _BoolT_co: ... @runtime_checkable -class CanInt(Protocol[_IntT_co]): - def __int__(self, /) -> _IntT_co: ... +class CanInt(Protocol): + def __int__(self, /) -> int: ... @runtime_checkable @@ -272,26 +272,14 @@ def __complex__(self, /) -> complex: ... @runtime_checkable -class CanBytes(Protocol[_BytesT_co]): - """ - The `__bytes__: (CanBytes[Y]) -> Y` method is *co*variant on `+Y`. - So if `__bytes__` returns an instance of a custom `bytes` subtype - `Y <: bytes`, then `bytes()` will also return `Y` (i.e. no upcasting). - """ - - def __bytes__(self, /) -> _BytesT_co: ... +class CanBytes(Protocol): + def __bytes__(self, /) -> bytes: ... @runtime_checkable -class CanStr(Protocol[_StrT_co]): - """ - Each `object` has a *co*variant `__str__: (CanStr[Y=str]) -> Y` method on - `+Y`. That means that if `__str__()` returns an instance of a custom `str` - subtype `Y <: str`, then `str()` will also return `Y` (i.e. no upcasting). - """ - +class CanStr(Protocol): @override - def __str__(self, /) -> _StrT_co: ... + def __str__(self, /) -> str: ... # Object representation @@ -304,32 +292,20 @@ def __hash__(self, /) -> int: ... @runtime_checkable -class CanIndex(Protocol[_IntT_co]): - def __index__(self, /) -> _IntT_co: ... +class CanIndex(Protocol): + def __index__(self, /) -> int: ... @runtime_checkable -class CanRepr(Protocol[_StrT_co]): - """ - Each `object` has a *co*variant `__repr__: (CanRepr[Y=str]) -> Y` method. - That means that if `__repr__` returns an instance of a custom `str` - subtype `Y <: str`, then `repr()` will also return `Y` (i.e. no upcasting). - """ - +class CanRepr(Protocol): @override - def __repr__(self, /) -> _StrT_co: ... + def __repr__(self, /) -> str: ... @runtime_checkable -class CanFormat(Protocol[_StrT_contra, _StrT_co]): - """ - Each `object` has a `__format__: (CanFormat[X, Y], X) -> Y` method, with - `-X` *contra*variant, and `+Y` *co*variant. Both `X` and `Y` can be `str` - or `str` subtypes. Note that `format()` *does not* upcast `Y` to `str`. - """ - +class CanFormat(Protocol): @override - def __format__(self, fmt: _StrT_contra, /) -> _StrT_co: ... # pyright: ignore[reportIncompatibleMethodOverride] # pyrefly: ignore[bad-override] + def __format__(self, format_spec: str, /) -> str: ... # Iteration @@ -408,7 +384,7 @@ class CanEq(Protocol[_T_object_contra, _T_bool_co]): # noqa: PLW1641 """ @override - def __eq__(self, rhs: _T_object_contra, /) -> _T_bool_co: ... # pyright:ignore[reportIncompatibleMethodOverride] # pyrefly: ignore[bad-override] + def __eq__(self, rhs: _T_object_contra, /) -> _T_bool_co: ... # type: ignore[override] # pyright:ignore[reportIncompatibleMethodOverride] # pyrefly: ignore[bad-override] @runtime_checkable @@ -419,7 +395,7 @@ class CanNe(Protocol[_T_object_contra, _T_bool_co]): """ @override - def __ne__(self, rhs: _T_object_contra, /) -> _T_bool_co: ... # pyright:ignore[reportIncompatibleMethodOverride] # pyrefly: ignore[bad-override] + def __ne__(self, rhs: _T_object_contra, /) -> _T_bool_co: ... # type: ignore[override] # pyright:ignore[reportIncompatibleMethodOverride] # pyrefly: ignore[bad-override] @runtime_checkable @@ -447,6 +423,8 @@ def __ge__(self, rhs: _T_object_contra, /) -> _T_bool_co: ... @runtime_checkable class CanCall(Protocol[_Tss, _T_object_co]): + """Equivalent to `typing.Callable` in theory, but not always in practice.""" + def __call__(self, /, *args: _Tss.args, **kwargs: _Tss.kwargs) -> _T_object_co: ... @@ -454,49 +432,36 @@ def __call__(self, /, *args: _Tss.args, **kwargs: _Tss.kwargs) -> _T_object_co: @runtime_checkable -class CanGetattr(Protocol[_StrT_contra, _T_object_co]): - def __getattr__(self, name: _StrT_contra, /) -> _T_object_co: ... # type: ignore[misc] +class CanGetattr(Protocol[_T_object_co]): + def __getattr__(self, name: str, /) -> _T_object_co: ... @runtime_checkable -class CanGetattribute(Protocol[_StrT_contra, _T_object_co]): +class CanGetattribute(Protocol[_T_object_co]): """Note that `isinstance(x, CanGetattribute)` is always `True`.""" @override - def __getattribute__(self, name: _StrT_contra, /) -> _T_object_co: ... # type: ignore[misc] # pyright: ignore[reportIncompatibleMethodOverride] + def __getattribute__(self, name: str, /) -> _T_object_co: ... @runtime_checkable -class CanSetattr(Protocol[_StrT_contra, _T_object_contra]): +class CanSetattr(Protocol[_T_object_contra]): """Note that `isinstance(x, CanSetattr)` is always true.""" @override - def __setattr__( # type: ignore[misc] # pyright: ignore[reportIncompatibleMethodOverride] - self, - name: _StrT_contra, - value: _T_object_contra, - /, - ) -> _Ignored: ... + def __setattr__(self, name: str, value: _T_object_contra, /) -> _Ignored: ... # type: ignore[misc, override] # pyright: ignore[reportIncompatibleMethodOverride] @runtime_checkable -class CanDelattr(Protocol[_StrT_contra]): +class CanDelattr(Protocol): @override - def __delattr__(self, name: _StrT_contra, /) -> _Ignored: ... # pyright: ignore[reportIncompatibleMethodOverride] # pyrefly: ignore[bad-override] - - -_AnyStrIterT_co = TypeVar( - "_AnyStrIterT_co", - bound=CanIter[CanNext[object]], - covariant=True, - default=CanIter[CanIterSelf[str]], -) + def __delattr__(self, name: str, /) -> _Ignored: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] # pyrefly: ignore[bad-override] @runtime_checkable -class CanDir(Protocol[_AnyStrIterT_co]): +class CanDir(Protocol[_IterT_str_co]): @override - def __dir__(self, /) -> _AnyStrIterT_co: ... # pyright: ignore[reportIncompatibleMethodOverride] + def __dir__(self, /) -> _IterT_str_co: ... # Descriptors @@ -531,21 +496,21 @@ def __delete__(self, owner: _T_contra, /) -> _Ignored: ... @runtime_checkable -class CanSetName(Protocol[_T_contra, _StrT_contra]): - def __set_name__(self, cls: type[_T_contra], name: _StrT_contra, /) -> _Ignored: ... +class CanSetName(Protocol[_T_contra]): + def __set_name__(self, cls: type[_T_contra], name: str, /) -> _Ignored: ... # Collection type operands. @runtime_checkable -class CanLen(Protocol[_IntT_co]): - def __len__(self, /) -> _IntT_co: ... +class CanLen(Protocol): + def __len__(self, /) -> int: ... @runtime_checkable -class CanLengthHint(Protocol[_IntT_co]): - def __length_hint__(self, /) -> _IntT_co: ... +class CanLengthHint(Protocol): + def __length_hint__(self, /) -> int: ... @runtime_checkable @@ -571,10 +536,10 @@ def __reversed__(self, /) -> _T_co: ... @runtime_checkable -class CanContains(Protocol[_T_object_contra, _BoolT_co]): +class CanContains(Protocol[_T_object_contra]): # usually the key is required to also be a hashable object, but this # isn't strictly required - def __contains__(self, key: _T_object_contra, /) -> _BoolT_co: ... + def __contains__(self, key: _T_object_contra, /) -> bool: ... @runtime_checkable @@ -593,8 +558,8 @@ class CanGetMissing( @runtime_checkable class CanSequence( CanGetitem[_IndexT_contra, _V_co], - CanLen[_IntT_co], - Protocol[_IndexT_contra, _V_co, _IntT_co], + CanLen, + Protocol[_IndexT_contra, _V_co], ): """ A sequence is an object with a __len__ method and a @@ -1130,6 +1095,8 @@ def __ror__(self, lhs: _T_contra, /) -> Self: ... ### # Augmented arithmetic operands +# ruff: noqa: PYI034 + # __iadd__ @@ -1572,11 +1539,7 @@ def __exit__( # noqa: PYI036 @runtime_checkable -class CanWith( - CanEnter[_T_co], - CanExit[_T_None_co], - Protocol[_T_co, _T_None_co], -): ... +class CanWith(CanEnter[_T_co], CanExit[_T_None_co], Protocol[_T_co, _T_None_co]): ... @runtime_checkable @@ -1630,8 +1593,8 @@ class CanAsyncWithSelf(CanAEnterSelf, CanAExit[_T_None_co], Protocol[_T_None_co] @runtime_checkable -class CanBuffer(Protocol[_IntT_contra]): - def __buffer__(self, buffer: _IntT_contra, /) -> memoryview: ... +class CanBuffer(Protocol): + def __buffer__(self, buffer: int, /) -> memoryview: ... @runtime_checkable diff --git a/optype/_core/_do.py b/optype/_core/_do.py index 3b874657..de4c6c24 100644 --- a/optype/_core/_do.py +++ b/optype/_core/_do.py @@ -2,7 +2,7 @@ import math import operator as _o -from typing import Final, Literal, TypeVar, cast, overload +from typing import Final, TypeVar, cast, overload import optype._core._can as _c import optype._core._does as _d @@ -101,48 +101,48 @@ def __dir__() -> list[str]: # type conversion -do_bool: Final = cast("_d.DoesBool", bool) -do_int: Final = cast("_d.DoesInt", int) -do_float: Final = cast("_d.DoesFloat", float) -do_complex: Final = cast("_d.DoesComplex", complex) -do_bytes: Final = cast("_d.DoesBytes", bytes) -do_str: Final = cast("_d.DoesStr", str) +do_bool: Final[_d.DoesBool] = cast("_d.DoesBool", bool) +do_int: Final[_d.DoesInt] = cast("_d.DoesInt", int) +do_float: Final[_d.DoesFloat] = cast("_d.DoesFloat", float) +do_complex: Final[_d.DoesComplex] = cast("_d.DoesComplex", complex) +do_bytes: Final[_d.DoesBytes] = cast("_d.DoesBytes", bytes) +do_str: Final[_d.DoesStr] = cast("_d.DoesStr", str) # formatting -do_repr: Final = cast("_d.DoesRepr", repr) -do_format: Final = cast("_d.DoesFormat", format) +do_repr: Final[_d.DoesRepr] = repr +do_format: Final[_d.DoesFormat] = format # iteration -do_next: Final = next -do_iter: Final = cast("_d.DoesIter", iter) +do_next: Final[_d.DoesNext] = next +do_iter: Final[_d.DoesIter] = cast("_d.DoesIter", iter) # async iteration -do_anext: Final = cast("_d.DoesANext", anext) -do_aiter: Final = cast("_d.DoesAIter", aiter) +do_anext: Final[_d.DoesANext] = cast("_d.DoesANext", anext) +do_aiter: Final[_d.DoesAIter] = cast("_d.DoesAIter", aiter) # rich comparison -do_lt: Final = cast("_d.DoesLt", _o.lt) -do_le: Final = cast("_d.DoesLe", _o.le) -do_eq: Final = cast("_d.DoesEq", _o.eq) -do_ne: Final = cast("_d.DoesNe", _o.ne) -do_gt: Final = cast("_d.DoesGt", _o.gt) -do_ge: Final = cast("_d.DoesGe", _o.ge) +do_lt: Final[_d.DoesLt] = cast("_d.DoesLt", _o.lt) +do_le: Final[_d.DoesLe] = cast("_d.DoesLe", _o.le) +do_eq: Final[_d.DoesEq] = cast("_d.DoesEq", _o.eq) +do_ne: Final[_d.DoesNe] = cast("_d.DoesNe", _o.ne) +do_gt: Final[_d.DoesGt] = cast("_d.DoesGt", _o.gt) +do_ge: Final[_d.DoesGe] = cast("_d.DoesGe", _o.ge) # attributes -do_getattr: Final = cast("_d.DoesGetattr", getattr) -do_setattr: Final = cast("_d.DoesSetattr", setattr) -do_delattr: Final = delattr -do_dir: Final = cast("_d.DoesDir", dir) +do_getattr: Final[_d.DoesGetattr] = cast("_d.DoesGetattr", getattr) +do_setattr: Final[_d.DoesSetattr] = cast("_d.DoesSetattr", setattr) +do_delattr: Final[_d.DoesDelattr] = delattr +do_dir: Final[_d.DoesDir] = cast("_d.DoesDir", dir) # callables -do_call: Final = _o.call +do_call: Final[_d.DoesCall] = _o.call # pyrefly: ignore[bad-assignment] # containers and sequences -do_len: Final = cast("_d.DoesLen", len) -do_length_hint: Final = cast("_d.DoesLengthHint", _o.length_hint) +do_len: Final[_d.DoesLen] = len +do_length_hint: Final[_d.DoesLengthHint] = cast("_d.DoesLengthHint", _o.length_hint) # `operator.getitem` isn't used, because it has an (unreasonably loose, and @@ -181,36 +181,33 @@ def do_missing(obj: _c.CanMissing[_KT, _DT], key: _KT, /) -> _DT: return obj.__missing__(key) -_BoolT = TypeVar("_BoolT", Literal[False], Literal[True], bool) - - # `operator.contains` cannot be used, as it incorrectly requires `key` # to be exactly of type `object`, so that it only accepts `object()`... -def do_contains(obj: _c.CanContains[_KT, _BoolT], key: _KT, /) -> _BoolT: +def do_contains(obj: _c.CanContains[_KT], key: _KT, /) -> bool: """Same as `key in obj`.""" - return cast("_BoolT", key in obj) # type: ignore[redundant-cast] + return key in obj # `builtins.reversed` is annotated incorrectly within typeshed: # https://github.com/python/typeshed/issues/11645 -do_reversed: Final = cast("_d.DoesReversed", reversed) +do_reversed: Final[_d.DoesReversed] = cast("_d.DoesReversed", reversed) # infix ops -do_add: Final = cast("_d.DoesAdd", _o.add) -do_sub: Final = cast("_d.DoesSub", _o.sub) -do_mul: Final = cast("_d.DoesMul", _o.mul) -do_matmul: Final = cast("_d.DoesMatmul", _o.matmul) -do_truediv: Final = cast("_d.DoesTruediv", _o.truediv) -do_floordiv: Final = cast("_d.DoesFloordiv", _o.floordiv) -do_mod: Final = cast("_d.DoesMod", _o.mod) -do_divmod: Final = divmod -do_pow: Final = cast("_d.DoesPow", pow) -do_lshift: Final = cast("_d.DoesLshift", _o.lshift) -do_rshift: Final = cast("_d.DoesRshift", _o.rshift) -do_and: Final = cast("_d.DoesAnd", _o.and_) -do_xor: Final = cast("_d.DoesXor", _o.xor) -do_or: Final = cast("_d.DoesOr", _o.or_) +do_add: Final[_d.DoesAdd] = cast("_d.DoesAdd", _o.add) +do_sub: Final[_d.DoesSub] = cast("_d.DoesSub", _o.sub) +do_mul: Final[_d.DoesMul] = cast("_d.DoesMul", _o.mul) +do_matmul: Final[_d.DoesMatmul] = cast("_d.DoesMatmul", _o.matmul) +do_truediv: Final[_d.DoesTruediv] = cast("_d.DoesTruediv", _o.truediv) +do_floordiv: Final[_d.DoesFloordiv] = cast("_d.DoesFloordiv", _o.floordiv) +do_mod: Final[_d.DoesMod] = cast("_d.DoesMod", _o.mod) +do_divmod: Final[_d.DoesDivmod] = divmod +do_pow: Final[_d.DoesPow] = cast("_d.DoesPow", pow) +do_lshift: Final[_d.DoesLshift] = cast("_d.DoesLshift", _o.lshift) +do_rshift: Final[_d.DoesRshift] = cast("_d.DoesRshift", _o.rshift) +do_and: Final[_d.DoesAnd] = cast("_d.DoesAnd", _o.and_) +do_xor: Final[_d.DoesXor] = cast("_d.DoesXor", _o.xor) +do_or: Final[_d.DoesOr] = cast("_d.DoesOr", _o.or_) # reflected ops @@ -293,33 +290,33 @@ def do_ror(a: _c.CanROr[_LeftT, _OutT], b: _LeftT, /) -> _OutT: # augmented ops -do_iadd: Final = cast("_d.DoesIAdd", _o.iadd) -do_isub: Final = cast("_d.DoesISub", _o.isub) -do_imul: Final = cast("_d.DoesIMul", _o.imul) -do_imatmul: Final = cast("_d.DoesIMatmul", _o.imatmul) -do_itruediv: Final = cast("_d.DoesITruediv", _o.itruediv) -do_ifloordiv: Final = cast("_d.DoesIFloordiv", _o.ifloordiv) -do_imod: Final = cast("_d.DoesIMod", _o.imod) -do_ipow: Final = cast("_d.DoesIPow", _o.ipow) -do_ilshift: Final = cast("_d.DoesILshift", _o.ilshift) -do_irshift: Final = cast("_d.DoesIRshift", _o.irshift) -do_iand: Final = cast("_d.DoesIAnd", _o.iand) -do_ixor: Final = cast("_d.DoesIXor", _o.ixor) -do_ior: Final = cast("_d.DoesIOr", _o.ior) +do_iadd: Final[_d.DoesIAdd] = cast("_d.DoesIAdd", _o.iadd) +do_isub: Final[_d.DoesISub] = cast("_d.DoesISub", _o.isub) +do_imul: Final[_d.DoesIMul] = cast("_d.DoesIMul", _o.imul) +do_imatmul: Final[_d.DoesIMatmul] = cast("_d.DoesIMatmul", _o.imatmul) +do_itruediv: Final[_d.DoesITruediv] = cast("_d.DoesITruediv", _o.itruediv) +do_ifloordiv: Final[_d.DoesIFloordiv] = cast("_d.DoesIFloordiv", _o.ifloordiv) +do_imod: Final[_d.DoesIMod] = cast("_d.DoesIMod", _o.imod) +do_ipow: Final[_d.DoesIPow] = cast("_d.DoesIPow", _o.ipow) +do_ilshift: Final[_d.DoesILshift] = cast("_d.DoesILshift", _o.ilshift) +do_irshift: Final[_d.DoesIRshift] = cast("_d.DoesIRshift", _o.irshift) +do_iand: Final[_d.DoesIAnd] = cast("_d.DoesIAnd", _o.iand) +do_ixor: Final[_d.DoesIXor] = cast("_d.DoesIXor", _o.ixor) +do_ior: Final[_d.DoesIOr] = cast("_d.DoesIOr", _o.ior) # unary ops -do_neg: Final = _o.neg -do_pos: Final = _o.pos -do_abs: Final = abs -do_invert: Final = _o.invert +do_neg: Final[_d.DoesNeg] = _o.neg +do_pos: Final[_d.DoesPos] = _o.pos +do_abs: Final[_d.DoesAbs] = abs +do_invert: Final[_d.DoesInvert] = _o.invert # fingerprinting -do_hash: Final = hash -do_index: Final = cast("_d.DoesIndex", _o.index) +do_hash: Final[_d.DoesHash] = hash +do_index: Final[_d.DoesIndex] = _o.index # rounding # (the typeshed stubs for `round` are unnecessarily strict) -do_round: Final = cast("_d.DoesRound", round) -do_trunc: Final = math.trunc -do_floor: Final = cast("_d.DoesFloor", math.floor) -do_ceil: Final = cast("_d.DoesCeil", math.ceil) +do_round: Final[_d.DoesRound] = cast("_d.DoesRound", round) +do_trunc: Final[_d.DoesTrunc] = math.trunc +do_floor: Final[_d.DoesFloor] = cast("_d.DoesFloor", math.floor) +do_ceil: Final[_d.DoesCeil] = cast("_d.DoesCeil", math.ceil) diff --git a/optype/_core/_does.py b/optype/_core/_does.py index 8e79bd66..96f5a6a0 100644 --- a/optype/_core/_does.py +++ b/optype/_core/_does.py @@ -1,6 +1,6 @@ import sys from collections.abc import AsyncIterator, Callable, Iterable, Iterator -from typing import Literal, Protocol, TypeAlias, overload +from typing import Protocol, overload if sys.version_info >= (3, 13): from typing import ParamSpec, TypeVar @@ -101,17 +101,6 @@ def __dir__() -> list[str]: ### -_JustFalse: TypeAlias = Literal[False] -_JustTrue: TypeAlias = Literal[True] -_Just0: TypeAlias = Literal[0] -# cannot use `optype.typing.LiteralByte` here, as it starts at 0 -_PosInt: TypeAlias = Literal[ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, - 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, -] # fmt: skip - _Tss = ParamSpec("_Tss") _KeyT = TypeVar("_KeyT") @@ -128,10 +117,6 @@ def __dir__() -> list[str]: _AIteratorT = TypeVar("_AIteratorT", bound=AsyncIterator[object] | _c.CanANext[object]) _IterT = TypeVar("_IterT", bound=Iterable[object]) _BoolT = TypeVar("_BoolT", bound=bool) -_IntT = TypeVar("_IntT", bound=int) -_StrT = TypeVar("_StrT", bound=str) -_FormatT = TypeVar("_FormatT", bound=str) -_BytesT = TypeVar("_BytesT", bound=bytes) ### @@ -205,42 +190,35 @@ def __call__(self, obj: _c.CanFloat, /) -> float: ... class DoesInt(Protocol): - def __call__(self, obj: _c.CanInt[_IntT], /) -> _IntT: ... + def __call__(self, obj: _c.CanInt, /) -> int: ... class DoesBool(Protocol): @overload def __call__(self, obj: _c.CanBool[_BoolT], /) -> _BoolT: ... @overload - def __call__(self, obj: _c.CanLen[_Just0], /) -> _JustFalse: ... - @overload - def __call__(self, obj: _c.CanLen[_PosInt], /) -> _JustTrue: ... + def __call__(self, obj: _c.CanLen, /) -> bool: ... @overload def __call__(self, obj: object, /) -> bool: ... class DoesStr(Protocol): - def __call__(self, obj: _c.CanStr[_StrT], /) -> _StrT: ... + def __call__(self, obj: _c.CanStr, /) -> str: ... class DoesBytes(Protocol): - def __call__(self, obj: _c.CanBytes[_BytesT], /) -> _BytesT: ... + def __call__(self, obj: _c.CanBytes, /) -> bytes: ... # formatting class DoesRepr(Protocol): - def __call__(self, obj: _c.CanRepr[_StrT], /) -> _StrT: ... + def __call__(self, obj: _c.CanRepr, /) -> str: ... class DoesFormat(Protocol): - def __call__( - self, - obj: _c.CanFormat[_FormatT, _StrT], - format_spec: _FormatT = ..., - /, - ) -> _StrT: ... + def __call__(self, obj: _c.CanFormat, format_spec: str = ..., /) -> str: ... # rich comparison @@ -293,27 +271,22 @@ def __call__(self, lhs: _LeftT, rhs: _c.CanLe[_LeftT, _OutT], /) -> _OutT: ... class DoesGetattr(Protocol): @overload - def __call__(self, obj: _c.CanGetattr[_StrT, _AttrT], name: _StrT, /) -> _AttrT: ... + def __call__(self, obj: _c.CanGetattr[_AttrT], name: str, /) -> _AttrT: ... @overload - def __call__( - self, - obj: _c.CanGetattribute[_StrT, _AttrT], - name: _StrT, - /, - ) -> _AttrT: ... + def __call__(self, obj: _c.CanGetattribute[_AttrT], name: str, /) -> _AttrT: ... @overload def __call__( self, - obj: _c.CanGetattr[_StrT, _AttrT], - name: _StrT, + obj: _c.CanGetattr[_AttrT], + name: str, default: _DefaultT, /, ) -> _AttrT | _DefaultT: ... @overload def __call__( self, - obj: _c.CanGetattribute[_StrT, _AttrT], - name: _StrT, + obj: _c.CanGetattribute[_AttrT], + name: str, default: _DefaultT, /, ) -> _AttrT | _DefaultT: ... @@ -322,15 +295,15 @@ def __call__( class DoesSetattr(Protocol): def __call__( self, - obj: _c.CanSetattr[_StrT, _AttrT], - name: _StrT, + obj: _c.CanSetattr[_AttrT], + name: str, value: _AttrT, /, ) -> None: ... class DoesDelattr(Protocol): - def __call__(self, obj: _c.CanDelattr[_StrT], name: _StrT, /) -> None: ... + def __call__(self, obj: _c.CanDelattr, name: str, /) -> None: ... class DoesDir(Protocol): @@ -357,11 +330,11 @@ def __call__( class DoesLen(Protocol): - def __call__(self, obj: _c.CanLen[_IntT], /) -> _IntT: ... + def __call__(self, obj: _c.CanLen, /) -> int: ... class DoesLengthHint(Protocol): - def __call__(self, obj: _c.CanLengthHint[_IntT], /) -> _IntT: ... + def __call__(self, obj: _c.CanLengthHint, /) -> int: ... class DoesGetitem(Protocol): @@ -397,7 +370,7 @@ def __call__( class DoesContains(Protocol): - def __call__(self, obj: _c.CanContains[_KeyT, _BoolT], key: _KeyT, /) -> _BoolT: ... + def __call__(self, obj: _c.CanContains[_KeyT], key: _KeyT, /) -> bool: ... class DoesReversed(Protocol): @@ -789,7 +762,7 @@ def __call__(self, obj: _c.CanInvert[_OutT], /) -> _OutT: ... class DoesIndex(Protocol): - def __call__(self, obj: _c.CanIndex[_IntT], /) -> _IntT: ... + def __call__(self, obj: _c.CanIndex, /) -> int: ... class DoesHash(Protocol): diff --git a/optype/typing.py b/optype/typing.py index 69b818ee..866ec93e 100644 --- a/optype/typing.py +++ b/optype/typing.py @@ -50,7 +50,6 @@ def __dir__() -> tuple[str, ...]: ### _T = TypeVar("_T") -_IntT = TypeVar("_IntT", bound=int, default=int) _ValueT = TypeVar("_ValueT", default=object) @@ -101,7 +100,7 @@ class JustComplex(_just.JustComplex, Protocol, just=complex): ... # type: ignor # Anything that can *always* be converted to an `int` / `float` / `complex` -AnyInt: TypeAlias = _IntT | _c.CanInt[_IntT] | _c.CanIndex[_IntT] +AnyInt: TypeAlias = int | _c.CanInt | _c.CanIndex AnyFloat: TypeAlias = _c.CanFloat | _c.CanIndex AnyComplex: TypeAlias = _c.CanComplex | _c.CanFloat | _c.CanIndex diff --git a/pyproject.toml b/pyproject.toml index e3d96c2a..8626ffd5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ module-root = "" [project] name = "optype" description = "Building Blocks for Precise & Flexible Type Hints" -version = "0.14.1.dev0" +version = "0.15.0.dev0" authors = [{ name = "Joren Hammudoglu", email = "jhammudoglu@gmail.com" }] license = "BSD-3-Clause" license-files = ["LICENSE"] diff --git a/tests/core/test_do.py b/tests/core/test_do.py index 9b624ea8..78534a8a 100644 --- a/tests/core/test_do.py +++ b/tests/core/test_do.py @@ -16,7 +16,7 @@ def test_static() -> None: _do_setitem: _does.DoesSetitem = _do.do_setitem _do_delitem: _does.DoesDelitem = _do.do_delitem _do_missing: _does.DoesMissing = _do.do_missing - _do_contains: _does.DoesContains = _do.do_contains # type: ignore[assignment] + _do_contains: _does.DoesContains = _do.do_contains _do_radd: _does.DoesRAdd = _do.do_radd _do_rsub: _does.DoesRSub = _do.do_rsub diff --git a/tests/test_typing.py b/tests/test_typing.py index 65fc2c9e..139ac682 100644 --- a/tests/test_typing.py +++ b/tests/test_typing.py @@ -1,6 +1,6 @@ import enum import sys -from typing import Generic, Literal, final +from typing import Generic, final if sys.version_info >= (3, 13): from typing import TypeVar @@ -73,9 +73,9 @@ def __getitem__(self, index: int, /) -> _V_co: def test_any_int() -> None: p_bool: opt.AnyInt = True - p_int: opt.AnyInt[Literal[2]] = 2 - p_index: opt.AnyInt[Literal[3]] = SimpleIndex(3) - p_int_like: opt.AnyInt[Literal[4]] = SimpleInt(4) + p_int: opt.AnyInt = 2 + p_index: opt.AnyInt = SimpleIndex(3) + p_int_like: opt.AnyInt = SimpleInt(4) n_complex: opt.AnyInt = 5j # type: ignore[assignment] # pyright: ignore[reportAssignmentType] n_str: opt.AnyInt = "6" # type: ignore[assignment] # pyright: ignore[reportAssignmentType] diff --git a/uv.lock b/uv.lock index 9970c691..6b10e81b 100644 --- a/uv.lock +++ b/uv.lock @@ -313,7 +313,7 @@ wheels = [ [[package]] name = "optype" -version = "0.14.1.dev0" +version = "0.15.0.dev0" source = { editable = "." } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.13'" },