From 9fa905f8e6a36aa8e42fc8cee3d16c2ef608dd4e Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 7 Apr 2025 09:00:59 -0700 Subject: [PATCH 01/13] Specify module location as `string.templatelib` --- peps/pep-0750.rst | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 7ed1f858946..e56157bb1bd 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -109,13 +109,13 @@ 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 the standard library -module ``<>``. +module ``string.templatelib``. The following code creates a ``Template`` instance: .. code-block:: python - from TBD import Template + from string.templatelib import Template template = t"This is a template string." assert isinstance(template, Template) @@ -134,7 +134,8 @@ see the `Raw Template Strings`_ section below for more information. The ``Template`` Type --------------------- -Template strings evaluate to an instance of a new immutable type, ``<>.Template``: +Template strings evaluate to an instance of a new immutable type, +``string.templatelib.Template``: .. code-block:: python @@ -192,7 +193,7 @@ The ``Interpolation`` Type -------------------------- The ``Interpolation`` type represents an expression inside a template string. -Like ``Template``, it is a new class found in the ``<>`` module: +Like ``Template``, it is a new class found in the ``string.templatelib`` module: .. code-block:: python @@ -315,7 +316,7 @@ interpolations in uppercase: .. code-block:: python - from TBD import Template, Interpolation + from string.templatelib import Template, Interpolation def lower_upper(template: Template) -> str: """Render static parts lowercased and interpolations uppercased.""" @@ -506,7 +507,7 @@ specifiers like ``:.2f``. The full code is fairly simple: .. code-block:: python - from TBD import Template, Interpolation + from string.templatelib import Template, Interpolation def convert(value: object, conv: Literal["a", "r", "s"] | None) -> object: if conv == "a": @@ -579,7 +580,7 @@ We can implement an improved version of ``StructuredMessage`` using template str .. code-block:: python import json - from TBD import Interpolation, Template + from string.templatelib import Interpolation, Template from typing import Mapping class TemplateMessage: @@ -636,7 +637,7 @@ and a ``ValuesFormatter`` for JSON output: import json from logging import Formatter, LogRecord - from TBD import Interpolation, Template + from string.templatelib import Interpolation, Template from typing import Any, Mapping @@ -820,7 +821,7 @@ best practice for many template function implementations: .. code-block:: python - from TBD import Template, Interpolation + from string.templatelib import Template, Interpolation def process(template: Template) -> Any: for item in template: @@ -1228,14 +1229,8 @@ The Final Home for ``Template`` and ``Interpolation`` Previous versions of this PEP proposed placing the ``Template`` and ``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 for -potential future template processing code to be added to submodules -(``templatelib.shell``, etc.). +and even in a new top-level module, ``templatelib``. The final decision was to +place them in ``string.templatelib``. Enable Full Reconstruction of Original Template Literal From 3e38704ecfaa861cfb65435589a48b1ee12dce82 Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 7 Apr 2025 09:08:42 -0700 Subject: [PATCH 02/13] Use fully spelled-out attribute names for `conv` and `expr`. --- peps/pep-0750.rst | 94 ++++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 45 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index e56157bb1bd..53a9f0ee0e1 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -199,17 +199,17 @@ Like ``Template``, it is a new class found in the ``string.templatelib`` module: class Interpolation: value: object - expr: str - conv: Literal["a", "r", "s"] | None + expression: str + conversion: Literal["a", "r", "s"] | None format_spec: str - __match_args__ = ("value", "expr", "conv", "format_spec") + __match_args__ = ("value", "expression", "conversion", "format_spec") def __new__( cls, value: object, - expr: str, - conv: Literal["a", "r", "s"] | None = None, + expression: str, + conversion: Literal["a", "r", "s"] | None = None, format_spec: str = "", ): ... @@ -225,20 +225,21 @@ The ``value`` attribute is the evaluated result of the interpolation: template = t"Hello {name}" assert template.interpolations[0].value == "World" -The ``expr`` attribute is the *original text* of the interpolation: +The ``expression`` attribute is the *original text* of the interpolation: .. code-block:: python name = "World" template = t"Hello {name}" - assert template.interpolations[0].expr == "name" + assert template.interpolations[0].expression == "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. -See both the `Common Patterns Seen in Processing Templates`_ section and the -`Examples`_ section for more information on how to process template strings. +We expect that the ``expression`` attribute will not be used in most template +processing code. It is provided for completeness and for use in debugging and +introspection. See both the `Common Patterns Seen in Processing Templates`_ +section and the `Examples`_ section for more information on how to process +template strings. -The ``conv`` attribute is the :ref:`optional conversion ` +The ``conversion`` attribute is the :ref:`optional conversion ` to be used, one of ``r``, ``s``, and ``a``, corresponding to ``repr()``, ``str()``, and ``ascii()`` conversions. As with f-strings, no other conversions are supported: @@ -247,9 +248,9 @@ are supported: name = "World" template = t"Hello {name!r}" - assert template.interpolations[0].conv == "r" + assert template.interpolations[0].conversion == "r" -If no conversion is provided, ``conv`` is ``None``. +If no conversion is provided, ``conversion`` is ``None``. The ``format_spec`` attribute is the :ref:`format specification `. As with f-strings, this is an arbitrary string that defines how to present the value: @@ -276,7 +277,7 @@ string (``""``). This matches the ``format_spec`` parameter of Python's :func:`python:format` built-in. Unlike f-strings, it is up to code that processes the template to determine how to -interpret the ``conv`` and ``format_spec`` attributes. +interpret the ``conversion`` and ``format_spec`` attributes. Such code is not required to use these attributes, but when present they should be respected, and to the extent possible match the behavior of f-strings. It would be surprising if, for example, a template string that uses ``{value:.2f}`` @@ -405,7 +406,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!r}'``: +In particular, ``t'{expression=}'`` is treated as ``t'expression={expression!r}'``: .. code-block:: python @@ -413,13 +414,13 @@ In particular, ``t'{expr=}'`` is treated as ``t'expr={expr!r}'``: template = t"Hello {name=}" assert template.strings[0] == "Hello name=" assert template.interpolations[0].value == "World" - assert template.interpolations[0].conv == "r" + assert template.interpolations[0].conversion == "r" -If a separate format string is also provided, ``t'{expr=:fmt}`` is treated instead as -``t'expr={expr!s:fmt}'``. +If a separate format string is also provided, ``t'{expression=:fmt}`` is treated +instead as ``t'expression={expression!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'{expression = }'`` is +treated as ``t'expression = {expression!r}'``. Raw Template Strings @@ -509,12 +510,12 @@ specifiers like ``:.2f``. The full code is fairly simple: from string.templatelib import Template, Interpolation - def convert(value: object, conv: Literal["a", "r", "s"] | None) -> object: - if conv == "a": + def convert(value: object, conversion: Literal["a", "r", "s"] | None) -> object: + if conversion == "a": return ascii(value) - elif conv == "r": + elif conversion == "r": return repr(value) - elif conv == "s": + elif conversion == "s": return str(value) return value @@ -524,8 +525,8 @@ specifiers like ``:.2f``. The full code is fairly simple: match item: case str() as s: parts.append(s) - case Interpolation(value, _, conv, format_spec): - value = convert(value, conv) + case Interpolation(value, _, conversion, format_spec): + value = convert(value, conversion) value = format(value, format_spec) parts.append(value) return "".join(parts) @@ -595,7 +596,7 @@ We can implement an improved version of ``StructuredMessage`` using template str @property def values(self) -> Mapping[str, object]: return { - item.expr: item.value + item.expression: item.value for item in self.template if isinstance(item, Interpolation) } @@ -615,7 +616,8 @@ class. With template strings it is no longer necessary for developers to make sure that their format string and values dictionary are kept in sync; a single template string literal is all that is needed. The ``TemplateMessage`` implementation can automatically extract structured keys and values from -the ``Interpolation.expr`` and ``Interpolation.value`` attributes, respectively. +the ``Interpolation.expression`` and ``Interpolation.value`` attributes, +respectively. Approach 2: Custom Formatters @@ -656,7 +658,7 @@ and a ``ValuesFormatter`` for JSON output: class ValuesFormatter(Formatter): def values(self, template: Template) -> Mapping[str, Any]: return { - item.expr: item.value + item.expression: item.value for item in template if isinstance(item, Interpolation) } @@ -1193,10 +1195,10 @@ 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. +were equal; ``Interpolations`` were considered equal if their ``value``, +``expression``, ``conversion``, 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 @@ -1244,9 +1246,10 @@ There are several limitations with respect to round-tripping to the original source text: - ``Interpolation.format_spec`` defaults to ``""`` if not provided. It is therefore - impossible to distinguish ``t"{expr}"`` from ``t"{expr:}"``. + impossible to distinguish ``t"{expression}"`` from ``t"{expression:}"``. - The debug specifier, ``=``, is treated as a special case. It is therefore not - possible to distinguish ``t"{expr=}"`` from ``t"expr={expr}"``. + possible to distinguish ``t"{expression=}"`` from + ``t"expression={expression}"``. - Finally, format specifiers in f-strings allow arbitrary nesting. In this PEP and in the reference implementation, the specifier is eagerly evaluated to set the ``format_spec`` in the ``Interpolation``, thereby losing @@ -1302,23 +1305,24 @@ PEP adheres closely to :pep:`701`. Any changes to allowed values should be in a separate PEP. -Removing ``conv`` From ``Interpolation`` ----------------------------------------- +Removing ``conversion`` From ``Interpolation`` +---------------------------------------------- -During the authoring of this PEP, we considered removing the ``conv`` attribute -from ``Interpolation`` and specifying that the conversion should be performed -eagerly, before ``Interpolation.value`` is set. +During the authoring of this PEP, we considered removing the ``conversion`` +attribute from ``Interpolation`` and specifying that the conversion should be +performed eagerly, before ``Interpolation.value`` is set. This was done to simplify the work of writing template processing code. The -``conv`` attribute is of limited extensibility (it is typed as +``conversion`` attribute is of limited extensibility (it is typed as ``Literal["r", "s", "a"] | None``). It is not clear that it adds significant value or flexibility to template strings that couldn't better be achieved with custom format specifiers. Unlike with format specifiers, there is no -equivalent to Python's :func:`python:format` built-in. (Instead, we include an +equivalent to Python's :func:`python:format` built-in. (Instead, we include a sample implementation of ``convert()`` in the `Examples`_ section.) -Ultimately we decided to keep the ``conv`` attribute in the ``Interpolation`` type -to maintain compatibility with f-strings and to allow for future extensibility. +Ultimately we decided to keep the ``conversion`` attribute in the +``Interpolation`` type to maintain compatibility with f-strings and to allow +for future extensibility. Alternate Interpolation Symbols From 61217cffa46120114fc6210409a4efb4933a006a Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 7 Apr 2025 09:43:01 -0700 Subject: [PATCH 03/13] First cut at more detailed section on __iter__() --- peps/pep-0750.rst | 56 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 53a9f0ee0e1..9650afdf583 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -284,10 +284,11 @@ 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.values`` Property +-------------------------------- -The ``Template.values`` property is equivalent to: +The ``Template.values`` property is a shortcut for accessing the ``value`` +attribute of each ``Interpolation`` in the template and is equivalent to: .. code-block:: python @@ -296,7 +297,14 @@ The ``Template.values`` property is equivalent to: return tuple(i.value for i in self.interpolations) -The ``Template.__iter__()`` method is equivalent to: +Iterating ``Template`` Contents +------------------------------- + +The ``Template.__iter__()`` method provides a simple way to access the full +contents of a template. It yields the string parts and interpolations in +the order they appear, with empty strings omitted. + +The ``__iter__()`` method is equivalent to: .. code-block:: python @@ -308,6 +316,46 @@ The ``Template.__iter__()`` method is equivalent to: yield i +The following examples show the ``__iter__()`` method in action: + +.. code-block:: python + + assert list(t"") == [] + + assert list(t"Hello") == ["Hello"] + + name = "World" + template = t"Hello {name}!" + contents = list(template) + assert len(contents) == 3 + assert contents[0] == "Hello " + assert contents[1].value == "World" + assert contents[1].expression == "name" + assert contents[2] == "!" + +Empty strings, which may be present in ``Template.strings``, are not included +in the output of the ``__iter__()`` method: + +.. code-block:: python + + first = "Eat" + second = "Roquefort" + template = t"{first}{second}" + contents = list(template) + assert len(contents) == 2 + assert contents[0].value == "Eat" + assert contents[0].expression == "first" + assert contents[1].value == "Roquefort" + assert contents[1].expression == "second" + + # However, the strings attribute contains empty strings: + assert template.strings == ("", "", "") + +Template processing code can choose to work with any combination of +``strings``, ``interpolations``, ``values``, and ``__iter__()`` based on +requirements and convenience. + + Processing Template Strings --------------------------- From 2a7c6ee778981534e53d85d742705b4caa07cd7d Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 7 Apr 2025 10:19:43 -0700 Subject: [PATCH 04/13] First stab at extra clarification in "round trip" section --- peps/pep-0750.rst | 50 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 9650afdf583..1e11ab8f689 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -1288,27 +1288,49 @@ Enable Full Reconstruction of Original Template Literal Earlier versions of this PEP attempted to make it possible to fully reconstruct the text of the original template string from a ``Template`` instance. This was -rejected as being overly complex. +rejected as being overly complex. The mapping between template literal source +and the underlying AST is not one-to-one and there are several limitations with +respect to round-tripping to the original source text. -There are several limitations with respect to round-tripping to the original -source text: +First, ``Interpolation.format_spec`` defaults to ``""`` if not provided: -- ``Interpolation.format_spec`` defaults to ``""`` if not provided. It is therefore - impossible to distinguish ``t"{expression}"`` from ``t"{expression:}"``. -- The debug specifier, ``=``, is treated as a special case. It is therefore not - possible to distinguish ``t"{expression=}"`` from - ``t"expression={expression}"``. -- Finally, format specifiers in f-strings allow arbitrary nesting. In this PEP - and in the reference implementation, the specifier is eagerly evaluated - to set the ``format_spec`` in the ``Interpolation``, thereby losing - the original expressions. For example: +.. code-block:: python + + value = 42 + template1 = t"{value}" + template2 = t"{value:}" + assert template1.interpolations[0].format_spec == "" + assert template2.interpolations[0].format_spec == "" + +Next, the debug specifier, ``=``, is treated as a special case and is processed +before the AST is created. It is therefore not possible to distinguish +``t"{expression=}"`` from ``t"expression={expression!r}"``: + +.. code-block:: python + + value = 42 + template1 = t"{value=}" + template2 = t"value={value!r}" + assert template1.strings[0] == "value=" + assert template1.interpolations[0].expression == "value" + assert template1.interpolations[0].conversion == "r" + assert template2.strings[0] == "value=" + assert template2.interpolations[0].expression == "value" + assert template2.interpolations[0].conversion == "r" + +Finally, format specifiers in f-strings allow arbitrary nesting. In this PEP +and in the reference implementation, the specifier is eagerly evaluated to +set the ``format_spec`` in the ``Interpolation``, thereby losing the original +expressions. For example: .. code-block:: python value = 42 precision = 2 - template = t"Value: {value:.{precision}f}" - assert template.interpolations[0].format_spec == ".2f" + template1 = t"{value:.2f}" + template2 = t"{value:.{precision}f}" + assert template1.interpolations[0].format_spec == ".2f" + assert template2.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 From f8d00a3f0240607001e3be551cf396dba187d8a3 Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 7 Apr 2025 10:31:00 -0700 Subject: [PATCH 05/13] Fix lint/whitespace issue. --- peps/pep-0750.rst | 48 +++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 1e11ab8f689..662e5514b74 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 immutable type, +Template strings evaluate to an instance of a new immutable type, ``string.templatelib.Template``: .. code-block:: python @@ -233,10 +233,10 @@ The ``expression`` attribute is the *original text* of the interpolation: template = t"Hello {name}" assert template.interpolations[0].expression == "name" -We expect that the ``expression`` attribute will not be used in most template -processing code. It is provided for completeness and for use in debugging and -introspection. See both the `Common Patterns Seen in Processing Templates`_ -section and the `Examples`_ section for more information on how to process +We expect that the ``expression`` attribute will not be used in most template +processing code. It is provided for completeness and for use in debugging and +introspection. See both the `Common Patterns Seen in Processing Templates`_ +section and the `Examples`_ section for more information on how to process template strings. The ``conversion`` attribute is the :ref:`optional conversion ` @@ -287,7 +287,7 @@ did not round the value to two decimal places when processed. The ``Template.values`` Property -------------------------------- -The ``Template.values`` property is a shortcut for accessing the ``value`` +The ``Template.values`` property is a shortcut for accessing the ``value`` attribute of each ``Interpolation`` in the template and is equivalent to: .. code-block:: python @@ -351,8 +351,8 @@ in the output of the ``__iter__()`` method: # However, the strings attribute contains empty strings: assert template.strings == ("", "", "") -Template processing code can choose to work with any combination of -``strings``, ``interpolations``, ``values``, and ``__iter__()`` based on +Template processing code can choose to work with any combination of +``strings``, ``interpolations``, ``values``, and ``__iter__()`` based on requirements and convenience. @@ -464,10 +464,10 @@ In particular, ``t'{expression=}'`` is treated as ``t'expression={expression!r}' assert template.interpolations[0].value == "World" assert template.interpolations[0].conversion == "r" -If a separate format string is also provided, ``t'{expression=:fmt}`` is treated +If a separate format string is also provided, ``t'{expression=:fmt}`` is treated instead as ``t'expression={expression!s:fmt}'``. -Whitespace is preserved in the debug specifier, so ``t'{expression = }'`` is +Whitespace is preserved in the debug specifier, so ``t'{expression = }'`` is treated as ``t'expression = {expression!r}'``. @@ -664,7 +664,7 @@ class. With template strings it is no longer necessary for developers to make sure that their format string and values dictionary are kept in sync; a single template string literal is all that is needed. The ``TemplateMessage`` implementation can automatically extract structured keys and values from -the ``Interpolation.expression`` and ``Interpolation.value`` attributes, +the ``Interpolation.expression`` and ``Interpolation.value`` attributes, respectively. @@ -1243,9 +1243,9 @@ 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``, -``expression``, ``conversion``, and ``format_spec`` were equal. Interpolation -hashing was similar to tuple hashing: an ``Interpolation`` was hashable if and +were equal; ``Interpolations`` were considered equal if their ``value``, +``expression``, ``conversion``, 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 @@ -1288,8 +1288,8 @@ Enable Full Reconstruction of Original Template Literal Earlier versions of this PEP attempted to make it possible to fully reconstruct the text of the original template string from a ``Template`` instance. This was -rejected as being overly complex. The mapping between template literal source -and the underlying AST is not one-to-one and there are several limitations with +rejected as being overly complex. The mapping between template literal source +and the underlying AST is not one-to-one and there are several limitations with respect to round-tripping to the original source text. First, ``Interpolation.format_spec`` defaults to ``""`` if not provided: @@ -1303,7 +1303,7 @@ First, ``Interpolation.format_spec`` defaults to ``""`` if not provided: assert template2.interpolations[0].format_spec == "" Next, the debug specifier, ``=``, is treated as a special case and is processed -before the AST is created. It is therefore not possible to distinguish +before the AST is created. It is therefore not possible to distinguish ``t"{expression=}"`` from ``t"expression={expression!r}"``: .. code-block:: python @@ -1318,9 +1318,9 @@ before the AST is created. It is therefore not possible to distinguish assert template2.interpolations[0].expression == "value" assert template2.interpolations[0].conversion == "r" -Finally, format specifiers in f-strings allow arbitrary nesting. In this PEP -and in the reference implementation, the specifier is eagerly evaluated to -set the ``format_spec`` in the ``Interpolation``, thereby losing the original +Finally, format specifiers in f-strings allow arbitrary nesting. In this PEP +and in the reference implementation, the specifier is eagerly evaluated to +set the ``format_spec`` in the ``Interpolation``, thereby losing the original expressions. For example: .. code-block:: python @@ -1378,8 +1378,8 @@ separate PEP. Removing ``conversion`` From ``Interpolation`` ---------------------------------------------- -During the authoring of this PEP, we considered removing the ``conversion`` -attribute from ``Interpolation`` and specifying that the conversion should be +During the authoring of this PEP, we considered removing the ``conversion`` +attribute from ``Interpolation`` and specifying that the conversion should be performed eagerly, before ``Interpolation.value`` is set. This was done to simplify the work of writing template processing code. The @@ -1390,8 +1390,8 @@ custom format specifiers. Unlike with format specifiers, there is no equivalent to Python's :func:`python:format` built-in. (Instead, we include a sample implementation of ``convert()`` in the `Examples`_ section.) -Ultimately we decided to keep the ``conversion`` attribute in the -``Interpolation`` type to maintain compatibility with f-strings and to allow +Ultimately we decided to keep the ``conversion`` attribute in the +``Interpolation`` type to maintain compatibility with f-strings and to allow for future extensibility. From b78f97a6e04dc01f3e47a3f9a3818576d4b0a01c Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 7 Apr 2025 10:39:04 -0700 Subject: [PATCH 06/13] Remove possible confusion in debug spec examples by using `foo`. --- peps/pep-0750.rst | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 662e5514b74..1a526220329 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -454,7 +454,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'{expression=}'`` is treated as ``t'expression={expression!r}'``: +In particular, ``t'{foo=}'`` is treated as ``t'foo={foo!r}'``: .. code-block:: python @@ -464,11 +464,11 @@ In particular, ``t'{expression=}'`` is treated as ``t'expression={expression!r}' assert template.interpolations[0].value == "World" assert template.interpolations[0].conversion == "r" -If a separate format string is also provided, ``t'{expression=:fmt}`` is treated -instead as ``t'expression={expression!s:fmt}'``. +If a separate format string is also provided, ``t'{foo=:fmt}`` is treated +instead as ``t'foo={foo!s:fmt}'``. -Whitespace is preserved in the debug specifier, so ``t'{expression = }'`` is -treated as ``t'expression = {expression!r}'``. +Whitespace is preserved in the debug specifier, so ``t'{foo = }'`` is +treated as ``t'foo = {foo!r}'``. Raw Template Strings @@ -1304,18 +1304,18 @@ First, ``Interpolation.format_spec`` defaults to ``""`` if not provided: Next, the debug specifier, ``=``, is treated as a special case and is processed before the AST is created. It is therefore not possible to distinguish -``t"{expression=}"`` from ``t"expression={expression!r}"``: +``t"{foo=}"`` from ``t"foo={foo!r}"``: .. code-block:: python - value = 42 - template1 = t"{value=}" - template2 = t"value={value!r}" - assert template1.strings[0] == "value=" - assert template1.interpolations[0].expression == "value" + foo = 42 + template1 = t"{foo=}" + template2 = t"foo={foo!r}" + assert template1.strings[0] == "foo=" + assert template1.interpolations[0].expression == "foo" assert template1.interpolations[0].conversion == "r" - assert template2.strings[0] == "value=" - assert template2.interpolations[0].expression == "value" + assert template2.strings[0] == "foo=" + assert template2.interpolations[0].expression == "foo" assert template2.interpolations[0].conversion == "r" Finally, format specifiers in f-strings allow arbitrary nesting. In this PEP From 90ef017f5f97405d543b3f61417d6e7494db708e Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 7 Apr 2025 10:42:19 -0700 Subject: [PATCH 07/13] Might like this best --- peps/pep-0750.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index 1a526220329..34a7c8581be 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -1308,14 +1308,14 @@ before the AST is created. It is therefore not possible to distinguish .. code-block:: python - foo = 42 - template1 = t"{foo=}" - template2 = t"foo={foo!r}" - assert template1.strings[0] == "foo=" - assert template1.interpolations[0].expression == "foo" + value = 42 + template1 = t"{value=}" + template2 = t"value={value!r}" + assert template1.strings[0] == "value=" + assert template1.interpolations[0].expression == "value" assert template1.interpolations[0].conversion == "r" - assert template2.strings[0] == "foo=" - assert template2.interpolations[0].expression == "foo" + assert template2.strings[0] == "value=" + assert template2.interpolations[0].expression == "value" assert template2.interpolations[0].conversion == "r" Finally, format specifiers in f-strings allow arbitrary nesting. In this PEP From 17b8009eb7a4ece65eb75ee381fd077d49604359 Mon Sep 17 00:00:00 2001 From: Dave Peck Date: Mon, 7 Apr 2025 11:21:45 -0700 Subject: [PATCH 08/13] Apply suggestions from code review Apply several of @AA-Turner's suggestions. Will address the remaining separately. Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- 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 34a7c8581be..b2938c62135 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -109,7 +109,7 @@ 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 the standard library -module ``string.templatelib``. +module :mod:`!string.templatelib`. The following code creates a ``Template`` instance: @@ -135,7 +135,7 @@ The ``Template`` Type --------------------- Template strings evaluate to an instance of a new immutable type, -``string.templatelib.Template``: +:class:`!string.templatelib.Template`: .. code-block:: python @@ -193,7 +193,7 @@ The ``Interpolation`` Type -------------------------- The ``Interpolation`` type represents an expression inside a template string. -Like ``Template``, it is a new class found in the ``string.templatelib`` module: +Like ``Template``, it is a new class found in the :mod:`!string.templatelib` module: .. code-block:: python @@ -454,7 +454,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'{foo=}'`` is treated as ``t'foo={foo!r}'``: +In particular, ``t'{value=}'`` is treated as ``t'value={value!r}'``: .. code-block:: python @@ -464,11 +464,11 @@ In particular, ``t'{foo=}'`` is treated as ``t'foo={foo!r}'``: assert template.interpolations[0].value == "World" assert template.interpolations[0].conversion == "r" -If a separate format string is also provided, ``t'{foo=:fmt}`` is treated -instead as ``t'foo={foo!s:fmt}'``. +If a separate format string is also provided, ``t'{value=:fmt}`` is treated +instead as ``t'value={value!s:fmt}'``. -Whitespace is preserved in the debug specifier, so ``t'{foo = }'`` is -treated as ``t'foo = {foo!r}'``. +Whitespace is preserved in the debug specifier, so ``t'{value = }'`` is +treated as ``t'value = {value!r}'``. Raw Template Strings From fb450fc5a8db11ce424b95bce6eeccb9aa540c00 Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 7 Apr 2025 11:24:59 -0700 Subject: [PATCH 09/13] Spell out Abstract Syntax Tree the first time it appears in the document. --- peps/pep-0750.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index b2938c62135..720b5eae6e0 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -339,13 +339,13 @@ in the output of the ``__iter__()`` method: .. code-block:: python first = "Eat" - second = "Roquefort" + second = "Red Leicester" template = t"{first}{second}" contents = list(template) assert len(contents) == 2 assert contents[0].value == "Eat" assert contents[0].expression == "first" - assert contents[1].value == "Roquefort" + assert contents[1].value == "Red Leicester" assert contents[1].expression == "second" # However, the strings attribute contains empty strings: @@ -833,9 +833,10 @@ 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. +abstract syntax tree (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 From 0d62f212d837cf0dca1b978ea0ee49683dfe1a5b Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 7 Apr 2025 11:29:44 -0700 Subject: [PATCH 10/13] While drafting --- 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 720b5eae6e0..a0a9157a5bb 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -1379,7 +1379,7 @@ separate PEP. Removing ``conversion`` From ``Interpolation`` ---------------------------------------------- -During the authoring of this PEP, we considered removing the ``conversion`` +While drafting this PEP, we considered removing the ``conversion`` attribute from ``Interpolation`` and specifying that the conversion should be performed eagerly, before ``Interpolation.value`` is set. From 1ac438f2c1f129a557bcf9e0f3ea17fc83dde0fb Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 7 Apr 2025 11:38:31 -0700 Subject: [PATCH 11/13] Re-add statement on top-level module. --- peps/pep-0750.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index a0a9157a5bb..a4e240e8a83 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -1283,6 +1283,11 @@ Previous versions of this PEP proposed placing the ``Template`` and and even in a new top-level module, ``templatelib``. The final decision was to place them in ``string.templatelib``. +One argument in favor of creating an entirely new module is that it would allow +for future addition of related methods (like ``convert()``) and for +potential future template processing code to be added to submodules +(``string.templatelib.shell``, etc.). + Enable Full Reconstruction of Original Template Literal ------------------------------------------------------- From c04608f5d61823b6f846b8aba8b7e9804fb7213a Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 7 Apr 2025 11:44:05 -0700 Subject: [PATCH 12/13] foo -> value --- 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 a4e240e8a83..be6b617b2b2 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -1310,7 +1310,7 @@ First, ``Interpolation.format_spec`` defaults to ``""`` if not provided: Next, the debug specifier, ``=``, is treated as a special case and is processed before the AST is created. It is therefore not possible to distinguish -``t"{foo=}"`` from ``t"foo={foo!r}"``: +``t"{value=}"`` from ``t"value={value!r}"``: .. code-block:: python From 26034ed5f25e04e2512afa5b03684f6c0e71fd40 Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 7 Apr 2025 12:00:39 -0700 Subject: [PATCH 13/13] Explicitly describe top-level module --- peps/pep-0750.rst | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/peps/pep-0750.rst b/peps/pep-0750.rst index be6b617b2b2..986ad9dc831 100644 --- a/peps/pep-0750.rst +++ b/peps/pep-0750.rst @@ -527,6 +527,16 @@ The ``Template`` and ``Interpolation`` types both provide useful ``__repr__()`` implementations. +The :mod:`!string.templatelib` Module +------------------------------------- + +The :mod:`string` module will be converted into a package, with a new +``templatelib`` submodule containing the ``Template`` and ``Interpolation`` +types. Following the implementation of this PEP, this new module may be used +for related functions, such as :func:`!convert`, or potential future template +processing code, such as shell script helpers. + + Examples ======== @@ -1283,11 +1293,6 @@ Previous versions of this PEP proposed placing the ``Template`` and and even in a new top-level module, ``templatelib``. The final decision was to place them in ``string.templatelib``. -One argument in favor of creating an entirely new module is that it would allow -for future addition of related methods (like ``convert()``) and for -potential future template processing code to be added to submodules -(``string.templatelib.shell``, etc.). - Enable Full Reconstruction of Original Template Literal -------------------------------------------------------