AI Skill Report Card
Processing Template Strings
Quick Start14 / 15
Python# t-strings (t"...") produce Template objects, NOT strings. # Unlike f-strings, interpolated values stay as live objects until you process them. name = "World" template = t"Hello, {name}!" print(type(template)) # <class 'string.templatelib.Template'> # Iterate over strings and interpolations in order for item in template: print(repr(item)) # 'Hello, ' # Interpolation(value='World', expression='name', conversion=None, format_spec='') # '!' # Convert to a plain string manually (no auto-escaping happens for you) result = "".join( str(item.value) if hasattr(item, "value") else item for item in template ) print(result) # "Hello, World!"
Recommendation▾
Add an example showing error handling when format_spec and conversion conflict (e.g., repr + numeric format spec)
Core Concepts
Template— the object produced byt"...". Iterable, yielding a mix ofstrliteral segments andInterpolationobjects in source order..strings— tuple of the literal string parts (length = interpolations + 1)..interpolations— tuple ofInterpolationobjects..values()— shortcut generator of just the interpolation values.- Supports
+concatenation with otherTemplateorstrobjects (returns aTemplate).
Interpolation— represents one{expr}slot. Fields:.value— the evaluated Python object (already evaluated, not lazy)..expression— source text of the expression, e.g."name"or"x + 1"..conversion—"s","r","a", orNone(from!s/!r/!a)..format_spec— string from:spec,""if absent (raw, not pre-applied).
- Key distinction from f-strings: nothing is stringified automatically. You own the conversion/format/escaping logic — this is the entire point of t-strings (safe templating DSLs).
Workflow13 / 15
Progress:
- Identify whether you need a
Template(t-string, deferred) or an f-string (immediate string) - Write a renderer that walks
template.strings/template.interpolationsor iterates theTemplatedirectly - Apply
conversion(!s/!r/!a) manually if present - Apply
format_specmanually (typically viaformat(value, spec)) - Apply domain-specific escaping (HTML, SQL, shell) to interpolated values only — never to literal segments
- Join everything into the final output
Step-by-step renderer pattern
Pythonfrom string.templatelib import Template, Interpolation def render(template: Template, escape=str) -> str: parts = [] for item in template: if isinstance(item, Interpolation): value = item.value if item.conversion == "s": value = str(value) elif item.conversion == "r": value = repr(value) elif item.conversion == "a": value = ascii(value) if item.format_spec: value = format(value, item.format_spec) parts.append(escape(str(value))) else: parts.append(item) # literal text: NOT escaped return "".join(parts)
Recommendation▾
Include a brief note on Python version requirement (3.14+) for PEP 750 t-strings since this is very new syntax
Examples18 / 20
Example 1: HTML auto-escaping (the canonical t-string use case) Input:
Pythonimport html def html_escape(template): parts = [] for item in template: if isinstance(item, str): parts.append(item) else: parts.append(html.escape(str(item.value))) return "".join(parts) user_input = "<script>alert(1)</script>" page = t"<p>Comment: {user_input}</p>" print(html_escape(page))
Output:
<p>Comment: <script>alert(1)</script></p>
Example 2: Safe SQL parameterization Input:
Pythondef to_sql(template): sql_parts = [] params = [] for item in template: if isinstance(item, str): sql_parts.append(item) else: sql_parts.append("?") params.append(item.value) return "".join(sql_parts), tuple(params) name = "Robert'); DROP TABLE students;--" query = t"SELECT * FROM users WHERE name = {name}" sql, params = to_sql(query) print(sql, params)
Output:
SELECT * FROM users WHERE name = ? ("Robert'); DROP TABLE students;--",)
Example 3: Inspecting format spec and conversion Input:
Pythonvalue = 3.14159 t_obj = t"{value!r:.2f}" interp = t_obj.interpolations[0] print(interp.value, interp.conversion, interp.format_spec)
Output:
3.14159 r .2f
(Note: conversion and format_spec are metadata only — applying both is your responsibility, e.g. format(repr(value), ".2f") would error since repr returns a str; typically you choose one or handle order deliberately.)
Recommendation▾
Show a more complex DSL example combining multiple interpolations with different escaping needs in one template
Best Practices
- Treat
Templateas an AST-like object, not a string — never callstr(template)expecting sensible output (it does not exist as a stringification API; iterate instead). - Centralize escaping in one
render/escapefunction per output context (HTML, SQL, shell, logging) rather than escaping ad hoc. - Preserve literal
.stringssegments verbatim — only transformInterpolation.value. - When building a DSL, validate
.expressiononly for debugging/error messages, not as executable logic —.valueis already evaluated. - Use
.values()when you only need the raw interpolated objects and don't care about surrounding literals or metadata. - Combine
Templateobjects with+to compose reusable partial templates before final rendering.
Common Pitfalls
- Assuming auto-conversion: unlike f-strings,
t"{x}"does not stringifyx—interpolation.valuekeeps its original type (int, list, custom object, etc.). - Escaping literal segments: only escape
Interpolationvalues; escaping the plainstrsegments from.stringswill double-escape or corrupt intended markup. - Ignoring
format_spec/conversion: these are captured as metadata strings, not auto-applied — forgetting to apply them silently drops formatting like.2for!r. - Using t-strings where f-strings suffice: if no deferred processing/escaping is needed, an f-string is simpler and produces a
strdirectly. - Mutating and reusing
Interpolationobjects: they're immutable value carriers; build newTemplate/output rather than trying to patch fields in place.