Skip to content

Consider making Template and Interpolation generic at runtime #133970

Open
@sobolevn

Description

@sobolevn

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

Linked PRs

Metadata

Metadata

Assignees

Labels

3.14bugs and security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)topic-typingtype-featureA feature request or enhancement

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions