Coverage for tdom / template_utils.py: 100%
39 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-01-12 16:43 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-01-12 16:43 +0000
1import typing as t
2from dataclasses import dataclass
3from string.templatelib import Interpolation, Template
6def template_from_parts(
7 strings: t.Sequence[str], interpolations: t.Sequence[Interpolation]
8) -> Template:
9 """Construct a template string from the given strings and parts."""
10 assert len(strings) == len(interpolations) + 1, (
11 "TemplateRef must have one more string than interpolation references."
12 )
13 flat = [x for pair in zip(strings, interpolations) for x in pair] + [strings[-1]]
14 return Template(*flat)
17def combine_template_refs(*template_refs: TemplateRef) -> TemplateRef:
18 return TemplateRef.from_naive_template(
19 sum((tr.to_naive_template() for tr in template_refs), t"")
20 )
23@dataclass(slots=True, frozen=True)
24class TemplateRef:
25 """Reference to a template with indexes for its original interpolations."""
27 strings: tuple[str, ...]
28 """Static string parts of the original string.templatelib.Template"""
30 i_indexes: tuple[int, ...]
31 """Indexes of the interpolations in the original string.templatelib.Template"""
33 @property
34 def is_literal(self) -> bool:
35 """Return True if there are no interpolations."""
36 return not self.i_indexes
38 @property
39 def is_empty(self) -> bool:
40 """Return True if the template is empty."""
41 return self.is_literal and self.strings[0] == ""
43 @property
44 def is_singleton(self) -> bool:
45 """Return True if there is exactly one interpolation and no other content."""
46 return self.strings == ("", "")
48 def to_naive_template(self) -> Template:
49 return template_from_parts(
50 self.strings, [Interpolation(i, "", None, "") for i in self.i_indexes]
51 )
53 @classmethod
54 def literal(cls, s: str) -> t.Self:
55 return cls((s,), ())
57 @classmethod
58 def empty(cls) -> t.Self:
59 return cls.literal("")
61 @classmethod
62 def singleton(cls, i_index: int) -> t.Self:
63 return cls(("", ""), (i_index,))
65 @classmethod
66 def from_naive_template(cls, t: Template) -> TemplateRef:
67 return cls(
68 strings=t.strings,
69 i_indexes=tuple([int(ip.value) for ip in t.interpolations]),
70 )
72 def __post_init__(self):
73 if len(self.strings) != len(self.i_indexes) + 1:
74 raise ValueError(
75 "TemplateRef must have one more string than interpolation indexes."
76 )