Coverage for tdom / placeholders.py: 100%
48 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
1from dataclasses import dataclass, field
2import random
3import re
4import string
6from .template_utils import TemplateRef
9def make_placeholder_config() -> PlaceholderConfig:
10 prefix = f"t🐍{''.join(random.choices(string.ascii_lowercase, k=2))}-"
11 suffix = f"-{''.join(random.choices(string.ascii_lowercase, k=2))}🐍t"
12 return PlaceholderConfig(
13 prefix=prefix,
14 suffix=suffix,
15 pattern=re.compile(re.escape(prefix) + r"(\d+)" + re.escape(suffix)),
16 )
19@dataclass(frozen=True)
20class PlaceholderConfig:
21 """String operations for working with a placeholder pattern."""
23 prefix: str
24 suffix: str
25 pattern: re.Pattern
27 def make_placeholder(self, i: int) -> str:
28 """Generate a placeholder for the i-th interpolation."""
29 return f"{self.prefix}{i}{self.suffix}"
31 def match_placeholders(self, s: str) -> list[re.Match[str]]:
32 """Find all placeholders in a string."""
33 return list(self.pattern.finditer(s))
35 def find_placeholders(self, s: str) -> TemplateRef:
36 """
37 Find all placeholders in a string and return a TemplateRef.
39 If no placeholders are found, returns a static TemplateRef.
40 """
41 matches = self.match_placeholders(s)
42 if not matches:
43 return TemplateRef.literal(s)
45 strings: list[str] = []
46 i_indexes: list[int] = []
47 last_index = 0
48 for match in matches:
49 start, end = match.span()
50 strings.append(s[last_index:start])
51 i_indexes.append(int(match[1]))
52 last_index = end
53 strings.append(s[last_index:])
55 return TemplateRef(tuple(strings), tuple(i_indexes))
58@dataclass
59class PlaceholderState:
60 known: set[int] = field(default_factory=set)
61 config: PlaceholderConfig = field(default_factory=make_placeholder_config)
62 """Collection of currently 'known and active' placeholder indexes."""
64 @property
65 def is_empty(self) -> bool:
66 return len(self.known) == 0
68 def add_placeholder(self, index: int) -> str:
69 placeholder = self.config.make_placeholder(index)
70 self.known.add(index)
71 return placeholder
73 def remove_placeholders(self, text: str) -> TemplateRef:
74 """
75 Find all known placeholders in the text and return their indices.
77 If unknown placeholders are found, raises ValueError.
79 If no placeholders are found, returns a static PlaceholderRef.
80 """
81 pt = self.config.find_placeholders(text)
82 for index in pt.i_indexes:
83 if index not in self.known:
84 raise ValueError(f"Unknown placeholder index {index} found in text.")
85 self.known.remove(index)
86 return pt