Description
Feature or enhancement
Template
and Interpolation
will once be supported by type-checkers. And it might be worth adding generics support for these types.
Example
from string.templatelib import Template, Interpolation
from urllib.parse import quote_plus
domain = 'example.com'
query = 'python string formatting is too complex'
template = t'https://{domain}?q={query}'
def quote_url(interp: Interpolation[str]) -> str:
return quote_plus(interp.value)
def format_url(template: Template) -> str:
parts = []
for part in template:
match part:
case str() as s: # regular string
parts.append(s)
case Interpolation(str(), expression='query') as interp:
parts.append(quote_url(interp))
case Interpolation(value):
parts.append(value)
return ''.join(parts)
print(format_url(template))
Here we can see that Interpolation[str]
can be useful in users' code.
We can also benefit from generic Template
, where we can make it generic based on exact TypeVarTuple
. Demo:
from typing import Any, TypeVarTuple, Generic, Unpack
Ts = TypeVarTuple('Ts', default=Unpack[tuple[Any, ...]])
class Template(Generic[*Ts]):
def __new__(cls, *args: *Ts) -> Template[*Ts]: ... # type: ignore[empty-body]
class Interpolation: ...
reveal_type(Template('a', 'b', Interpolation(), 'd'))
# Revealed type is "__main__.Template[Literal['a']?, Literal['b']?, __main__.Interpolation, Literal['d']?]"
https://mypy-play.net/?mypy=latest&python=3.13&gist=0dc13b3b926e1efb9783ab9b70d39ceb
This can potentially help type checkers to infer correct Template
type.
Current state
Current typeshed
definitions: https://github.com/python/typeshed/blob/main/stdlib/string/templatelib.pyi
Here's how Template
is defined:
@final
class Template: # TODO: consider making `Template` generic on `TypeVarTuple`
strings: tuple[str, ...]
interpolations: tuple[Interpolation, ...]
def __new__(cls, *args: str | Interpolation) -> Template: ...
def __iter__(self) -> Iterator[str | Interpolation]: ...
def __add__(self, other: Template | str) -> Template: ...
@property
def values(self) -> tuple[Any, ...]: ... # Tuple of interpolation values, which can have any type
Ideally, it should be generic on TypeVarTuple
Here's how Interpolation
is defined:
@final
class Interpolation:
value: Any # TODO: consider making `Interpolation` generic in runtime
expression: str
conversion: Literal["a", "r", "s"] | None
format_spec: str
__match_args__ = ("value", "expression", "conversion", "format_spec")
def __new__(
cls, value: Any, expression: str, conversion: Literal["a", "r", "s"] | None = None, format_spec: str = ""
) -> Interpolation: ...
Ideally, it should be generic on value
. Interpolation[str]
means that value
is str
and Interpolation[tuple[int, int]]
means that Interpolation
has a value
for type tuple[int, int]
Proposal
From runtime's part with only need to add Py_GenericAlias
, that's it.
Is it too late for feature freeze? This is a minor detail, so I hope it is not :)
I have a PR ready.
CC @srittau @JelleZijlstra @lysnikolaou