We are working on a set of PEPs (spec, tutorial) here for tagstrings, which support custom prefixes - GitHub - jimbaker/tagstr: This repo contains an issue tracker, examples, and early work related to PEP 999: Tag Strings - and there’s a corresponding branch of CPython that @guido wrote that implements most of the functionality that is discussed - I am working in a branch · Issue #1 · jimbaker/tagstr · GitHub, which can be readily be updated. I should get back into gear on this work and just get it done .
First, tagstrings are a Pythonic version of JavaScript’s tagged template literals. So this is the way to think about it: tags = custom prefixes. In a nutshell, one can write tagstrings in a target DSL - html`, shell, sql, etc - and the interpolations are properly managed for that target (eg quoted). In particular, interpolations can be nested recursively; and the behavior is the same as with f-strings in that lexical scope is respected. (For example, this also opens up being able to work with numexpr, GitHub - pydata/numexpr: Fast numerical array expression evaluator for Python, NumPy, PyTables, pandas, bcolz and more, for example, which currently uses dynamic scope to resolve expressions.)
Examples:
# NOTE: tags are passed raw strings, so the grep works here
run(sh"find {path} -print | grep '\.py$'", shell=True)
or
table_name = 'lang'
name = 'C'
date = 1972
with sqlite3.connect(':memory:') as conn:
cur = conn.cursor()
cur.execute(*sql'create table {Identifier(table_name)} (name, first_appeared)')
Some additional things to know are that interpolations are “lambda wrapped” with a no-arg function; and the interface provided seems to be nicely Pythonic - it’s easy IMHO to write your own tag functions to work with your target DSL. Example for implementing the fl
tag, a lazy version of f-strings, assuming some imports and definitions like Thunk
(see tagstr/fl.py at main · jimbaker/tagstr · GitHub):
def just_like_f_string(*args: str | Thunk) -> str:
return ''.join((format_value(arg) for arg in decode_raw(*args)))
@dataclass
class LazyFString:
args: Sequence[str | Thunk]
def __str__(self) -> str:
return self.value
@cached_property
def value(self) -> str:
return just_like_f_string(*self.args)
def fl(*args: str | Thunk) -> LazyFString:
return LazyFString(args)