explicit typevar constraints¶
motivation¶
in Python, typevar constraints use a tuple literal as the bound annotation:
this is ambiguous: (int, str) looks like a tuple type being used as an upper bound, not
a list of discrete constraints. the distinction only becomes clear from context
basedpython requires an explicit constraints keyword to declare constraints, making the
intent unambiguous
syntax¶
def f[T: constraints (int, str)](x: T) -> T: ...
class Container[T: constraints (int, str, bytes)]: ...
type Alias[T: constraints (int, str)] = list[T]
semantics¶
T: constraints (int, str) declares a constrained typevar — T can only be specialized to
exactly int or str, never a subtype or union
T: (int, str) is a typevar with an upper bound of type tuple[int, str], not constraints
# constraints: T is int or str
def constrained[T: constraints (int, str)](x: T) -> T: ...
# bound: T must be a subtype of tuple[int, str]
def bounded[T: (int, str)](x: T) -> T: ...
minimum two types required¶
a constrained typevar must have at least two constraint types:
polyfill¶
on targets below Python 3.12, constraints (int, str) is polyfilled to a legacy TypeVar
with positional constraint arguments:
# generated for Python < 3.12
from typing import TypeVar
_T = TypeVar("_T", int, str)
def f(x: _T) -> _T: ...
on Python 3.12+, the constraints keyword is stripped and the standard tuple constraint
syntax is emitted: