From f1a4b24299cb3158396d8ac4918eac83e8828f82 Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 1 Nov 2024 10:36:40 -0700 Subject: [PATCH 01/52] Link to Lysandros' tstrings branch. --- peps/pep-0750.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 3291da787ea..b8eb6862d15 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -1042,7 +1042,7 @@ Reference Implementation ======================== At the time of this PEP's announcement, a fully-working implementation is -`available `_. +`available `_. There is also a public repository of `examples and tests `_ built around the reference implementation. If you're interested in playing with From d6e1ea77e8b042079a8b4feca7168a511677181a Mon Sep 17 00:00:00 2001 From: Dave Date: Tue, 12 Nov 2024 10:01:59 -0800 Subject: [PATCH 02/52] Add !r to spec on debug specifier. Closes #17 --- peps/pep-0750.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index b8eb6862d15..46137603464 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -399,7 +399,7 @@ The debug specifier, ``=``, is supported in template strings and behaves similar to how it behaves in f-strings, though due to limitations of the implementation there is a slight difference. -In particular, ``t'{expr=}'`` is treated as ``t'expr={expr}'``: +In particular, ``t'{expr=}'`` is treated as ``t'expr={expr!r}'``: .. code-block:: python @@ -407,6 +407,7 @@ In particular, ``t'{expr=}'`` is treated as ``t'expr={expr}'``: template = t"Hello {name=}" assert template.args[0] == "Hello name=" assert template.args[1].value == "World" + assert template.args[1].conv == "r" Raw Template Strings From 4baa43f133240fa166dc88c51ed3a1ff2ad2fd5e Mon Sep 17 00:00:00 2001 From: Dave Date: Tue, 12 Nov 2024 10:04:55 -0800 Subject: [PATCH 03/52] Finalize debug spec section. Closes #21 --- peps/pep-0750.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 46137603464..a9ab4c47ab6 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -409,6 +409,12 @@ In particular, ``t'{expr=}'`` is treated as ``t'expr={expr!r}'``: assert template.args[1].value == "World" assert template.args[1].conv == "r" +If a separate format string is also provided, ``t'{expr=:fmt}`` is treated instead as +``t'expr={expr!s:fmt}'``. + +Whitespace is preserved in the debug specifier, so ``t'{expr =}'`` is treated as +``t'expr ={expr!r}'``. + Raw Template Strings -------------------- From bf6115b1e31ba7d79dcba0b9d6f6c5ef65a7389c Mon Sep 17 00:00:00 2001 From: Dave Date: Tue, 12 Nov 2024 10:46:30 -0800 Subject: [PATCH 04/52] Add section on `Template.__hash__()`. Closes #19 --- peps/pep-0750.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index a9ab4c47ab6..e4409568a49 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -382,6 +382,28 @@ Two instances of ``Interpolation`` are defined to be equal if their ``value``, ) +Template and Interpolation Hashing +---------------------------------- + +The ``Template`` and ``Interpolation`` types implement the ``__hash__()`` method +roughly as follows: + +.. code-block:: python + + class Template: + def __hash__(self) -> int: + return hash(self.args) + + class Interpolation: + def __hash__(self) -> int: + return hash((self.value, self.expr, self.conv, self.format_spec)) + +An ``Interpolation`` instance is hashable if its ``value`` attribute +is also hashable. Likewise, a ``Template`` instance is hashable if +all of its ``Interpolation`` instances are hashable. In all other cases, +``TypeError`` is raised. + + No Support for Ordering ----------------------- From 5a501eef0b68041aed3e6dc6bd8abb58c91607f9 Mon Sep 17 00:00:00 2001 From: Dave Date: Tue, 12 Nov 2024 10:52:00 -0800 Subject: [PATCH 05/52] Explicitly describe the lack of a specialized `Template.__str__()`. Closes #22 --- peps/pep-0750.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index e4409568a49..11672497a10 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -481,6 +481,17 @@ Exceptions raised in t-string literals are the same as those raised in f-string literals. +No ``Template.__str__()`` Implementation +---------------------------------------- + +The ``Template`` type does not provide a specialized ``__str__()`` implementation; +it inherits the default implementation from the ``object`` class. + +This is because ``Template`` instances are intended to be used by template processing +code, which may return a string or any other type. There is no canonical way to +convert a Template to a string. + + Interleaving of ``Template.args`` --------------------------------- From 9084a8c4a5dee2fa84b98c0f4b127c4c9ec493ae Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 15 Nov 2024 10:37:15 -0800 Subject: [PATCH 06/52] Update PEP to use tuple, not Sequence for args. --- peps/pep-0750.rst | 45 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 11672497a10..bcbb0ad6176 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -138,7 +138,7 @@ Template strings evaluate to an instance of a new type, ``templatelib.Template`` .. code-block:: python class Template: - args: Sequence[str | Interpolation] + args: tuple[str | Interpolation, ...] def __init__(self, *args: str | Interpolation): ... @@ -495,7 +495,7 @@ convert a Template to a string. Interleaving of ``Template.args`` --------------------------------- -In the ``Template`` type, the ``args`` attribute is a sequence that will always +In the ``Template`` type, the ``args`` attribute is a tuple that will always alternate between string literals and ``Interpolation`` instances. Specifically: - Even-indexed elements (0, 2, 4, ...) are always of type ``str``, representing @@ -515,7 +515,7 @@ For example, the following assertions hold: assert template.args[2] == "" These rules imply that the ``args`` attribute will always have an odd length. -As a consequence, empty strings are added to the sequence when the template +As a consequence, empty strings are added to the tuple when the template begins or ends with an interpolation, or when two interpolations are adjacent: .. code-block:: python @@ -531,7 +531,7 @@ begins or ends with an interpolation, or when two interpolations are adjacent: Most template processing code will not care about this detail and will use either structural pattern matching or ``isinstance()`` checks to distinguish -between the two types of elements in the sequence. +between the two types of elements in the tuple. The detail exists because it allows for performance optimizations in template processing code. For example, a template processor could cache the static parts @@ -833,6 +833,15 @@ developers call a processing function that they get the result they want: typically, a string, although processing code can of course return any arbitrary type. +Developers will also want to understand how template strings relate to other +string formatting methods like f-strings and :meth:`str.format`. They will need +to decide when to use each method. If a simple string is all that is needed, and +there are no security implications, f-strings are likely the best choice. For +most cases where a format string is used, it can be replaced with a function +wrapping the creation of a template string. In cases where the format string is +obtained from user input, the filesystem, or databases, it is possible to write +code to convert it into a ``Template`` instance if desired. + Because developers will learn that t-strings are nearly always used in tandem with processing functions, they don't necessarily need to understand the details of the ``Template`` type. As with descriptors and decorators, we expect many more @@ -1078,6 +1087,26 @@ values, they can write a function to return a ``Template`` instance: This is, of course, no different from how f-strings can be reused. +Use of Format Strings +--------------------- + +The venerable :meth:`str.format` method accepts format strings that can later +be used to format values. Developers can write a function that takes a format +string along with a dictionary of values and returns a ``Template`` instance: + +.. code-block:: python + + def make_template(fmt_str: str, **values: object) -> Template: + to_eval = f't"{fmt_str}"' + return eval(to_eval, values) + + with open("template.txt") as f: + # Imagine that template.txt contains "Hello {name}, value: {value:.2f}" + template = make_template(f.read(), name="World", value=42) + + assert f(template) == "Hello World, value: 42.00" + + Reference Implementation ======================== @@ -1308,10 +1337,10 @@ During the development of this PEP, we considered several alternate layouts for the ``args`` attribute of the ``Template`` type. This included: - Instead of ``args``, ``Template`` contains a ``strings`` attribute of type - ``Sequence[str]`` and an ``interpolations`` attribute of type - ``Sequence[Interpolation]``. There are zero or more interpolations and + ``tuple[str, ...]`` and an ``interpolations`` attribute of type + ``tuple[Interpolation, ...]``. There are zero or more interpolations and there is always one more string than there are interpolations. Utility code - could build an interleaved sequence of strings and interpolations from these + could build an interleaved tuple of strings and interpolations from these separate attributes. This was rejected as being overly complex. - ``args`` is typed as a ``Sequence[tuple[str, Interpolation | None]]``. Each @@ -1319,7 +1348,7 @@ the ``args`` attribute of the ``Template`` type. This included: string part has no corresponding interpolation. This was rejected as being overly complex. -- ``args`` remains a ``Sequence[str | Interpolation]`` but does not support +- ``args`` remains a ``tuple[str | Interpolation, ...]`` but does not support interleaving. As a result, empty strings are not added to the sequence. It is no longer possible to obtain static strings with ``args[::2]``; instead, instance checks or structural pattern matching must be used to distinguish From 59c8280d51cee2421803dfbee985651d420426c2 Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 15 Nov 2024 10:45:05 -0800 Subject: [PATCH 07/52] Update rationale for templatelib. --- peps/pep-0750.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index bcbb0ad6176..dcb1b794410 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -586,7 +586,6 @@ specifiers like ``:.2f``. The full code is fairly simple: return str(value) return value - def f(template: Template) -> str: parts = [] for arg in template.args: @@ -1212,10 +1211,10 @@ allowing combination of ``r`` and ``t`` prefixes. Other Homes for ``Template`` and ``Interpolation`` -------------------------------------------------- -Previous versions of this PEP proposed that the ``Template`` and ``Interpolation`` -types be placed in the ``types`` module. This was rejected in favor of creating -a new top-level standard library module, ``templatelib``. This was done to avoid -polluting the ``types`` module with seemingly unrelated types. +Previous versions of this PEP proposed placing the ``Template`` and +``Interpolation`` types in the ``types`` module. Members of the Python core +team internally discussed the various options and ultimately decided to create +a new top-level standard library module, ``templatelib``. Enable Full Reconstruction of Original Template Literal From 961a32761b72f44247d0313a92412b93a95e64d8 Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 15 Nov 2024 10:59:01 -0800 Subject: [PATCH 08/52] Describe convenience accessors for Template. --- peps/pep-0750.rst | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index dcb1b794410..9274c0fd9de 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -143,6 +143,18 @@ Template strings evaluate to an instance of a new type, ``templatelib.Template`` def __init__(self, *args: str | Interpolation): ... + @property + def strings(self) -> tuple[str, ...]: + ... + + @property + def interpolations(self) -> tuple[Interpolation, ...]: + ... + + @property + def values(self) -> tuple[object, ...]: + ... + The ``args`` attribute provides access to the string parts and any interpolations in the literal: @@ -256,6 +268,25 @@ It would be surprising if, for example, a template string that uses ``{value:.2f did not round the value to two decimal places when processed. +Convenience Accessors in ``Template`` +------------------------------------- + +The ``Template.strings`` and ``Template.interpolations`` properties provide +convenient strongly-typed access to the ``str`` and ``Interpolation`` instances +found in ``Template.args``. + +Finally, the ``Template.values`` property is equivalent to: + +.. code-block:: + + @property + def values(self) -> tuple[object, ...]: + return tuple(i.value for i in self.args if isinstance(i, Interpolation)) + +Details about the layout of ``Template.args`` are explained in the +`Interleaving of Template.args`_ section below. + + Processing Template Strings --------------------------- From 3d0bad902442b19be434af342a6432a9f6523bbf Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 15 Nov 2024 11:15:47 -0800 Subject: [PATCH 09/52] Add discussion of Template convenience accessors. --- peps/pep-0750.rst | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 9274c0fd9de..1872a007289 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -526,8 +526,8 @@ convert a Template to a string. Interleaving of ``Template.args`` --------------------------------- -In the ``Template`` type, the ``args`` attribute is a tuple that will always -alternate between string literals and ``Interpolation`` instances. Specifically: +In the ``Template`` type, the ``args`` attribute is a tuple that always +alternates between string literals and ``Interpolation`` instances. Specifically: - Even-indexed elements (0, 2, 4, ...) are always of type ``str``, representing the literal parts of the template. @@ -561,15 +561,27 @@ begins or ends with an interpolation, or when two interpolations are adjacent: assert template.args[4] == "" Most template processing code will not care about this detail and will use -either structural pattern matching or ``isinstance()`` checks to distinguish -between the two types of elements in the tuple. +one or more of: -The detail exists because it allows for performance optimizations in template -processing code. For example, a template processor could cache the static parts -of the template and only reprocess the dynamic parts when the template is -evaluated with different values. Access to the static parts can be done with -``template.args[::2]``. +- Structural pattern matching +- ``isinstance()`` checks +- The ``strings``, ``interpolations``, and ``values`` properties +For example: + +.. code-block:: python + + name = "World" + template = t"Hello {name}!" + assert template.strings == ("Hello ", "!") + assert template.interpolations == (Interpolation(value=name, expr="name"),) + assert template.values == (name,) + +Interleaving allows for performance optimizations in template processing code. +For example, a template processor could cache the static parts of the template +and only reprocess the dynamic parts when the template is evaluated with +different values. + Interleaving is an invariant maintained by the ``Template`` class. Developers can take advantage of it but they are not required to themselves maintain it. Specifically, ``Template.__init__()`` can be called with ``str`` and @@ -936,12 +948,12 @@ The structure of ``Template`` objects allows for effective memoization: .. code-block:: python - source = template.args[::2] # Static string parts - values = [i.value for i in template.args[1::2]] # Dynamic interpolated values + strings = template.strings # Static string parts + values = tempalte.values # Dynamic interpolated values -This separation enables caching of processed static parts, while dynamic parts can be -inserted as needed. Authors of template processing code can use the static -``source`` as cache keys, leading to significant performance improvements when +This separation enables caching of processed static parts while dynamic parts +can be inserted as needed. Authors of template processing code can use the static +``strings`` as cache keys, leading to significant performance improvements when similar templates are used repeatedly. From 6da8042e9e1e8400277e474e10387aa3569ed9ba Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 15 Nov 2024 11:18:47 -0800 Subject: [PATCH 10/52] Get rid of wrong. --- peps/pep-0750.rst | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 1872a007289..fcfcc2a6c93 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -1362,16 +1362,6 @@ This was rejected in favor of keeping t-string syntax as close to f-string synta as possible. -A Lazy Conversion Specifier ---------------------------- - -We considered adding a new conversion specifier, ``!()``, that would explicitly -wrap the interpolation expression in a lambda. - -This was rejected in favor of the simpler approach of using explicit lambdas -when lazy evaluation is desired. - - Alternate Layouts for ``Template.args`` --------------------------------------- From f2991d31acfc78af81f86b2d1a40934b3e72aa6e Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 18 Nov 2024 09:23:46 -0800 Subject: [PATCH 11/52] Wrap up work on format strings and relationship to templates. --- peps/pep-0750.rst | 82 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 62 insertions(+), 20 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index fcfcc2a6c93..b5f03ad8fd4 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -1099,17 +1099,17 @@ and await it before processing the template string: return "Sleepy" template = t"Hello {get_name}" - assert await aformat(template) == "Hello Sleepy" + assert await async_f(template) == "Hello Sleepy" -This assumes that the template processing code in ``aformat()`` is asynchronous +This assumes that the template processing code in ``async_f()`` is asynchronous and is able to ``await`` an interpolation's value. .. note:: Example code - See `aformat.py`__ and `test_aformat.py`__. + See `afstring.py`__ and `test_afstring.py`__. - __ https://github.com/davepeck/pep750-examples/blob/main/pep/aformat.py - __ https://github.com/davepeck/pep750-examples/blob/main/pep/test_aformat.py + __ https://github.com/davepeck/pep750-examples/blob/main/pep/afstring.py + __ https://github.com/davepeck/pep750-examples/blob/main/pep/test_afstring.py Approaches to Template Reuse @@ -1129,31 +1129,70 @@ values, they can write a function to return a ``Template`` instance: This is, of course, no different from how f-strings can be reused. -Use of Format Strings ---------------------- +Relation to Format Strings +-------------------------- The venerable :meth:`str.format` method accepts format strings that can later -be used to format values. Developers can write a function that takes a format -string along with a dictionary of values and returns a ``Template`` instance: +be used to format values: + +.. code-block:: python + + alas_fmt = "We're all out of {cheese}." + assert alas_fmt.format(cheese="Red Leicester") == "We're all out of Red Leicester." + +If one squints, one can think of format strings as a kind of function definition. +The *call* to :meth:`str.format` can be seen as a kind of function call. The +t-string equivalent is to simply define a standard Python function that returns +a ``Template`` instance: + +.. code-block:: python + + def make_alas(*, cheese: str) -> Template: + return t"We're all out of {cheese}." + + alas_t = make_alas(cheese="Red Leicester") + # Using the f() function from the f-string example, above + assert f(alas_t) == "We're all out of Red Leicester." + +The ``make_alas()`` function itself can be thought of as analogous to the +format string. The call to ``make_alas()`` is analogous to the call to +:meth:`str.format`. + +Of course, it is common to load format strings from external sources like a +filesystem or database. Thankfully, because ``Template`` and ``Interpolation`` +are simple Python types, it is possible to write a function that takes an +old-style format string and returns an equivalent ``Template`` instance: .. code-block:: python - def make_template(fmt_str: str, **values: object) -> Template: - to_eval = f't"{fmt_str}"' - return eval(to_eval, values) + def from_format(fmt: str, /, *args: object, **kwargs: object) -> Template: + """Parse `fmt` and return a `Template` instance.""" + ... - with open("template.txt") as f: - # Imagine that template.txt contains "Hello {name}, value: {value:.2f}" - template = make_template(f.read(), name="World", value=42) + # Load this from a file, database, etc. + alas_fmt = "We're all out of {cheese}." + alas_t = from_format(alas_fmt, cheese="Red Leicester") + # Using the f() function from the f-string example, above + assert f(alas_t) == "We're all out of Red Leicester." - assert f(template) == "Hello World, value: 42.00" +This is a powerful pattern that allows developers to use template strings in +places where they might have previously used format strings. A full implementation +of ``from_format()`` is available in the examples repository. It supports the +full grammar of format strings including positional and keyword arguments, +automatic and manual field numbering, etc. + +.. note:: Example code + + See `format.py`__ and `test_format.py`__. + + __ https://github.com/davepeck/pep750-examples/blob/main/pep/format.py + __ https://github.com/davepeck/pep750-examples/blob/main/pep/test_format.py Reference Implementation ======================== -At the time of this PEP's announcement, a fully-working implementation is -`available `_. +A CPython implementation of PEP 750 is `available `_. There is also a public repository of `examples and tests `_ built around the reference implementation. If you're interested in playing with @@ -1228,8 +1267,11 @@ This was rejected for several reasons: static analysis. Most importantly, there are viable (if imperfect) alternatives to implicit -lambda wrapping when lazy evaluation is desired. See the section on -`Approaches to Lazy Evaluation`_, above, for more information. +lambda wrapping in many cases where lazy evaluation is desired. See the section +on `Approaches to Lazy Evaluation`_, above, for more information. + +While delayed evaluation was rejected for *this* PEP, we hope that the community +continues to explore the idea. Making ``Template`` and ``Interpolation`` Into Protocols From 2cb55e71bd5da22db1d02ca10e07dc2674128323 Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 18 Nov 2024 09:46:04 -0800 Subject: [PATCH 12/52] Add post history for Nov 18 2024 --- peps/pep-0750.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index b5f03ad8fd4..1012290aca9 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -13,7 +13,8 @@ Created: 08-Jul-2024 Python-Version: 3.14 Post-History: `09-Aug-2024 `__, `17-Oct-2024 `__, - `21-Oct-2024 `__ + `21-Oct-2024 `__, + `18-Nov-2024 `__ Abstract From e908775d17e373c0795462b98b6114875e91459c Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 18 Nov 2024 10:37:56 -0800 Subject: [PATCH 13/52] Correct spelling mistake --- peps/pep-0750.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 1012290aca9..190ad5a3c31 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -950,7 +950,7 @@ The structure of ``Template`` objects allows for effective memoization: .. code-block:: python strings = template.strings # Static string parts - values = tempalte.values # Dynamic interpolated values + values = template.values # Dynamic interpolated values This separation enables caching of processed static parts while dynamic parts can be inserted as needed. Authors of template processing code can use the static From 375168201e25ab5731335e202bd92b4cf1b52cef Mon Sep 17 00:00:00 2001 From: Dave Date: Tue, 19 Nov 2024 10:43:42 -0800 Subject: [PATCH 14/52] Update writing on t-strings and bytes. --- peps/pep-0750.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 190ad5a3c31..143e56a9315 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -125,11 +125,16 @@ the ability to nest template strings within interpolations, as well as the abili to use all valid quote marks (``'``, ``"``, ``'''``, and ``"""``). Like other string prefixes, the ``t`` prefix must immediately precede the quote. Like f-strings, both lowercase ``t`` and uppercase ``T`` prefixes are supported. Like -f-strings, t-strings may not be combined with the ``b`` or ``u`` prefixes. +f-strings, t-strings may not be combined with ``u`` prefix. + Additionally, f-strings and t-strings cannot be combined, so the ``ft`` prefix is invalid as well. t-strings *may* be combined with the ``r`` prefix; see the `Raw Template Strings`_ section below for more information. +The combination of t-strings and bytes (``tb``) is considered out of scope for +this PEP. However, unlike f-strings, there is no inherent reason why t-strings +could not be combined with bytes. Support could be considered in a future PEP. + The ``Template`` Type --------------------- From f4b03d6291fd3a46f08c7dc56b8d56ab16c1b948 Mon Sep 17 00:00:00 2001 From: Dave Date: Tue, 19 Nov 2024 10:45:09 -0800 Subject: [PATCH 15/52] leave the door open for t-string + bytes in a future PEP --- peps/pep-0750.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 143e56a9315..639d6eaa76f 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -132,8 +132,8 @@ prefix is invalid as well. t-strings *may* be combined with the ``r`` prefix; see the `Raw Template Strings`_ section below for more information. The combination of t-strings and bytes (``tb``) is considered out of scope for -this PEP. However, unlike f-strings, there is no inherent reason why t-strings -could not be combined with bytes. Support could be considered in a future PEP. +this PEP. However, unlike f-strings, there is no fundamental reason why t-strings +and bytes cannot be combined. Support could be considered in a future PEP. The ``Template`` Type From a762212a56474add9367f6293f9b0736acd9c792 Mon Sep 17 00:00:00 2001 From: Dave Date: Tue, 19 Nov 2024 10:46:21 -0800 Subject: [PATCH 16/52] Remove needless 'as well' --- peps/pep-0750.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 639d6eaa76f..5027e740bd6 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -128,7 +128,7 @@ both lowercase ``t`` and uppercase ``T`` prefixes are supported. Like f-strings, t-strings may not be combined with ``u`` prefix. Additionally, f-strings and t-strings cannot be combined, so the ``ft`` -prefix is invalid as well. t-strings *may* be combined with the ``r`` prefix; +prefix is invalid. t-strings *may* be combined with the ``r`` prefix; see the `Raw Template Strings`_ section below for more information. The combination of t-strings and bytes (``tb``) is considered out of scope for From 8792b7a66720a37d0d51563af289bf0d039d793b Mon Sep 17 00:00:00 2001 From: Dave Date: Tue, 19 Nov 2024 10:57:18 -0800 Subject: [PATCH 17/52] Simplify comprehension --- peps/pep-0750.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 5027e740bd6..e1746c21c84 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -287,7 +287,7 @@ Finally, the ``Template.values`` property is equivalent to: @property def values(self) -> tuple[object, ...]: - return tuple(i.value for i in self.args if isinstance(i, Interpolation)) + return tuple(i.value for i in self.interpolations) Details about the layout of ``Template.args`` are explained in the `Interleaving of Template.args`_ section below. From 457fde6d7a5a483976e4502512f18b939c13555a Mon Sep 17 00:00:00 2001 From: Dave Date: Tue, 19 Nov 2024 14:00:20 -0800 Subject: [PATCH 18/52] Update Discussions-To --- peps/pep-0750.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index e1746c21c84..a83af88e8d3 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -6,7 +6,7 @@ Author: Jim Baker , Koudai Aono , Lysandros Nikolaou , Dave Peck -Discussions-To: https://discuss.python.org/t/pep-750-tag-strings-for-writing-domain-specific-languages/60408 +Discussions-To: https://discuss.python.org/t/pep750-template-strings-new-updates/71594 Status: Draft Type: Standards Track Created: 08-Jul-2024 From fbef23c75ff73358aeb580c773c146357067d6ef Mon Sep 17 00:00:00 2001 From: Dave Date: Tue, 19 Nov 2024 16:16:41 -0800 Subject: [PATCH 19/52] Update 'structured logging' paragraph --- peps/pep-0750.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index a83af88e8d3..689e09c4008 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -659,10 +659,9 @@ specifiers like ``:.2f``. The full code is fairly simple: Example: Structured Logging --------------------------- -Structured logging allows developers to log data in both a human-readable format -*and* a structured format (like JSON) using only a single logging call. This is -useful for log aggregation systems that process the structured format while -still allowing developers to easily read their logs. +Structured logging allows developers to log data in machine-readable +formats like JSON. With t-strings, developers can easily log structured data +alongside human-readable messages using just a single log statement. We present two different approaches to implementing structured logging with template strings. From 9c4c8f376f8477452699f69b02a0521ae55b56f4 Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 19 Dec 2024 13:03:08 -0800 Subject: [PATCH 20/52] Fix 'strongly typed' (whoops) and just say "class" --- peps/pep-0750.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 689e09c4008..260cf676002 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -184,7 +184,7 @@ The ``Interpolation`` Type -------------------------- The ``Interpolation`` type represents an expression inside a template string. -Like ``Template``, it is a new concrete type found in the ``templatelib`` module: +Like ``Template``, it is a new class found in the ``templatelib`` module: .. code-block:: python @@ -278,7 +278,7 @@ Convenience Accessors in ``Template`` ------------------------------------- The ``Template.strings`` and ``Template.interpolations`` properties provide -convenient strongly-typed access to the ``str`` and ``Interpolation`` instances +convenient access to the ``str`` and ``Interpolation`` instances found in ``Template.args``. Finally, the ``Template.values`` property is equivalent to: @@ -1283,9 +1283,9 @@ Making ``Template`` and ``Interpolation`` Into Protocols -------------------------------------------------------- An early version of this PEP proposed that the ``Template`` and ``Interpolation`` -types be runtime checkable protocols rather than concrete types. +types be runtime checkable protocols rather than classes. -In the end, we felt that using concrete types was more straightforward. +In the end, we felt that using classes was more straightforward. An Additional ``Decoded`` Type From 2190362c08ff8a7b004d7e364d9d1647f0444cad Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 20 Dec 2024 16:25:34 -0800 Subject: [PATCH 21/52] {Template,Interpolation}.__init__ --> __new__ I can't believe I didn't catch this in any of my zillion previous re-reads. Sheesh! --- peps/pep-0750.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 260cf676002..fb6249966b9 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -146,7 +146,7 @@ Template strings evaluate to an instance of a new type, ``templatelib.Template`` class Template: args: tuple[str | Interpolation, ...] - def __init__(self, *args: str | Interpolation): + def __new__(cls, *args: str | Interpolation): ... @property @@ -196,8 +196,8 @@ Like ``Template``, it is a new class found in the ``templatelib`` module: __match_args__ = ("value", "expr", "conv", "format_spec") - def __init__( - self, + def __new__( + cls, value: object, expr: str, conv: Literal["a", "r", "s"] | None = None, @@ -590,9 +590,9 @@ different values. Interleaving is an invariant maintained by the ``Template`` class. Developers can take advantage of it but they are not required to themselves maintain it. -Specifically, ``Template.__init__()`` can be called with ``str`` and -``Interpolation`` instances in *any* order; the constructor will "interleave" them -as necessary before assigning them to ``args``. +Specifically, a ``Template`` can be constructed with ``str`` and +``Interpolation`` instances in *any* order; they will automatically be +"interleaved" as necessary before ``args`` is assigned. Examples From 22bb86afc405948c55d6d167609ce436664106e9 Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 20 Dec 2024 16:32:17 -0800 Subject: [PATCH 22/52] Move statement about `tb` prefix to rejected ideas. --- peps/pep-0750.rst | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index fb6249966b9..547f9f3db46 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -125,16 +125,12 @@ the ability to nest template strings within interpolations, as well as the abili to use all valid quote marks (``'``, ``"``, ``'''``, and ``"""``). Like other string prefixes, the ``t`` prefix must immediately precede the quote. Like f-strings, both lowercase ``t`` and uppercase ``T`` prefixes are supported. Like -f-strings, t-strings may not be combined with ``u`` prefix. +f-strings, t-strings may not be combined with ``u`` or the ``b`` prefix. Additionally, f-strings and t-strings cannot be combined, so the ``ft`` prefix is invalid. t-strings *may* be combined with the ``r`` prefix; see the `Raw Template Strings`_ section below for more information. -The combination of t-strings and bytes (``tb``) is considered out of scope for -this PEP. However, unlike f-strings, there is no fundamental reason why t-strings -and bytes cannot be combined. Support could be considered in a future PEP. - The ``Template`` Type --------------------- @@ -1450,6 +1446,14 @@ tag.) While exciting, this PEP does not propose any specific mechanism. It is our hope that, over time, the community will develop conventions for this purpose. +Binary Template Strings +----------------------- + +The combination of t-strings and bytes (``tb``) is considered out of scope for +this PEP. However, unlike f-strings, there is no fundamental reason why t-strings +and bytes cannot be combined. Support could be considered in a future PEP. + + Acknowledgements ================ From d202effe2ad4c44cafd7c4a465c4d94d9f5bfa03 Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 20 Dec 2024 16:38:34 -0800 Subject: [PATCH 23/52] Add doccomments to template properties --- peps/pep-0750.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 547f9f3db46..193eb029b19 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -143,18 +143,38 @@ Template strings evaluate to an instance of a new type, ``templatelib.Template`` args: tuple[str | Interpolation, ...] def __new__(cls, *args: str | Interpolation): + """ + Create a new Template instance. + + Arguments can be provided in any order. They will be coalesced + and interleaved as necessary to ensure that the `args` attribute + is a tuple of alternating string parts and Interpolation instances. + """ ... @property def strings(self) -> tuple[str, ...]: + """ + Return a tuple of the N+1 string parts of the template, where N is + the number of interpolations. This will always contain at least one + element. + """ ... @property def interpolations(self) -> tuple[Interpolation, ...]: + """ + Return a tuple of the N interpolations in the template. This can + be an empty tuple. + """ ... @property def values(self) -> tuple[object, ...]: + """ + Return a tuple of the `value` attributes of each Interpolation + in the template. This can be an empty tuple. + """ ... The ``args`` attribute provides access to the string parts and From ba5119c3d15ba54227e8215cc19b282e07fc8fa4 Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 20 Dec 2024 16:40:52 -0800 Subject: [PATCH 24/52] Make whitespace clear in debug spec --- peps/pep-0750.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 193eb029b19..2718c872147 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -487,8 +487,8 @@ In particular, ``t'{expr=}'`` is treated as ``t'expr={expr!r}'``: If a separate format string is also provided, ``t'{expr=:fmt}`` is treated instead as ``t'expr={expr!s:fmt}'``. -Whitespace is preserved in the debug specifier, so ``t'{expr =}'`` is treated as -``t'expr ={expr!r}'``. +Whitespace is preserved in the debug specifier, so ``t'{expr = }'`` is treated as +``t'expr = {expr!r}'``. Raw Template Strings From 25baa4eb9bdd895aa778a517cfc2e3f4bf54e057 Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 13 Jan 2025 11:23:23 -0800 Subject: [PATCH 25/52] Change to TBD --- peps/pep-0750.rst | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 2718c872147..532cb737c6d 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -109,14 +109,14 @@ Template String Literals ------------------------ This PEP introduces a new string prefix, ``t``, to define template string literals. -These literals resolve to a new type, ``Template``, found in a new top-level -standard library module, ``templatelib``. +These literals resolve to a new type, ``Template``, found in the standard library +module ``<>``. The following code creates a ``Template`` instance: .. code-block:: python - from templatelib import Template + from TBD import Template template = t"This is a template string." assert isinstance(template, Template) @@ -135,7 +135,7 @@ see the `Raw Template Strings`_ section below for more information. The ``Template`` Type --------------------- -Template strings evaluate to an instance of a new type, ``templatelib.Template``: +Template strings evaluate to an instance of a new type, ``<>.Template``: .. code-block:: python @@ -200,7 +200,7 @@ The ``Interpolation`` Type -------------------------- The ``Interpolation`` type represents an expression inside a template string. -Like ``Template``, it is a new class found in the ``templatelib`` module: +Like ``Template``, it is a new class found in the ``<>`` module: .. code-block:: python @@ -318,7 +318,7 @@ interpolations in uppercase: .. code-block:: python - from templatelib import Template, Interpolation + from TBD import Template, Interpolation def lower_upper(template: Template) -> str: """Render static parts lowercased and interpolations uppercased.""" @@ -640,7 +640,7 @@ specifiers like ``:.2f``. The full code is fairly simple: .. code-block:: python - from templatelib import Template, Interpolation + from TBD import Template, Interpolation def convert(value: object, conv: Literal["a", "r", "s"] | None) -> object: if conv == "a": @@ -713,7 +713,7 @@ We can implement an improved version of ``StructuredMessage`` using template str .. code-block:: python import json - from templatelib import Interpolation, Template + from TBD import Interpolation, Template from typing import Mapping class TemplateMessage: @@ -770,7 +770,7 @@ and a ``ValuesFormatter`` for JSON output: import json from logging import Formatter, LogRecord - from templatelib import Interpolation, Template + from TBD import Interpolation, Template from typing import Any, Mapping @@ -938,7 +938,7 @@ best practice for many template function implementations: .. code-block:: python - from templatelib import Template, Interpolation + from TBD import Template, Interpolation def process(template: Template) -> Any: for arg in template.args: @@ -1314,13 +1314,19 @@ We rejected this in favor of the simpler approach of using plain ``str`` and allowing combination of ``r`` and ``t`` prefixes. -Other Homes for ``Template`` and ``Interpolation`` --------------------------------------------------- +The Final Home for ``Template`` and ``Interpolation`` +----------------------------------------------------- Previous versions of this PEP proposed placing the ``Template`` and -``Interpolation`` types in the ``types`` module. Members of the Python core -team internally discussed the various options and ultimately decided to create -a new top-level standard library module, ``templatelib``. +``Interpolation`` types in: ``types``, ``collections``, ``collections.abc``, +and even in a new top-level module, ``templatelib``. As of this writing, no core +team consensus has emerged on the final location for these types. The current +PEP leaves this open for a final decision. + +One argument in favor of a new top-level ``templatelib`` module is that it would +allow for future addition of related methods (like ``convert()`` and ``from_format_string()``) +and for potential future template processing code to be added to submodules +(``templatelib.shell``, etc.). Enable Full Reconstruction of Original Template Literal From 48648c642b73637578c889f5e0a800070b7a301d Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 13 Jan 2025 11:29:25 -0800 Subject: [PATCH 26/52] Add a little about HTML and about overlapping types --- peps/pep-0750.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 532cb737c6d..58ecdef68b5 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -927,6 +927,22 @@ the language itself. Bringing the full power and generality of Python to bear on string processing tasks opens new possibilities for framework authors. +Why another templating approach? +================================ + +The world of Python already has mature templating languages with wide adoption, +such as Jinja. Why build support for creating new templating systems? + +Projects such as Jinja are still needed in cases where the template is less part +of the software by the developers, and more part of customization by designers +or even content created by users, for example in a CMS. + +The trends in frontend development have treated templating as part of the +software and written by developers. They want modern language features and a +good tooling experience. PEP 750 envisions DSLs where the non-static parts are +Python: same scope rules, typing, expression syntax, and the like. + + Common Patterns Seen in Processing Templates ============================================ From 9aba711336f174ffcb3155e9087f8d3999253d2c Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 13 Jan 2025 12:16:21 -0800 Subject: [PATCH 27/52] Goodbye, args. --- peps/pep-0750.rst | 325 ++++++++++++++++------------------------------ 1 file changed, 115 insertions(+), 210 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 58ecdef68b5..7ed2af97563 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -140,60 +140,55 @@ Template strings evaluate to an instance of a new type, ``<>.Template``: .. code-block:: python class Template: - args: tuple[str | Interpolation, ...] - + """Simulated implementation of the PEP750 Template type.""" + strings: tuple[str, ...] + """ + A tuple of the N+1 string parts of the template, where N is + the number of interpolations. This will always contain at least one + element. + """ + + interpolations: tuple[Interpolation, ...] + """ + A tuple of the N interpolations in the template. This can + be an empty tuple. + """ + def __new__(cls, *args: str | Interpolation): """ Create a new Template instance. - Arguments can be provided in any order. They will be coalesced - and interleaved as necessary to ensure that the `args` attribute - is a tuple of alternating string parts and Interpolation instances. + Arguments can be provided in any order. """ ... @property - def strings(self) -> tuple[str, ...]: + def values(self) -> tuple[object, ...]: """ - Return a tuple of the N+1 string parts of the template, where N is - the number of interpolations. This will always contain at least one - element. + Return a tuple of the `value` attributes of each Interpolation + in the template. This can be an empty tuple. """ ... - @property - def interpolations(self) -> tuple[Interpolation, ...]: + def __iter__(self) -> Iterator[str | Interpolation]: """ - Return a tuple of the N interpolations in the template. This can - be an empty tuple. - """ - ... + Iterate over the string parts and interpolations in the template. - @property - def values(self) -> tuple[object, ...]: - """ - Return a tuple of the `value` attributes of each Interpolation - in the template. This can be an empty tuple. + These may appear in any order. Empty strings will not be included. """ ... -The ``args`` attribute provides access to the string parts and -any interpolations in the literal: +The ``strings`` and ``interpolation`` attributes provide access to the string +parts and any interpolations in the literal: .. code-block:: python name = "World" template = t"Hello {name}" - assert isinstance(template.args[0], str) - assert isinstance(template.args[1], Interpolation) - assert template.args[0] == "Hello " - assert template.args[1].value == "World" - -See `Interleaving of Template.args`_ below for more information on how the -``args`` attribute is structured. + assert template.strings[0] == "Hello " + assert template.interpolations[0].value == "World" -The ``Template`` type is immutable. ``Template.args`` cannot be reassigned -or mutated. +The ``Template`` type is shallow immutable. Its attributes cannot be reassigned. The ``Interpolation`` Type @@ -230,7 +225,7 @@ The ``value`` attribute is the evaluated result of the interpolation: name = "World" template = t"Hello {name}" - assert template.args[1].value == "World" + assert template.interpolations[0].value == "World" The ``expr`` attribute is the *original text* of the interpolation: @@ -238,7 +233,7 @@ The ``expr`` attribute is the *original text* of the interpolation: name = "World" template = t"Hello {name}" - assert template.args[1].expr == "name" + assert template.interpolations[0].expr == "name" We expect that the ``expr`` attribute will not be used in most template processing code. It is provided for completeness and for use in debugging and introspection. @@ -254,7 +249,7 @@ are supported: name = "World" template = t"Hello {name!r}" - assert template.args[1].conv == "r" + assert template.interpolations[0].conv == "r" If no conversion is provided, ``conv`` is ``None``. @@ -265,7 +260,7 @@ As with f-strings, this is an arbitrary string that defines how to present the v value = 42 template = t"Value: {value:.2f}" - assert template.args[1].format_spec == ".2f" + assert template.interpolations[0].format_spec == ".2f" Format specifications in f-strings can themselves contain interpolations. This is permitted in template strings as well; ``format_spec`` is set to the eagerly @@ -276,7 +271,7 @@ evaluated result: value = 42 precision = 2 template = t"Value: {value:.{precision}f}" - assert template.args[1].format_spec == ".2f" + assert template.interpolations[0].format_spec == ".2f" If no format specification is provided, ``format_spec`` defaults to an empty string (``""``). This matches the ``format_spec`` parameter of Python's @@ -293,20 +288,25 @@ did not round the value to two decimal places when processed. Convenience Accessors in ``Template`` ------------------------------------- -The ``Template.strings`` and ``Template.interpolations`` properties provide -convenient access to the ``str`` and ``Interpolation`` instances -found in ``Template.args``. +The ``Template.values`` property is equivalent to: -Finally, the ``Template.values`` property is equivalent to: - -.. code-block:: +.. code-block::python @property def values(self) -> tuple[object, ...]: return tuple(i.value for i in self.interpolations) -Details about the layout of ``Template.args`` are explained in the -`Interleaving of Template.args`_ section below. + +The ``Template.__iter__()`` method is equivalent to: + +.. code-block::python + + def __iter__(self) -> Iterator[str | Interpolation]: + for s, i in zip_longest(self.strings, self.interpolations): + if s: + yield s + if i: + yield i Processing Template Strings @@ -323,11 +323,11 @@ interpolations in uppercase: def lower_upper(template: Template) -> str: """Render static parts lowercased and interpolations uppercased.""" parts: list[str] = [] - for arg in template.args: - if isinstance(arg, Interpolation): - parts.append(str(arg.value).upper()) + for item in template: + if isinstance(item, Interpolation): + parts.append(str(item.value).upper()) else: - parts.append(arg.lower()) + parts.append(item.lower()) return "".join(parts) name = "world" @@ -380,81 +380,22 @@ roughly as follows: class Template: def __add__(self, other: object) -> Template: - if isinstance(other, str): - return Template(*self.args[:-1], self.args[-1] + other) - if not isinstance(other, Template): - return NotImplemented - return Template(*self.args[:-1], self.args[-1] + other.args[0], *other.args[1:]) + return Template(*self, other) def __radd__(self, other: object) -> Template: - if not isinstance(other, str): - return NotImplemented - return Template(other + self.args[0], *self.args[1:]) - -Special care is taken to ensure that the interleaving of ``str`` and ``Interpolation`` -instances is maintained when concatenating. (See the -`Interleaving of Template.args`_ section for more information.) + return Template(other, *self) Template and Interpolation Equality ----------------------------------- -Two instances of ``Template`` are defined to be equal if their ``args`` attributes -contain the same strings and interpolations in the same order: - -.. code-block:: python - - assert t"I love {stilton}" == t"I love {stilton}" - assert t"I love {stilton}" != t"I love {roquefort}" - assert t"I " + t"love {stilton}" == t"I love {stilton}" - -The implementation of ``Template.__eq__()`` is roughly as follows: - -.. code-block:: python +``Template`` and ``Interpolation`` instances use the standard Python equality +semantics from the ``object`` class. No attempt is made to provide a custom +implementation of ``__eq__()`` or ``__hash__()``. - class Template: - def __eq__(self, other: object) -> bool: - if not isinstance(other, Template): - return NotImplemented - return self.args == other.args - -Two instances of ``Interpolation`` are defined to be equal if their ``value``, -``expr``, ``conv``, and ``format_spec`` attributes are equal: - -.. code-block:: python - - class Interpolation: - def __eq__(self, other: object) -> bool: - if not isinstance(other, Interpolation): - return NotImplemented - return ( - self.value == other.value - and self.expr == other.expr - and self.conv == other.conv - and self.format_spec == other.format_spec - ) - - -Template and Interpolation Hashing ----------------------------------- - -The ``Template`` and ``Interpolation`` types implement the ``__hash__()`` method -roughly as follows: - -.. code-block:: python - - class Template: - def __hash__(self) -> int: - return hash(self.args) - - class Interpolation: - def __hash__(self) -> int: - return hash((self.value, self.expr, self.conv, self.format_spec)) - -An ``Interpolation`` instance is hashable if its ``value`` attribute -is also hashable. Likewise, a ``Template`` instance is hashable if -all of its ``Interpolation`` instances are hashable. In all other cases, -``TypeError`` is raised. +``Template`` instances are intended to be used by template processing code, +which may return a string or any other type. Those types can provide their +own custom equality semantics as needed. No Support for Ordering @@ -480,9 +421,9 @@ In particular, ``t'{expr=}'`` is treated as ``t'expr={expr!r}'``: name = "World" template = t"Hello {name=}" - assert template.args[0] == "Hello name=" - assert template.args[1].value == "World" - assert template.args[1].conv == "r" + assert template.strings[0] == "Hello name=" + assert template.interpolations[0].value == "World" + assert template.interpolations[0].conv == "r" If a separate format string is also provided, ``t'{expr=:fmt}`` is treated instead as ``t'expr={expr!s:fmt}'``. @@ -500,8 +441,8 @@ Raw template strings are supported using the ``rt`` (or ``tr``) prefix: trade = 'shrubberies' t = rt'Did you say "{trade}"?\n' - assert t.args[0] == r'Did you say "' - assert t.args[2] == r'"?\n' + assert t.strings[0] == r'Did you say "' + assert t.strings[1] == r'"?\n' In this example, the ``\n`` is treated as two separate characters (a backslash followed by 'n') rather than a newline character. This is @@ -545,72 +486,6 @@ code, which may return a string or any other type. There is no canonical way to convert a Template to a string. -Interleaving of ``Template.args`` ---------------------------------- - -In the ``Template`` type, the ``args`` attribute is a tuple that always -alternates between string literals and ``Interpolation`` instances. Specifically: - -- Even-indexed elements (0, 2, 4, ...) are always of type ``str``, representing - the literal parts of the template. -- Odd-indexed elements (1, 3, 5, ...) are always ``Interpolation`` instances, - representing the interpolated expressions. - -For example, the following assertions hold: - -.. code-block:: python - - name = "World" - template = t"Hello {name}" - assert len(template.args) == 3 - assert template.args[0] == "Hello " - assert template.args[1].value == "World" - assert template.args[2] == "" - -These rules imply that the ``args`` attribute will always have an odd length. -As a consequence, empty strings are added to the tuple when the template -begins or ends with an interpolation, or when two interpolations are adjacent: - -.. code-block:: python - - a, b = "a", "b" - template = t"{a}{b}" - assert len(template.args) == 5 - assert template.args[0] == "" - assert template.args[1].value == "a" - assert template.args[2] == "" - assert template.args[3].value == "b" - assert template.args[4] == "" - -Most template processing code will not care about this detail and will use -one or more of: - -- Structural pattern matching -- ``isinstance()`` checks -- The ``strings``, ``interpolations``, and ``values`` properties - -For example: - -.. code-block:: python - - name = "World" - template = t"Hello {name}!" - assert template.strings == ("Hello ", "!") - assert template.interpolations == (Interpolation(value=name, expr="name"),) - assert template.values == (name,) - -Interleaving allows for performance optimizations in template processing code. -For example, a template processor could cache the static parts of the template -and only reprocess the dynamic parts when the template is evaluated with -different values. - -Interleaving is an invariant maintained by the ``Template`` class. Developers can -take advantage of it but they are not required to themselves maintain it. -Specifically, a ``Template`` can be constructed with ``str`` and -``Interpolation`` instances in *any* order; they will automatically be -"interleaved" as necessary before ``args`` is assigned. - - Examples ======== @@ -653,8 +528,8 @@ specifiers like ``:.2f``. The full code is fairly simple: def f(template: Template) -> str: parts = [] - for arg in template.args: - match arg: + for item in template: + match item: case str() as s: parts.append(s) case Interpolation(value, _, conv, format_spec): @@ -728,9 +603,9 @@ We can implement an improved version of ``StructuredMessage`` using template str @property def values(self) -> Mapping[str, object]: return { - arg.expr: arg.value - for arg in self.template.args - if isinstance(arg, Interpolation) + item.expr: item.value + for item in self.template + if isinstance(item, Interpolation) } def __str__(self) -> str: @@ -789,9 +664,9 @@ and a ``ValuesFormatter`` for JSON output: class ValuesFormatter(Formatter): def values(self, template: Template) -> Mapping[str, Any]: return { - arg.expr: arg.value - for arg in template.args - if isinstance(arg, Interpolation) + item.expr: item.value + for item in template + if isinstance(item, Interpolation) } def format(self, record: LogRecord) -> str: @@ -912,13 +787,13 @@ developers will use t-strings than write t-string processing functions. Over time, a small number of more advanced developers *will* wish to author their own template processing code. Writing processing code often requires thinking -in terms of formal grammars. Developers will need to learn how to parse the -``args`` attribute of a ``Template`` instance and how to process interpolations -in a context-sensitive fashion. More sophisticated grammars will likely require -parsing to intermediate representations like an AST. Great template processing -code will handle format specifiers and conversions when appropriate. Writing -production-grade template processing code -- for instance, to support HTML -templates -- can be a large undertaking. +in terms of formal grammars. Developers will need to learn how to work with the +``strings`` and ``interpolation`` attributes of a ``Template`` instance and how +to process interpolations in a context-sensitive fashion. More sophisticated +grammars will likely require parsing to intermediate representations like an +AST. Great template processing code will handle format specifiers and conversions +when appropriate. Writing production-grade template processing code -- for +instance, to support HTML templates -- can be a large undertaking. We expect that template strings will provide framework authors with a powerful new tool in their toolbox. While the functionality of template strings overlaps @@ -949,7 +824,7 @@ Common Patterns Seen in Processing Templates Structural Pattern Matching --------------------------- -Iterating over the ``Template.args`` with structural pattern matching is the expected +Iterating over the ``Template`` with structural pattern matching is the expected best practice for many template function implementations: .. code-block:: python @@ -957,8 +832,8 @@ best practice for many template function implementations: from TBD import Template, Interpolation def process(template: Template) -> Any: - for arg in template.args: - match arg: + for item in template: + match item: case str() as s: ... # handle each string part case Interpolation() as interpolation: @@ -1091,8 +966,8 @@ in a ``lambda`` in the template string literal: name = "World" template = t"Hello {(lambda: name)}" - assert callable(template.args[1].value) - assert template.args[1].value() == "World" + assert callable(template.interpolations[0].value) + assert template.interpolations[0].value() == "World" This assumes, of course, that template processing code anticipates and handles callable interpolation values. (One could imagine also supporting iterators, @@ -1320,6 +1195,35 @@ types be runtime checkable protocols rather than classes. In the end, we felt that using classes was more straightforward. +Overridden ``__eq__`` and ``__hash__`` for ``Template`` and ``Interpolation`` +----------------------------------------------------------------------------- + +Earlier versions of this PEP proposed that the ``Template`` and ``Interpolation`` +types should have their own implementations of ``__eq__`` and ``__hash__``. + +``Templates`` were considered equal if their ``strings`` and ``interpolations`` +were equal; ``Interpolations`` were considered equal if their ``value``, ``expr``, +``conv``, and ``format_spec`` were equal. Interpolation hashing was similar to +tuple hashing: an ``Interpolation`` was hashable if and only if its ``value`` +was hashable. + +This was rejected because ``Template.__hash__`` so defined was not useful as a +cache key in template processing code; we were concerned that it would be +confusing to developers. We also felt that tuple hashing, while well established +in Python, is not something we want to see more of as the language evolves. + +By dropping these implementations of ``__eq__`` and ``__hash__``, we lose the +ability to write asserts such as: + +.. code-block::python + + name = "World" + assert t"Hello " + t"{name}" == t"Hello {name}" + +Because ``Template`` instances are intended to be quickly processed by further +code, we felt that the utility of these asserts was limited. + + An Additional ``Decoded`` Type ------------------------------ @@ -1369,7 +1273,7 @@ source text: value = 42 precision = 2 template = t"Value: {value:.{precision}f}" - assert template.args[1].format_spec == ".2f" + assert template.interpolations[0].format_spec == ".2f" We do not anticipate that these limitations will be a significant issue in practice. Developers who need to obtain the original template string literal can always @@ -1447,11 +1351,12 @@ This was rejected in favor of keeping t-string syntax as close to f-string synta as possible. -Alternate Layouts for ``Template.args`` ---------------------------------------- +Alternate Layouts for ``Template`` +----------------------------------- During the development of this PEP, we considered several alternate layouts for -the ``args`` attribute of the ``Template`` type. This included: +the contents of ``Templates``. Many focused on a single ``args`` tuple that +contained both strings and interpolations. Variants included: - Instead of ``args``, ``Template`` contains a ``strings`` attribute of type ``tuple[str, ...]`` and an ``interpolations`` attribute of type From 2611119535a83bbfa0facbe7ace875b1f741c08e Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 13 Jan 2025 12:29:19 -0800 Subject: [PATCH 28/52] Remove final assert reliance on __eq__ --- peps/pep-0750.rst | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 7ed2af97563..5b6b8a6c89c 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -352,11 +352,15 @@ and a ``str``: .. code-block:: python name = "World" - template1 = t"Hello " - template2 = t"{name}" - assert template1 + template2 == t"Hello {name}" - assert template1 + "!" == t"Hello !" - assert "Hello " + template2 == t"Hello {name}" + template = t"{name}" + + assert isinstance(t"Hello " + template, Template) + assert (t"Hello " + template).strings == ("Hello ", "") + assert (t"Hello " + template).interpolations[0].value == "World" + + assert isinstance("Hello " + template, Template) + assert ("Hello " + template).strings == ("Hello ", "") + assert ("Hello " + template).interpolations[0].value == "World" Concatenation of templates is "viral": the concatenation of a ``Template`` and a ``str`` always results in a ``Template`` instance. @@ -367,11 +371,8 @@ will work as expected: .. code-block:: python name = "World" - template = t"Hello " "World" - assert template == t"Hello World" - template2 = t"Hello " t"World" - assert template2 == t"Hello World" - + assert (t"Hello " t"World").strings == ("Hello World",) + assert ("Hello " t"World").strings == ("Hello World",) The ``Template`` type implements the ``__add__()`` and ``__radd__()`` methods roughly as follows: @@ -390,7 +391,7 @@ Template and Interpolation Equality ----------------------------------- ``Template`` and ``Interpolation`` instances use the standard Python equality -semantics from the ``object`` class. No attempt is made to provide a custom +semantics from the ``object`` class. No attempt is made to provide an overridden implementation of ``__eq__()`` or ``__hash__()``. ``Template`` instances are intended to be used by template processing code, @@ -440,9 +441,9 @@ Raw template strings are supported using the ``rt`` (or ``tr``) prefix: .. code-block:: python trade = 'shrubberies' - t = rt'Did you say "{trade}"?\n' - assert t.strings[0] == r'Did you say "' - assert t.strings[1] == r'"?\n' + template = rt'Did you say "{trade}"?\n' + assert template.strings[0] == r'Did you say "' + assert template.strings[1] == r'"?\n' In this example, the ``\n`` is treated as two separate characters (a backslash followed by 'n') rather than a newline character. This is From 4893f396c88c5a6cc986dc2a7b3caeb6ff0a2cda Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 13 Jan 2025 13:25:32 -0800 Subject: [PATCH 29/52] Fix indentation issue --- peps/pep-0750.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 5b6b8a6c89c..0cc46c3744d 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -16,7 +16,6 @@ Post-History: `09-Aug-2024 `__, `21-Oct-2024 `__, `18-Nov-2024 `__ - Abstract ======== From d567dc1f367d15518084ae3768d39d98514e2464 Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 13 Jan 2025 13:25:57 -0800 Subject: [PATCH 30/52] Add space after docstring --- peps/pep-0750.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 0cc46c3744d..61d154ecdf7 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -140,6 +140,7 @@ Template strings evaluate to an instance of a new type, ``<>.Template``: class Template: """Simulated implementation of the PEP750 Template type.""" + strings: tuple[str, ...] """ A tuple of the N+1 string parts of the template, where N is From 4c520f12d2425269ddf05459943e2dc5a573ef71 Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 13 Jan 2025 13:26:23 -0800 Subject: [PATCH 31/52] Remove extraneous docstring --- peps/pep-0750.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 61d154ecdf7..1276b0ff411 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -139,8 +139,6 @@ Template strings evaluate to an instance of a new type, ``<>.Template``: .. code-block:: python class Template: - """Simulated implementation of the PEP750 Template type.""" - strings: tuple[str, ...] """ A tuple of the N+1 string parts of the template, where N is From 91de513b5a57024950ede88fff99e10f4ed4d1ae Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 13 Jan 2025 14:01:18 -0800 Subject: [PATCH 32/52] Minor fixes --- peps/pep-0750.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 1276b0ff411..6ed180b0211 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -176,7 +176,7 @@ Template strings evaluate to an instance of a new type, ``<>.Template``: """ ... -The ``strings`` and ``interpolation`` attributes provide access to the string +The ``strings`` and ``interpolations`` attributes provide access to the string parts and any interpolations in the literal: .. code-block:: python @@ -186,7 +186,7 @@ parts and any interpolations in the literal: assert template.strings[0] == "Hello " assert template.interpolations[0].value == "World" -The ``Template`` type is shallow immutable. Its attributes cannot be reassigned. +The ``Template`` type is immutable. The ``Interpolation`` Type @@ -214,7 +214,7 @@ Like ``Template``, it is a new class found in the ``<>`` module: ): ... -Like ``Template``, ``Interpolation`` is shallow immutable. Its attributes +The ``Interpolation`` type is shallow immutable. Its attributes cannot be reassigned. The ``value`` attribute is the evaluated result of the interpolation: From 8aad1925f739c65be699b12cb800cf1752401304 Mon Sep 17 00:00:00 2001 From: Dave Peck Date: Mon, 13 Jan 2025 14:32:36 -0800 Subject: [PATCH 33/52] Update peps/pep-0750.rst Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- peps/pep-0750.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 6ed180b0211..b7b00f326b6 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -14,7 +14,7 @@ Python-Version: 3.14 Post-History: `09-Aug-2024 `__, `17-Oct-2024 `__, `21-Oct-2024 `__, - `18-Nov-2024 `__ + `18-Nov-2024 `__, Abstract ======== From 3812b7c90ce0b7eb3c5eb4ead6abac0bc36af3b4 Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 13 Jan 2025 14:37:17 -0800 Subject: [PATCH 34/52] Formatting fix --- peps/pep-0750.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 6ed180b0211..ca418d97661 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -288,7 +288,7 @@ Convenience Accessors in ``Template`` The ``Template.values`` property is equivalent to: -.. code-block::python +.. code-block:: python @property def values(self) -> tuple[object, ...]: @@ -297,7 +297,7 @@ The ``Template.values`` property is equivalent to: The ``Template.__iter__()`` method is equivalent to: -.. code-block::python +.. code-block:: python def __iter__(self) -> Iterator[str | Interpolation]: for s, i in zip_longest(self.strings, self.interpolations): @@ -1214,7 +1214,7 @@ in Python, is not something we want to see more of as the language evolves. By dropping these implementations of ``__eq__`` and ``__hash__``, we lose the ability to write asserts such as: -.. code-block::python +.. code-block:: python name = "World" assert t"Hello " + t"{name}" == t"Hello {name}" From c024e855bef10499c6215465963a6cb5ae07ecd9 Mon Sep 17 00:00:00 2001 From: Dave Peck Date: Thu, 16 Jan 2025 12:36:38 -0800 Subject: [PATCH 35/52] Update peps/pep-0750.rst Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- peps/pep-0750.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 51f1273803b..eef0f9768b1 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -6,7 +6,7 @@ Author: Jim Baker , Koudai Aono , Lysandros Nikolaou , Dave Peck -Discussions-To: https://discuss.python.org/t/pep750-template-strings-new-updates/71594 +Discussions-To: https://discuss.python.org/t/71594 Status: Draft Type: Standards Track Created: 08-Jul-2024 From 0b7e15f84753a62f596e55eb889511e0faf6a14f Mon Sep 17 00:00:00 2001 From: Dave Peck Date: Thu, 16 Jan 2025 12:37:29 -0800 Subject: [PATCH 36/52] Update peps/pep-0750.rst Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- peps/pep-0750.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index eef0f9768b1..c134a9a0777 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -141,9 +141,9 @@ Template strings evaluate to an instance of a new type, ``<>.Template``: class Template: strings: tuple[str, ...] """ - A tuple of the N+1 string parts of the template, where N is - the number of interpolations. This will always contain at least one - element. + A non-empty tuple of the string parts of the template, + with N+1 items, where N is the number of interpolations + in the template. """ interpolations: tuple[Interpolation, ...] From d5bae6b004f7c5792c0a0126e6ff4a325318fc78 Mon Sep 17 00:00:00 2001 From: Dave Peck Date: Thu, 16 Jan 2025 12:37:50 -0800 Subject: [PATCH 37/52] Update peps/pep-0750.rst Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- peps/pep-0750.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index c134a9a0777..75ca9bb386e 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -148,8 +148,8 @@ Template strings evaluate to an instance of a new type, ``<>.Template``: interpolations: tuple[Interpolation, ...] """ - A tuple of the N interpolations in the template. This can - be an empty tuple. + A tuple of the interpolation parts of the template. + This will be an empty tuple if there are no interpolations. """ def __new__(cls, *args: str | Interpolation): From c07dd33a5cfbeb8d3c01f90ffb1f18fda8ca5853 Mon Sep 17 00:00:00 2001 From: Dave Peck Date: Thu, 16 Jan 2025 12:38:17 -0800 Subject: [PATCH 38/52] Update peps/pep-0750.rst Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- peps/pep-0750.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 75ca9bb386e..9f7c7c4aaee 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -164,7 +164,8 @@ Template strings evaluate to an instance of a new type, ``<>.Template``: def values(self) -> tuple[object, ...]: """ Return a tuple of the `value` attributes of each Interpolation - in the template. This can be an empty tuple. + in the template. + This will be an empty tuple if there are no interpolations. """ ... From ed3fb052df712c18933fc192e899308c4cbb3a17 Mon Sep 17 00:00:00 2001 From: Dave Peck Date: Thu, 16 Jan 2025 12:38:52 -0800 Subject: [PATCH 39/52] Update peps/pep-0750.rst Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- peps/pep-0750.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 9f7c7c4aaee..a2673a7198a 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -134,7 +134,7 @@ see the `Raw Template Strings`_ section below for more information. The ``Template`` Type --------------------- -Template strings evaluate to an instance of a new type, ``<>.Template``: +Template strings evaluate to an instance of a new immutable type, ``<>.Template``: .. code-block:: python From bdad99bf12bfcfc2391749fee014a8fae3b73bae Mon Sep 17 00:00:00 2001 From: Dave Peck Date: Thu, 16 Jan 2025 12:39:31 -0800 Subject: [PATCH 40/52] Update peps/pep-0750.rst Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- peps/pep-0750.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index a2673a7198a..8ae51a7456a 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -187,7 +187,6 @@ parts and any interpolations in the literal: assert template.strings[0] == "Hello " assert template.interpolations[0].value == "World" -The ``Template`` type is immutable. The ``Interpolation`` Type From cb3e46346874714003fb51d1411bb5a96d53b151 Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 16 Jan 2025 12:52:14 -0800 Subject: [PATCH 41/52] Fix __add__ and __radd__. Whoops. --- peps/pep-0750.rst | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 8ae51a7456a..4619c27b4f4 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -372,17 +372,9 @@ will work as expected: assert (t"Hello " t"World").strings == ("Hello World",) assert ("Hello " t"World").strings == ("Hello World",) -The ``Template`` type implements the ``__add__()`` and ``__radd__()`` methods -roughly as follows: - -.. code-block:: python - - class Template: - def __add__(self, other: object) -> Template: - return Template(*self, other) - - def __radd__(self, other: object) -> Template: - return Template(other, *self) +The ``Template`` type supports the ``__add__()`` and ``__radd__()`` methods +between two ``Template`` instances and between a ``Template`` instance and a +``str``. Template and Interpolation Equality From dafd4e1f6caa34156dad6fc011c18fdf13bdca6a Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 16 Jan 2025 12:54:03 -0800 Subject: [PATCH 42/52] Simplify --- peps/pep-0750.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 4619c27b4f4..2a3311463ce 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -380,9 +380,8 @@ between two ``Template`` instances and between a ``Template`` instance and a Template and Interpolation Equality ----------------------------------- -``Template`` and ``Interpolation`` instances use the standard Python equality -semantics from the ``object`` class. No attempt is made to provide an overridden -implementation of ``__eq__()`` or ``__hash__()``. +``Template`` and ``Interpolation`` instances compare with object identity +(``is``). ``Template`` instances are intended to be used by template processing code, which may return a string or any other type. Those types can provide their From 5e18658755464626316e3e9574e198d539ac1ba0 Mon Sep 17 00:00:00 2001 From: Dave Peck Date: Thu, 16 Jan 2025 12:54:49 -0800 Subject: [PATCH 43/52] Update peps/pep-0750.rst Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- peps/pep-0750.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 2a3311463ce..e7ea1f36762 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -385,7 +385,7 @@ Template and Interpolation Equality ``Template`` instances are intended to be used by template processing code, which may return a string or any other type. Those types can provide their -own custom equality semantics as needed. +own equality semantics as needed. No Support for Ordering From 4ce7da5cf865ec76d8cea389c6df83e089f81afb Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 16 Jan 2025 12:57:28 -0800 Subject: [PATCH 44/52] Clean up make_alas --- peps/pep-0750.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index e7ea1f36762..3111bfd23bf 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -1049,15 +1049,15 @@ a ``Template`` instance: .. code-block:: python - def make_alas(*, cheese: str) -> Template: + def make_template(*, cheese: str) -> Template: return t"We're all out of {cheese}." - alas_t = make_alas(cheese="Red Leicester") + template = make_template(cheese="Red Leicester") # Using the f() function from the f-string example, above - assert f(alas_t) == "We're all out of Red Leicester." + assert f(template) == "We're all out of Red Leicester." -The ``make_alas()`` function itself can be thought of as analogous to the -format string. The call to ``make_alas()`` is analogous to the call to +The ``make_template()`` function itself can be thought of as analogous to the +format string. The call to ``make_template()`` is analogous to the call to :meth:`str.format`. Of course, it is common to load format strings from external sources like a @@ -1072,10 +1072,10 @@ old-style format string and returns an equivalent ``Template`` instance: ... # Load this from a file, database, etc. - alas_fmt = "We're all out of {cheese}." - alas_t = from_format(alas_fmt, cheese="Red Leicester") + fmt = "We're all out of {cheese}." + template = from_format(fmt, cheese="Red Leicester") # Using the f() function from the f-string example, above - assert f(alas_t) == "We're all out of Red Leicester." + assert f(template) == "We're all out of Red Leicester." This is a powerful pattern that allows developers to use template strings in places where they might have previously used format strings. A full implementation From c6a7d5a3e6ff8991c2e5c37e7e174d0f665ec37e Mon Sep 17 00:00:00 2001 From: Dave Peck Date: Thu, 16 Jan 2025 12:58:28 -0800 Subject: [PATCH 45/52] Update peps/pep-0750.rst Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- peps/pep-0750.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 3111bfd23bf..3e2dacdf02e 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -1079,9 +1079,8 @@ old-style format string and returns an equivalent ``Template`` instance: This is a powerful pattern that allows developers to use template strings in places where they might have previously used format strings. A full implementation -of ``from_format()`` is available in the examples repository. It supports the -full grammar of format strings including positional and keyword arguments, -automatic and manual field numbering, etc. +of ``from_format()`` is available in the examples repository, +which supports the full grammar of format strings. .. note:: Example code From 2faca7641273deeb23f6cbb2fa1f3dbb5d0cf863 Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 16 Jan 2025 13:02:41 -0800 Subject: [PATCH 46/52] Convert() only --- peps/pep-0750.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 3111bfd23bf..103c441f255 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -1234,8 +1234,8 @@ team consensus has emerged on the final location for these types. The current PEP leaves this open for a final decision. One argument in favor of a new top-level ``templatelib`` module is that it would -allow for future addition of related methods (like ``convert()`` and ``from_format_string()``) -and for potential future template processing code to be added to submodules +allow for future addition of related methods (like ``convert()``) and for +potential future template processing code to be added to submodules (``templatelib.shell``, etc.). From 22a2e96de32eea9b6450feba4032e7960eca8f8a Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 16 Jan 2025 13:07:36 -0800 Subject: [PATCH 47/52] Mention __repr__ --- peps/pep-0750.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 8040751518d..97bf43a9889 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -468,13 +468,14 @@ literals. No ``Template.__str__()`` Implementation ---------------------------------------- -The ``Template`` type does not provide a specialized ``__str__()`` implementation; -it inherits the default implementation from the ``object`` class. +The ``Template`` type does not provide a specialized ``__str__()`` implementation. This is because ``Template`` instances are intended to be used by template processing code, which may return a string or any other type. There is no canonical way to convert a Template to a string. +``Template.__repr__()`` returns a simple representation suitable for debugging. + Examples ======== From 67a7b7c782a9acf93f6ace13c6032b0a103d0d34 Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 16 Jan 2025 13:14:43 -0800 Subject: [PATCH 48/52] Explicitly define __repr__ for Template and Interpolation. --- peps/pep-0750.rst | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 97bf43a9889..1221ed5ab10 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -474,7 +474,15 @@ This is because ``Template`` instances are intended to be used by template proce code, which may return a string or any other type. There is no canonical way to convert a Template to a string. -``Template.__repr__()`` returns a simple representation suitable for debugging. + +``Template.__repr__()`` and ``Interpolation.__repr__()`` +-------------------------------------------------------- + +``Template.__repr__()`` returns a dataclass-like representation useful for +debugging: ``"Template(strings=(...), interpolations=(...))"``. + +``Interpolation.__repr__()`` returns a similar representation: +``"Interpolation(value=..., expr='...', conv=None, format_spec='')"``. Examples From d05f26549db066ec625eda77fc2635b7336ff612 Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 16 Jan 2025 13:15:54 -0800 Subject: [PATCH 49/52] Simplify --- peps/pep-0750.rst | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 1221ed5ab10..6db115f6a31 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -474,15 +474,8 @@ This is because ``Template`` instances are intended to be used by template proce code, which may return a string or any other type. There is no canonical way to convert a Template to a string. - -``Template.__repr__()`` and ``Interpolation.__repr__()`` --------------------------------------------------------- - -``Template.__repr__()`` returns a dataclass-like representation useful for -debugging: ``"Template(strings=(...), interpolations=(...))"``. - -``Interpolation.__repr__()`` returns a similar representation: -``"Interpolation(value=..., expr='...', conv=None, format_spec='')"``. +The ``Template`` and ``Interpolation`` types both provide useful ``__repr__()`` +implementations. Examples From fd45b5f7e37dfe312f4171acc331b894cf8ed8bb Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 16 Jan 2025 13:18:40 -0800 Subject: [PATCH 50/52] Add Andrea Giammarchi to acknowledgements --- peps/pep-0750.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 6db115f6a31..549d59bb45c 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -1398,6 +1398,7 @@ Acknowledgements Thanks to Ryan Morshead for contributions during development of the ideas leading to template strings. Special mention also to Dropbox's `pyxl `_ for tackling similar ideas years ago. +Andrea Giammarchi provided thoughtful feedback on the early drafts of this PEP. Finally, thanks to Joachim Viide for his pioneering work on the `tagged library `_. Tagged was not just the precursor to template strings, but the place where the whole effort started via a GitHub issue From 62398236f310a88caca8759588926d953b90922f Mon Sep 17 00:00:00 2001 From: Dave Peck Date: Thu, 16 Jan 2025 14:20:09 -0800 Subject: [PATCH 51/52] Update peps/pep-0750.rst Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- peps/pep-0750.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 549d59bb45c..7817c832c79 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -188,7 +188,6 @@ parts and any interpolations in the literal: assert template.interpolations[0].value == "World" - The ``Interpolation`` Type -------------------------- From 31472c3241b74224a3dda0a4592edb5e047b2157 Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 16 Jan 2025 14:22:11 -0800 Subject: [PATCH 52/52] Interpolations --- peps/pep-0750.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 7817c832c79..8a1634a6315 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -1199,8 +1199,7 @@ was hashable. This was rejected because ``Template.__hash__`` so defined was not useful as a cache key in template processing code; we were concerned that it would be -confusing to developers. We also felt that tuple hashing, while well established -in Python, is not something we want to see more of as the language evolves. +confusing to developers. By dropping these implementations of ``__eq__`` and ``__hash__``, we lose the ability to write asserts such as: