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

1import typing as t 

2from dataclasses import dataclass 

3from string.templatelib import Interpolation, Template 

4 

5 

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) 

15 

16 

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 ) 

21 

22 

23@dataclass(slots=True, frozen=True) 

24class TemplateRef: 

25 """Reference to a template with indexes for its original interpolations.""" 

26 

27 strings: tuple[str, ...] 

28 """Static string parts of the original string.templatelib.Template""" 

29 

30 i_indexes: tuple[int, ...] 

31 """Indexes of the interpolations in the original string.templatelib.Template""" 

32 

33 @property 

34 def is_literal(self) -> bool: 

35 """Return True if there are no interpolations.""" 

36 return not self.i_indexes 

37 

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] == "" 

42 

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 == ("", "") 

47 

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 ) 

52 

53 @classmethod 

54 def literal(cls, s: str) -> t.Self: 

55 return cls((s,), ()) 

56 

57 @classmethod 

58 def empty(cls) -> t.Self: 

59 return cls.literal("") 

60 

61 @classmethod 

62 def singleton(cls, i_index: int) -> t.Self: 

63 return cls(("", ""), (i_index,)) 

64 

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 ) 

71 

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 )