OpType
OpType is a Python library (v0.17.0) providing building blocks for precise and flexible type hints, offering single-method protocols for dunder methods, exact types that reject sneaky subtypes, and typed operators. It aims to make type-checking more robust and expressive. The library is actively maintained with somewhat frequent minor releases and supports various modern type checkers like mypy, pyright, and pyrefly.
Warnings
- breaking In v0.15.0, several `optype.Can*` generic type parameters (e.g., `CanBytes`, `CanStr`, `CanLen`) intended for literal types were removed. This impacts protocols like `CanBytes`, `CanStr`, `CanIndex`, `CanRepr`, etc.
- breaking The `optype.dataclasses.HasDataclassFields` protocol had its generic type parameter removed in v0.17.0, and `__dataclass_fields__` was turned into a `ClassVar`. This fixes an assignability issue with dataclass instances but requires adjusting code that relied on the generic parameter.
- gotcha When using `optype.numpy`, ensure `numpy-typing-compat` is installed. As of v0.13.0, this dependency is required for robust static typing compatibility with NumPy versions and is automatically installed with the `optype[numpy]` extra.
- gotcha While `optype` protocols are runtime-checkable (e.g., `isinstance('snail', optype.CanAdd)`), it is considered bad practice to use them as base classes for your own implementations. They are pure interfaces, not abstract base classes like `collections.abc` protocols.
Install
-
pip install optype -
pip install optype[numpy]
Imports
- CanAdd
from optype import CanAdd
- CanAbs
from optype import CanAbs
- JustAny
from optype import JustAny
- HasDataclassFields
from optype.dataclasses import HasDataclassFields
- do_add
from optype import do_add
Quickstart
from typing import Literal, TypeVar
from optype import CanMul, CanRMul
Y = TypeVar('Y')
Two: Literal[2] = 2
def twice(x: CanRMul[Literal[2], Y]) -> Y:
return Two * x
# Example usage with different types
print(f"twice(2) = {twice(2)}")
print(f"twice(3.14) = {twice(3.14)}")
print(f"twice('I') = {twice('I')}")
# Fallback for types that implement __mul__ but not __rmul__
def twice_flexible(x: CanRMul[Literal[2], Y] | CanMul[Literal[2], Y]) -> Y:
if isinstance(x, CanRMul):
return Two * x
else:
return x * Two
print(f"twice_flexible(5) = {twice_flexible(5)}")