mirror of https://github.com/python/peps
687 lines
62 KiB
HTML
687 lines
62 KiB
HTML
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="color-scheme" content="light dark">
|
||
<title>PEP 501 – General purpose string interpolation | peps.python.org</title>
|
||
<link rel="shortcut icon" href="../_static/py.png">
|
||
<link rel="canonical" href="https://peps.python.org/pep-0501/">
|
||
<link rel="stylesheet" href="../_static/style.css" type="text/css">
|
||
<link rel="stylesheet" href="../_static/mq.css" type="text/css">
|
||
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light">
|
||
<link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark">
|
||
<link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss">
|
||
<meta property="og:title" content='PEP 501 – General purpose string interpolation | peps.python.org'>
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="https://peps.python.org/pep-0501/">
|
||
<meta property="og:site_name" content="Python Enhancement Proposals (PEPs)">
|
||
<meta property="og:image" content="https://peps.python.org/_static/og-image.png">
|
||
<meta property="og:image:alt" content="Python PEPs">
|
||
<meta property="og:image:width" content="200">
|
||
<meta property="og:image:height" content="200">
|
||
<meta name="description" content="Python Enhancement Proposals (PEPs)">
|
||
<meta name="theme-color" content="#3776ab">
|
||
</head>
|
||
<body>
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||
<symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Following system colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="9"></circle>
|
||
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
|
||
</svg>
|
||
</symbol>
|
||
<symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Selected dark colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
|
||
</svg>
|
||
</symbol>
|
||
<symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Selected light colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="5"></circle>
|
||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||
</svg>
|
||
</symbol>
|
||
</svg>
|
||
<script>
|
||
|
||
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
|
||
</script>
|
||
<section id="pep-page-section">
|
||
<header>
|
||
<h1>Python Enhancement Proposals</h1>
|
||
<ul class="breadcrumbs">
|
||
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> » </li>
|
||
<li><a href="../pep-0000/">PEP Index</a> » </li>
|
||
<li>PEP 501</li>
|
||
</ul>
|
||
<button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())">
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg>
|
||
<span class="visually-hidden">Toggle light / dark / auto colour theme</span>
|
||
</button>
|
||
</header>
|
||
<article>
|
||
<section id="pep-content">
|
||
<h1 class="page-title">PEP 501 – General purpose string interpolation</h1>
|
||
<dl class="rfc2822 field-list simple">
|
||
<dt class="field-odd">Author<span class="colon">:</span></dt>
|
||
<dd class="field-odd">Alyssa Coghlan <ncoghlan at gmail.com></dd>
|
||
<dt class="field-even">Status<span class="colon">:</span></dt>
|
||
<dd class="field-even"><abbr title="Inactive draft that may be taken up again at a later time">Deferred</abbr></dd>
|
||
<dt class="field-odd">Type<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
|
||
<dt class="field-even">Requires<span class="colon">:</span></dt>
|
||
<dd class="field-even"><a class="reference external" href="../pep-0498/">498</a></dd>
|
||
<dt class="field-odd">Created<span class="colon">:</span></dt>
|
||
<dd class="field-odd">08-Aug-2015</dd>
|
||
<dt class="field-even">Python-Version<span class="colon">:</span></dt>
|
||
<dd class="field-even">3.6</dd>
|
||
<dt class="field-odd">Post-History<span class="colon">:</span></dt>
|
||
<dd class="field-odd">08-Aug-2015, 23-Aug-2015, 30-Aug-2015</dd>
|
||
</dl>
|
||
<hr class="docutils" />
|
||
<section id="contents">
|
||
<details><summary>Table of Contents</summary><ul class="simple">
|
||
<li><a class="reference internal" href="#abstract">Abstract</a></li>
|
||
<li><a class="reference internal" href="#pep-deferral">PEP Deferral</a></li>
|
||
<li><a class="reference internal" href="#summary-of-differences-from-pep-498">Summary of differences from PEP 498</a></li>
|
||
<li><a class="reference internal" href="#proposal">Proposal</a></li>
|
||
<li><a class="reference internal" href="#rationale">Rationale</a></li>
|
||
<li><a class="reference internal" href="#specification">Specification</a><ul>
|
||
<li><a class="reference internal" href="#conversion-specifiers">Conversion specifiers</a></li>
|
||
<li><a class="reference internal" href="#writing-custom-renderers">Writing custom renderers</a></li>
|
||
<li><a class="reference internal" href="#expression-evaluation">Expression evaluation</a></li>
|
||
<li><a class="reference internal" href="#handling-code-injection-attacks">Handling code injection attacks</a></li>
|
||
<li><a class="reference internal" href="#format-specifiers">Format specifiers</a></li>
|
||
<li><a class="reference internal" href="#error-handling">Error handling</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#possible-integration-with-the-logging-module">Possible integration with the logging module</a></li>
|
||
<li><a class="reference internal" href="#discussion">Discussion</a><ul>
|
||
<li><a class="reference internal" href="#deferring-support-for-binary-interpolation">Deferring support for binary interpolation</a></li>
|
||
<li><a class="reference internal" href="#interoperability-with-str-only-interfaces">Interoperability with str-only interfaces</a></li>
|
||
<li><a class="reference internal" href="#preserving-the-raw-template-string">Preserving the raw template string</a></li>
|
||
<li><a class="reference internal" href="#creating-a-rich-object-rather-than-a-global-name-lookup">Creating a rich object rather than a global name lookup</a></li>
|
||
<li><a class="reference internal" href="#building-atop-pep-498-rather-than-competing-with-it">Building atop PEP 498, rather than competing with it</a></li>
|
||
<li><a class="reference internal" href="#deferring-consideration-of-possible-use-in-i18n-use-cases">Deferring consideration of possible use in i18n use cases</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#acknowledgements">Acknowledgements</a></li>
|
||
<li><a class="reference internal" href="#references">References</a></li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
</details></section>
|
||
<section id="abstract">
|
||
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
|
||
<p><a class="pep reference internal" href="../pep-0498/" title="PEP 498 – Literal String Interpolation">PEP 498</a> proposes new syntactic support for string interpolation that is
|
||
transparent to the compiler, allow name references from the interpolation
|
||
operation full access to containing namespaces (as with any other expression),
|
||
rather than being limited to explicit name references. These are referred
|
||
to in the PEP as “f-strings” (a mnemonic for “formatted strings”).</p>
|
||
<p>However, it only offers this capability for string formatting, making it likely
|
||
we will see code like the following:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="sa">f</span><span class="s2">"echo </span><span class="si">{</span><span class="n">message_from_user</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This kind of code is superficially elegant, but poses a significant problem
|
||
if the interpolated value <code class="docutils literal notranslate"><span class="pre">message_from_user</span></code> is in fact provided by an
|
||
untrusted user: it’s an opening for a form of code injection attack, where
|
||
the supplied user data has not been properly escaped before being passed to
|
||
the <code class="docutils literal notranslate"><span class="pre">os.system</span></code> call.</p>
|
||
<p>To address that problem (and a number of other concerns), this PEP proposes
|
||
the complementary introduction of “i-strings” (a mnemonic for “interpolation
|
||
template strings”), where <code class="docutils literal notranslate"><span class="pre">f"Message</span> <span class="pre">with</span> <span class="pre">{data}"</span></code> would produce the same
|
||
result as <code class="docutils literal notranslate"><span class="pre">format(i"Message</span> <span class="pre">with</span> <span class="pre">{data}")</span></code>.</p>
|
||
<p>Some possible examples of the proposed syntax:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">mycommand</span> <span class="o">=</span> <span class="n">sh</span><span class="p">(</span><span class="n">i</span><span class="s2">"cat </span><span class="si">{filename}</span><span class="s2">"</span><span class="p">)</span>
|
||
<span class="n">myquery</span> <span class="o">=</span> <span class="n">sql</span><span class="p">(</span><span class="n">i</span><span class="s2">"SELECT </span><span class="si">{column}</span><span class="s2"> FROM </span><span class="si">{table}</span><span class="s2">;"</span><span class="p">)</span>
|
||
<span class="n">myresponse</span> <span class="o">=</span> <span class="n">html</span><span class="p">(</span><span class="n">i</span><span class="s2">"<html><body></span><span class="si">{response.body}</span><span class="s2"></body></html>"</span><span class="p">)</span>
|
||
<span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="n">i</span><span class="s2">"Message with </span><span class="si">{detailed}</span><span class="s2"> </span><span class="si">{debugging}</span><span class="s2"> </span><span class="si">{info}</span><span class="s2">"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="pep-deferral">
|
||
<h2><a class="toc-backref" href="#pep-deferral" role="doc-backlink">PEP Deferral</a></h2>
|
||
<p>This PEP is currently deferred pending further experience with <a class="pep reference internal" href="../pep-0498/" title="PEP 498 – Literal String Interpolation">PEP 498</a>’s
|
||
simpler approach of only supporting eager rendering without the additional
|
||
complexity of also supporting deferred rendering.</p>
|
||
</section>
|
||
<section id="summary-of-differences-from-pep-498">
|
||
<h2><a class="toc-backref" href="#summary-of-differences-from-pep-498" role="doc-backlink">Summary of differences from PEP 498</a></h2>
|
||
<p>The key additions this proposal makes relative to <a class="pep reference internal" href="../pep-0498/" title="PEP 498 – Literal String Interpolation">PEP 498</a>:</p>
|
||
<ul class="simple">
|
||
<li>the “i” (interpolation template) prefix indicates delayed rendering, but
|
||
otherwise uses the same syntax and semantics as formatted strings</li>
|
||
<li>interpolation templates are available at runtime as a new kind of object
|
||
(<code class="docutils literal notranslate"><span class="pre">types.InterpolationTemplate</span></code>)</li>
|
||
<li>the default rendering used by formatted strings is invoked on an
|
||
interpolation template object by calling <code class="docutils literal notranslate"><span class="pre">format(template)</span></code> rather than
|
||
implicitly</li>
|
||
<li>while f-string <code class="docutils literal notranslate"><span class="pre">f"Message</span> <span class="pre">{here}"</span></code> would be <em>semantically</em> equivalent to
|
||
<code class="docutils literal notranslate"><span class="pre">format(i"Message</span> <span class="pre">{here}")</span></code>, it is expected that the explicit syntax would
|
||
avoid the runtime overhead of using the delayed rendering machinery</li>
|
||
</ul>
|
||
<p>NOTE: This proposal spells out a draft API for <code class="docutils literal notranslate"><span class="pre">types.InterpolationTemplate</span></code>.
|
||
The precise details of the structures and methods exposed by this type would
|
||
be informed by the reference implementation of <a class="pep reference internal" href="../pep-0498/" title="PEP 498 – Literal String Interpolation">PEP 498</a>, so it makes sense to
|
||
gain experience with that as an internal API before locking down a public API
|
||
(if this extension proposal is accepted).</p>
|
||
</section>
|
||
<section id="proposal">
|
||
<h2><a class="toc-backref" href="#proposal" role="doc-backlink">Proposal</a></h2>
|
||
<p>This PEP proposes the introduction of a new string prefix that declares the
|
||
string to be an interpolation template rather than an ordinary string:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">template</span> <span class="o">=</span> <span class="n">i</span><span class="s2">"Substitute </span><span class="si">{names}</span><span class="s2"> and {expressions()} at runtime"</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This would be effectively interpreted as:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">_raw_template</span> <span class="o">=</span> <span class="s2">"Substitute </span><span class="si">{names}</span><span class="s2"> and {expressions()} at runtime"</span>
|
||
<span class="n">_parsed_template</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="p">(</span><span class="s2">"Substitute "</span><span class="p">,</span> <span class="s2">"names"</span><span class="p">),</span>
|
||
<span class="p">(</span><span class="s2">" and "</span><span class="p">,</span> <span class="s2">"expressions()"</span><span class="p">),</span>
|
||
<span class="p">(</span><span class="s2">" at runtime"</span><span class="p">,</span> <span class="kc">None</span><span class="p">),</span>
|
||
<span class="p">)</span>
|
||
<span class="n">_field_values</span> <span class="o">=</span> <span class="p">(</span><span class="n">names</span><span class="p">,</span> <span class="n">expressions</span><span class="p">())</span>
|
||
<span class="n">_format_specifiers</span> <span class="o">=</span> <span class="p">(</span><span class="sa">f</span><span class="s2">""</span><span class="p">,</span> <span class="sa">f</span><span class="s2">""</span><span class="p">)</span>
|
||
<span class="n">template</span> <span class="o">=</span> <span class="n">types</span><span class="o">.</span><span class="n">InterpolationTemplate</span><span class="p">(</span><span class="n">_raw_template</span><span class="p">,</span>
|
||
<span class="n">_parsed_template</span><span class="p">,</span>
|
||
<span class="n">_field_values</span><span class="p">,</span>
|
||
<span class="n">_format_specifiers</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">__format__</span></code> method on <code class="docutils literal notranslate"><span class="pre">types.InterpolationTemplate</span></code> would then
|
||
implement the following <code class="docutils literal notranslate"><span class="pre">str.format</span></code> inspired semantics:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">datetime</span>
|
||
<span class="gp">>>> </span><span class="n">name</span> <span class="o">=</span> <span class="s1">'Jane'</span>
|
||
<span class="gp">>>> </span><span class="n">age</span> <span class="o">=</span> <span class="mi">50</span>
|
||
<span class="gp">>>> </span><span class="n">anniversary</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="p">(</span><span class="mi">1991</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">12</span><span class="p">)</span>
|
||
<span class="gp">>>> </span><span class="nb">format</span><span class="p">(</span><span class="n">i</span><span class="s1">'My name is </span><span class="si">{name}</span><span class="s1">, my age next year is {age+1}, my anniversary is {anniversary:%A, %B </span><span class="si">%d</span><span class="s1">, %Y}.'</span><span class="p">)</span>
|
||
<span class="go">'My name is Jane, my age next year is 51, my anniversary is Saturday, October 12, 1991.'</span>
|
||
<span class="gp">>>> </span><span class="nb">format</span><span class="p">(</span><span class="n">i</span><span class="s1">'She said her name is {repr(name)}.'</span><span class="p">)</span>
|
||
<span class="go">"She said her name is 'Jane'."</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>As with formatted strings, the interpolation template prefix can be combined with single-quoted, double-quoted and triple quoted strings, including raw strings.
|
||
It does not support combination with bytes literals.</p>
|
||
<p>Similarly, this PEP does not propose to remove or deprecate any of the existing
|
||
string formatting mechanisms, as those will remain valuable when formatting
|
||
strings that are not present directly in the source code of the application.</p>
|
||
</section>
|
||
<section id="rationale">
|
||
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
|
||
<p><a class="pep reference internal" href="../pep-0498/" title="PEP 498 – Literal String Interpolation">PEP 498</a> makes interpolating values into strings with full access to Python’s
|
||
lexical namespace semantics simpler, but it does so at the cost of creating a
|
||
situation where interpolating values into sensitive targets like SQL queries,
|
||
shell commands and HTML templates will enjoy a much cleaner syntax when handled
|
||
without regard for code injection attacks than when they are handled correctly.</p>
|
||
<p>This PEP proposes to provide the option of delaying the actual rendering
|
||
of an interpolation template to its <code class="docutils literal notranslate"><span class="pre">__format__</span></code> method, allowing the use of
|
||
other template renderers by passing the template around as a first class object.</p>
|
||
<p>While very different in the technical details, the
|
||
<code class="docutils literal notranslate"><span class="pre">types.InterpolationTemplate</span></code> interface proposed in this PEP is
|
||
conceptually quite similar to the <code class="docutils literal notranslate"><span class="pre">FormattableString</span></code> type underlying the
|
||
<a class="reference external" href="https://msdn.microsoft.com/en-us/library/dn961160.aspx">native interpolation</a> support introduced in C# 6.0.</p>
|
||
</section>
|
||
<section id="specification">
|
||
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
|
||
<p>This PEP proposes the introduction of <code class="docutils literal notranslate"><span class="pre">i</span></code> as a new string prefix that
|
||
results in the creation of an instance of a new type,
|
||
<code class="docutils literal notranslate"><span class="pre">types.InterpolationTemplate</span></code>.</p>
|
||
<p>Interpolation template literals are Unicode strings (bytes literals are not
|
||
permitted), and string literal concatenation operates as normal, with the
|
||
entire combined literal forming the interpolation template.</p>
|
||
<p>The template string is parsed into literals, expressions and format specifiers
|
||
as described for f-strings in <a class="pep reference internal" href="../pep-0498/" title="PEP 498 – Literal String Interpolation">PEP 498</a>. Conversion specifiers are handled
|
||
by the compiler, and appear as part of the field text in interpolation
|
||
templates.</p>
|
||
<p>However, rather than being rendered directly into a formatted strings, these
|
||
components are instead organised into an instance of a new type with the
|
||
following semantics:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">InterpolationTemplate</span><span class="p">:</span>
|
||
<span class="vm">__slots__</span> <span class="o">=</span> <span class="p">(</span><span class="s2">"raw_template"</span><span class="p">,</span> <span class="s2">"parsed_template"</span><span class="p">,</span>
|
||
<span class="s2">"field_values"</span><span class="p">,</span> <span class="s2">"format_specifiers"</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">raw_template</span><span class="p">,</span> <span class="n">parsed_template</span><span class="p">,</span>
|
||
<span class="n">field_values</span><span class="p">,</span> <span class="n">format_specifiers</span><span class="p">):</span>
|
||
<span class="bp">self</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">)</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">raw_template</span> <span class="o">=</span> <span class="n">raw_template</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">parsed_template</span> <span class="o">=</span> <span class="n">parsed_template</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">field_values</span> <span class="o">=</span> <span class="n">field_values</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">format_specifiers</span> <span class="o">=</span> <span class="n">format_specifiers</span>
|
||
<span class="k">return</span> <span class="bp">self</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="sa">f</span><span class="s2">"<</span><span class="si">{</span><span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="vm">__qualname__</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="nb">repr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_raw_template</span><span class="p">)</span><span class="si">}</span><span class="s2"> "</span>
|
||
<span class="sa">f</span><span class="s2">"at </span><span class="si">{</span><span class="nb">id</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><span class="si">:</span><span class="s2">#x</span><span class="si">}</span><span class="s2">>"</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__format__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">format_specifier</span><span class="p">):</span>
|
||
<span class="c1"># When formatted, render to a string, and use string formatting</span>
|
||
<span class="k">return</span> <span class="nb">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">render</span><span class="p">(),</span> <span class="n">format_specifier</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="nf">render</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">render_template</span><span class="o">=</span><span class="s1">''</span><span class="o">.</span><span class="n">join</span><span class="p">,</span>
|
||
<span class="n">render_field</span><span class="o">=</span><span class="nb">format</span><span class="p">):</span>
|
||
<span class="c1"># See definition of the template rendering semantics below</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The result of an interpolation template expression is an instance of this
|
||
type, rather than an already rendered string - rendering only takes
|
||
place when the instance’s <code class="docutils literal notranslate"><span class="pre">render</span></code> method is called (either directly, or
|
||
indirectly via <code class="docutils literal notranslate"><span class="pre">__format__</span></code>).</p>
|
||
<p>The compiler will pass the following details to the interpolation template for
|
||
later use:</p>
|
||
<ul class="simple">
|
||
<li>a string containing the raw template as written in the source code</li>
|
||
<li>a parsed template tuple that allows the renderer to render the
|
||
template without needing to reparse the raw string template for substitution
|
||
fields</li>
|
||
<li>a tuple containing the evaluated field values, in field substitution order</li>
|
||
<li>a tuple containing the field format specifiers, in field substitution order</li>
|
||
</ul>
|
||
<p>This structure is designed to take full advantage of compile time constant
|
||
folding by ensuring the parsed template is always constant, even when the
|
||
field values and format specifiers include variable substitution expressions.</p>
|
||
<p>The raw template is just the interpolation template as a string. By default,
|
||
it is used to provide a human readable representation for the interpolation
|
||
template.</p>
|
||
<p>The parsed template consists of a tuple of 2-tuples, with each 2-tuple
|
||
containing the following fields:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">leading_text</span></code>: a leading string literal. This will be the empty string if
|
||
the current field is at the start of the string, or immediately follows the
|
||
preceding field.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">field_expr</span></code>: the text of the expression element in the substitution field.
|
||
This will be None for a final trailing text segment.</li>
|
||
</ul>
|
||
<p>The tuple of evaluated field values holds the <em>results</em> of evaluating the
|
||
substitution expressions in the scope where the interpolation template appears.</p>
|
||
<p>The tuple of field specifiers holds the <em>results</em> of evaluating the field
|
||
specifiers as f-strings in the scope where the interpolation template appears.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">InterpolationTemplate.render</span></code> implementation then defines the rendering
|
||
process in terms of the following renderers:</p>
|
||
<ul class="simple">
|
||
<li>an overall <code class="docutils literal notranslate"><span class="pre">render_template</span></code> operation that defines how the sequence of
|
||
literal template sections and rendered fields are composed into a fully
|
||
rendered result. The default template renderer is string concatenation
|
||
using <code class="docutils literal notranslate"><span class="pre">''.join</span></code>.</li>
|
||
<li>a per field <code class="docutils literal notranslate"><span class="pre">render_field</span></code> operation that receives the field value and
|
||
format specifier for substitution fields within the template. The default
|
||
field renderer is the <code class="docutils literal notranslate"><span class="pre">format</span></code> builtin.</li>
|
||
</ul>
|
||
<p>Given an appropriate parsed template representation and internal methods of
|
||
iterating over it, the semantics of template rendering would then be equivalent
|
||
to the following:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">render</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">render_template</span><span class="o">=</span><span class="s1">''</span><span class="o">.</span><span class="n">join</span><span class="p">,</span>
|
||
<span class="n">render_field</span><span class="o">=</span><span class="nb">format</span><span class="p">):</span>
|
||
<span class="n">iter_fields</span> <span class="o">=</span> <span class="nb">enumerate</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">parsed_template</span><span class="p">)</span>
|
||
<span class="n">values</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">field_values</span>
|
||
<span class="n">specifiers</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">format_specifiers</span>
|
||
<span class="n">template_parts</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="k">for</span> <span class="n">field_pos</span><span class="p">,</span> <span class="p">(</span><span class="n">leading_text</span><span class="p">,</span> <span class="n">field_expr</span><span class="p">)</span> <span class="ow">in</span> <span class="n">iter_fields</span><span class="p">:</span>
|
||
<span class="n">template_parts</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">leading_text</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">field_expr</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">values</span><span class="p">[</span><span class="n">field_pos</span><span class="p">]</span>
|
||
<span class="n">specifier</span> <span class="o">=</span> <span class="n">specifiers</span><span class="p">[</span><span class="n">field_pos</span><span class="p">]</span>
|
||
<span class="n">rendered_field</span> <span class="o">=</span> <span class="n">render_field</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">specifier</span><span class="p">)</span>
|
||
<span class="n">template_parts</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">rendered_field</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="n">template_parts</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<section id="conversion-specifiers">
|
||
<h3><a class="toc-backref" href="#conversion-specifiers" role="doc-backlink">Conversion specifiers</a></h3>
|
||
<p>NOTE:</p>
|
||
<blockquote>
|
||
<div>Appropriate handling of conversion specifiers is currently an open question.
|
||
Exposing them more directly to custom renderers would increase the
|
||
complexity of the <code class="docutils literal notranslate"><span class="pre">InterpolationTemplate</span></code> definition without providing an
|
||
increase in expressiveness (since they’re redundant with calling the builtins
|
||
directly). At the same time, they <em>are</em> made available as arbitrary strings
|
||
when writing custom <code class="docutils literal notranslate"><span class="pre">string.Formatter</span></code> implementations, so it may be
|
||
desirable to offer similar levels of flexibility of interpretation in
|
||
interpolation templates.</div></blockquote>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">!a</span></code>, <code class="docutils literal notranslate"><span class="pre">!r</span></code> and <code class="docutils literal notranslate"><span class="pre">!s</span></code> conversion specifiers supported by <code class="docutils literal notranslate"><span class="pre">str.format</span></code>
|
||
and hence <a class="pep reference internal" href="../pep-0498/" title="PEP 498 – Literal String Interpolation">PEP 498</a> are handled in interpolation templates as follows:</p>
|
||
<ul class="simple">
|
||
<li>they’re included unmodified in the raw template to ensure no information is
|
||
lost</li>
|
||
<li>they’re <em>replaced</em> in the parsed template with the corresponding builtin
|
||
calls, in order to ensure that <code class="docutils literal notranslate"><span class="pre">field_expr</span></code> always contains a valid
|
||
Python expression</li>
|
||
<li>the corresponding field value placed in the field values tuple is
|
||
converted appropriately <em>before</em> being passed to the interpolation
|
||
template</li>
|
||
</ul>
|
||
<p>This means that, for most purposes, the difference between the use of
|
||
conversion specifiers and calling the corresponding builtins in the
|
||
original interpolation template will be transparent to custom renderers. The
|
||
difference will only be apparent if reparsing the raw template, or attempting
|
||
to reconstruct the original template from the parsed template.</p>
|
||
</section>
|
||
<section id="writing-custom-renderers">
|
||
<h3><a class="toc-backref" href="#writing-custom-renderers" role="doc-backlink">Writing custom renderers</a></h3>
|
||
<p>Writing a custom renderer doesn’t requiring any special syntax. Instead,
|
||
custom renderers are ordinary callables that process an interpolation
|
||
template directly either by calling the <code class="docutils literal notranslate"><span class="pre">render()</span></code> method with alternate <code class="docutils literal notranslate"><span class="pre">render_template</span></code> or <code class="docutils literal notranslate"><span class="pre">render_field</span></code> implementations, or by accessing the
|
||
template’s data attributes directly.</p>
|
||
<p>For example, the following function would render a template using objects’
|
||
<code class="docutils literal notranslate"><span class="pre">repr</span></code> implementations rather than their native formatting support:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">reprformat</span><span class="p">(</span><span class="n">template</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">render_field</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">specifier</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="nb">format</span><span class="p">(</span><span class="nb">repr</span><span class="p">(</span><span class="n">value</span><span class="p">),</span> <span class="n">specifier</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">template</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="n">render_field</span><span class="o">=</span><span class="n">render_field</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>When writing custom renderers, note that the return type of the overall
|
||
rendering operation is determined by the return type of the passed in <code class="docutils literal notranslate"><span class="pre">render_template</span></code> callable. While this is expected to be a string in most
|
||
cases, producing non-string objects <em>is</em> permitted. For example, a custom
|
||
template renderer could involve an <code class="docutils literal notranslate"><span class="pre">sqlalchemy.sql.text</span></code> call that produces
|
||
an <a class="reference external" href="http://docs.sqlalchemy.org/en/rel_1_0/core/tutorial.html#using-textual-sql">SQL Alchemy query object</a>.</p>
|
||
<p>Non-strings may also be returned from <code class="docutils literal notranslate"><span class="pre">render_field</span></code>, as long as it is paired
|
||
with a <code class="docutils literal notranslate"><span class="pre">render_template</span></code> implementation that expects that behaviour.</p>
|
||
</section>
|
||
<section id="expression-evaluation">
|
||
<h3><a class="toc-backref" href="#expression-evaluation" role="doc-backlink">Expression evaluation</a></h3>
|
||
<p>As with f-strings, the subexpressions that are extracted from the interpolation
|
||
template are evaluated in the context where the interpolation template
|
||
appears. This means the expression has full access to local, nonlocal and global variables. Any valid Python expression can be used inside <code class="docutils literal notranslate"><span class="pre">{}</span></code>, including
|
||
function and method calls.</p>
|
||
<p>Because the substitution expressions are evaluated where the string appears in
|
||
the source code, there are no additional security concerns related to the
|
||
contents of the expression itself, as you could have also just written the
|
||
same expression and used runtime field parsing:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">bar</span><span class="o">=</span><span class="mi">10</span>
|
||
<span class="gp">>>> </span><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
|
||
<span class="gp">... </span> <span class="k">return</span> <span class="n">data</span> <span class="o">+</span> <span class="mi">20</span>
|
||
<span class="gp">...</span>
|
||
<span class="gp">>>> </span><span class="nb">str</span><span class="p">(</span><span class="n">i</span><span class="s1">'input=</span><span class="si">{bar}</span><span class="s1">, output={foo(bar)}'</span><span class="p">)</span>
|
||
<span class="go">'input=10, output=30'</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Is essentially equivalent to:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="s1">'input=</span><span class="si">{}</span><span class="s1">, output=</span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">bar</span><span class="p">,</span> <span class="n">foo</span><span class="p">(</span><span class="n">bar</span><span class="p">))</span>
|
||
<span class="go">'input=10, output=30'</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="handling-code-injection-attacks">
|
||
<h3><a class="toc-backref" href="#handling-code-injection-attacks" role="doc-backlink">Handling code injection attacks</a></h3>
|
||
<p>The <a class="pep reference internal" href="../pep-0498/" title="PEP 498 – Literal String Interpolation">PEP 498</a> formatted string syntax makes it potentially attractive to write
|
||
code like the following:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">runquery</span><span class="p">(</span><span class="sa">f</span><span class="s2">"SELECT </span><span class="si">{</span><span class="n">column</span><span class="si">}</span><span class="s2"> FROM </span><span class="si">{</span><span class="n">table</span><span class="si">}</span><span class="s2">;"</span><span class="p">)</span>
|
||
<span class="n">runcommand</span><span class="p">(</span><span class="sa">f</span><span class="s2">"cat </span><span class="si">{</span><span class="n">filename</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||
<span class="n">return_response</span><span class="p">(</span><span class="sa">f</span><span class="s2">"<html><body></span><span class="si">{</span><span class="n">response</span><span class="o">.</span><span class="n">body</span><span class="si">}</span><span class="s2"></body></html>"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>These all represent potential vectors for code injection attacks, if any of the
|
||
variables being interpolated happen to come from an untrusted source. The
|
||
specific proposal in this PEP is designed to make it straightforward to write
|
||
use case specific renderers that take care of quoting interpolated values
|
||
appropriately for the relevant security context:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">runquery</span><span class="p">(</span><span class="n">sql</span><span class="p">(</span><span class="n">i</span><span class="s2">"SELECT </span><span class="si">{column}</span><span class="s2"> FROM </span><span class="si">{table}</span><span class="s2">;"</span><span class="p">))</span>
|
||
<span class="n">runcommand</span><span class="p">(</span><span class="n">sh</span><span class="p">(</span><span class="n">i</span><span class="s2">"cat </span><span class="si">{filename}</span><span class="s2">"</span><span class="p">))</span>
|
||
<span class="n">return_response</span><span class="p">(</span><span class="n">html</span><span class="p">(</span><span class="n">i</span><span class="s2">"<html><body></span><span class="si">{response.body}</span><span class="s2"></body></html>"</span><span class="p">))</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This PEP does not cover adding such renderers to the standard library
|
||
immediately, but rather proposes to ensure that they can be readily provided by
|
||
third party libraries, and potentially incorporated into the standard library
|
||
at a later date.</p>
|
||
<p>For example, a renderer that aimed to offer a POSIX shell style experience for
|
||
accessing external programs, without the significant risks posed by running
|
||
<code class="docutils literal notranslate"><span class="pre">os.system</span></code> or enabling the system shell when using the <code class="docutils literal notranslate"><span class="pre">subprocess</span></code> module
|
||
APIs, might provide an interface for running external programs similar to that
|
||
offered by the
|
||
<a class="reference external" href="http://julia.readthedocs.org/en/latest/manual/running-external-programs/">Julia programming language</a>,
|
||
only with the backtick based <code class="docutils literal notranslate"><span class="pre">\`cat</span> <span class="pre">$filename\`</span></code> syntax replaced by
|
||
<code class="docutils literal notranslate"><span class="pre">i"cat</span> <span class="pre">{filename}"</span></code> style interpolation templates.</p>
|
||
</section>
|
||
<section id="format-specifiers">
|
||
<h3><a class="toc-backref" href="#format-specifiers" role="doc-backlink">Format specifiers</a></h3>
|
||
<p>Aside from separating them out from the substitution expression during parsing,
|
||
format specifiers are otherwise treated as opaque strings by the interpolation
|
||
template parser - assigning semantics to those (or, alternatively,
|
||
prohibiting their use) is handled at runtime by the field renderer.</p>
|
||
</section>
|
||
<section id="error-handling">
|
||
<h3><a class="toc-backref" href="#error-handling" role="doc-backlink">Error handling</a></h3>
|
||
<p>Either compile time or run time errors can occur when processing interpolation
|
||
expressions. Compile time errors are limited to those errors that can be
|
||
detected when parsing a template string into its component tuples. These
|
||
errors all raise SyntaxError.</p>
|
||
<p>Unmatched braces:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">i</span><span class="s1">'x={x'</span>
|
||
File <span class="nb">"<stdin>"</span>, line <span class="m">1</span>
|
||
<span class="gr">SyntaxError</span>: <span class="n">missing '}' in interpolation expression</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Invalid expressions:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>>>> i'x={!x}'
|
||
File "<fstring>", line 1
|
||
!x
|
||
^
|
||
SyntaxError: invalid syntax
|
||
</pre></div>
|
||
</div>
|
||
<p>Run time errors occur when evaluating the expressions inside a
|
||
template string before creating the interpolation template object. See <a class="pep reference internal" href="../pep-0498/" title="PEP 498 – Literal String Interpolation">PEP 498</a>
|
||
for some examples.</p>
|
||
<p>Different renderers may also impose additional runtime
|
||
constraints on acceptable interpolated expressions and other formatting
|
||
details, which will be reported as runtime exceptions.</p>
|
||
</section>
|
||
</section>
|
||
<section id="possible-integration-with-the-logging-module">
|
||
<h2><a class="toc-backref" href="#possible-integration-with-the-logging-module" role="doc-backlink">Possible integration with the logging module</a></h2>
|
||
<p>One of the challenges with the logging module has been that we have previously
|
||
been unable to devise a reasonable migration strategy away from the use of
|
||
printf-style formatting. The runtime parsing and interpolation overhead for
|
||
logging messages also poses a problem for extensive logging of runtime events
|
||
for monitoring purposes.</p>
|
||
<p>While beyond the scope of this initial PEP, interpolation template support
|
||
could potentially be added to the logging module’s event reporting APIs,
|
||
permitting relevant details to be captured using forms like:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="n">i</span><span class="s2">"Event: </span><span class="si">{event}</span><span class="s2">; Details: </span><span class="si">{data}</span><span class="s2">"</span><span class="p">)</span>
|
||
<span class="n">logging</span><span class="o">.</span><span class="n">critical</span><span class="p">(</span><span class="n">i</span><span class="s2">"Error: </span><span class="si">{error}</span><span class="s2">; Details: </span><span class="si">{data}</span><span class="s2">"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Rather than the current mod-formatting style:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">"Event: </span><span class="si">%s</span><span class="s2">; Details: </span><span class="si">%s</span><span class="s2">"</span><span class="p">,</span> <span class="n">event</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>
|
||
<span class="n">logging</span><span class="o">.</span><span class="n">critical</span><span class="p">(</span><span class="s2">"Error: </span><span class="si">%s</span><span class="s2">; Details: </span><span class="si">%s</span><span class="s2">"</span><span class="p">,</span> <span class="n">event</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>As the interpolation template is passed in as an ordinary argument, other
|
||
keyword arguments would also remain available:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">logging</span><span class="o">.</span><span class="n">critical</span><span class="p">(</span><span class="n">i</span><span class="s2">"Error: </span><span class="si">{error}</span><span class="s2">; Details: </span><span class="si">{data}</span><span class="s2">"</span><span class="p">,</span> <span class="n">exc_info</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>As part of any such integration, a recommended approach would need to be
|
||
defined for “lazy evaluation” of interpolated fields, as the <code class="docutils literal notranslate"><span class="pre">logging</span></code>
|
||
module’s existing delayed interpolation support provides access to
|
||
<a class="reference external" href="https://docs.python.org/3/library/logging.html#logrecord-attributes">various attributes</a> of the event <code class="docutils literal notranslate"><span class="pre">LogRecord</span></code> instance.</p>
|
||
<p>For example, since interpolation expressions are arbitrary Python expressions,
|
||
string literals could be used to indicate cases where evaluation itself is
|
||
being deferred, not just rendering:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="n">i</span><span class="s2">"Logger: {'record.name'}; Event: </span><span class="si">{event}</span><span class="s2">; Details: </span><span class="si">{data}</span><span class="s2">"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This could be further extended with idioms like using inline tuples to indicate
|
||
deferred function calls to be made only if the log message is actually
|
||
going to be rendered at current logging levels:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="n">i</span><span class="s2">"Event: </span><span class="si">{event}</span><span class="s2">; Details: {expensive_call, raw_data}"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This kind of approach would be possible as having access to the actual <em>text</em>
|
||
of the field expression would allow the logging renderer to distinguish
|
||
between inline tuples that appear in the field expression itself, and tuples
|
||
that happen to be passed in as data values in a normal field.</p>
|
||
</section>
|
||
<section id="discussion">
|
||
<h2><a class="toc-backref" href="#discussion" role="doc-backlink">Discussion</a></h2>
|
||
<p>Refer to <a class="pep reference internal" href="../pep-0498/" title="PEP 498 – Literal String Interpolation">PEP 498</a> for additional discussion, as several of the points there
|
||
also apply to this PEP.</p>
|
||
<section id="deferring-support-for-binary-interpolation">
|
||
<h3><a class="toc-backref" href="#deferring-support-for-binary-interpolation" role="doc-backlink">Deferring support for binary interpolation</a></h3>
|
||
<p>Supporting binary interpolation with this syntax would be relatively
|
||
straightforward (the elements in the parsed fields tuple would just be
|
||
byte strings rather than text strings, and the default renderer would be
|
||
markedly less useful), but poses a significant likelihood of producing
|
||
confusing type errors when a text renderer was presented with
|
||
binary input.</p>
|
||
<p>Since the proposed syntax is useful without binary interpolation support, and
|
||
such support can be readily added later, further consideration of binary
|
||
interpolation is considered out of scope for the current PEP.</p>
|
||
</section>
|
||
<section id="interoperability-with-str-only-interfaces">
|
||
<h3><a class="toc-backref" href="#interoperability-with-str-only-interfaces" role="doc-backlink">Interoperability with str-only interfaces</a></h3>
|
||
<p>For interoperability with interfaces that only accept strings, interpolation
|
||
templates can still be prerendered with <code class="docutils literal notranslate"><span class="pre">format</span></code>, rather than delegating the
|
||
rendering to the called function.</p>
|
||
<p>This reflects the key difference from <a class="pep reference internal" href="../pep-0498/" title="PEP 498 – Literal String Interpolation">PEP 498</a>, which <em>always</em> eagerly applies
|
||
the default rendering, without any way to delegate the choice of renderer to
|
||
another section of the code.</p>
|
||
</section>
|
||
<section id="preserving-the-raw-template-string">
|
||
<h3><a class="toc-backref" href="#preserving-the-raw-template-string" role="doc-backlink">Preserving the raw template string</a></h3>
|
||
<p>Earlier versions of this PEP failed to make the raw template string available
|
||
on the interpolation template. Retaining it makes it possible to provide a more
|
||
attractive template representation, as well as providing the ability to
|
||
precisely reconstruct the original string, including both the expression text
|
||
and the details of any eagerly rendered substitution fields in format specifiers.</p>
|
||
</section>
|
||
<section id="creating-a-rich-object-rather-than-a-global-name-lookup">
|
||
<h3><a class="toc-backref" href="#creating-a-rich-object-rather-than-a-global-name-lookup" role="doc-backlink">Creating a rich object rather than a global name lookup</a></h3>
|
||
<p>Earlier versions of this PEP used an <code class="docutils literal notranslate"><span class="pre">__interpolate__</span></code> builtin, rather than
|
||
a creating a new kind of object for later consumption by interpolation
|
||
functions. Creating a rich descriptive object with a useful default renderer
|
||
made it much easier to support customisation of the semantics of interpolation.</p>
|
||
</section>
|
||
<section id="building-atop-pep-498-rather-than-competing-with-it">
|
||
<h3><a class="toc-backref" href="#building-atop-pep-498-rather-than-competing-with-it" role="doc-backlink">Building atop PEP 498, rather than competing with it</a></h3>
|
||
<p>Earlier versions of this PEP attempted to serve as a complete substitute for
|
||
<a class="pep reference internal" href="../pep-0498/" title="PEP 498 – Literal String Interpolation">PEP 498</a>, rather than building a more flexible delayed rendering capability on
|
||
top of <a class="pep reference internal" href="../pep-0498/" title="PEP 498 – Literal String Interpolation">PEP 498</a>’s eager rendering.</p>
|
||
<p>Assuming the presence of f-strings as a supporting capability simplified a
|
||
number of aspects of the proposal in this PEP (such as how to handle substitution
|
||
fields in format specifiers)</p>
|
||
</section>
|
||
<section id="deferring-consideration-of-possible-use-in-i18n-use-cases">
|
||
<h3><a class="toc-backref" href="#deferring-consideration-of-possible-use-in-i18n-use-cases" role="doc-backlink">Deferring consideration of possible use in i18n use cases</a></h3>
|
||
<p>The initial motivating use case for this PEP was providing a cleaner syntax
|
||
for i18n translation, as that requires access to the original unmodified
|
||
template. As such, it focused on compatibility with the substitution syntax used
|
||
in Python’s <code class="docutils literal notranslate"><span class="pre">string.Template</span></code> formatting and Mozilla’s l20n project.</p>
|
||
<p>However, subsequent discussion revealed there are significant additional
|
||
considerations to be taken into account in the i18n use case, which don’t
|
||
impact the simpler cases of handling interpolation into security sensitive
|
||
contexts (like HTML, system shells, and database queries), or producing
|
||
application debugging messages in the preferred language of the development
|
||
team (rather than the native language of end users).</p>
|
||
<p>Due to the original design of the <code class="docutils literal notranslate"><span class="pre">str.format</span></code> substitution syntax in <a class="pep reference internal" href="../pep-3101/" title="PEP 3101 – Advanced String Formatting">PEP 3101</a>
|
||
being inspired by C#’s string formatting syntax, the specific field
|
||
substitution syntax used in <a class="pep reference internal" href="../pep-0498/" title="PEP 498 – Literal String Interpolation">PEP 498</a> is consistent not only with Python’s own <code class="docutils literal notranslate"><span class="pre">str.format</span></code> syntax, but also with string formatting in C#, including the
|
||
native “$-string” interpolation syntax introduced in C# 6.0 (released in July
|
||
2015). The related <code class="docutils literal notranslate"><span class="pre">IFormattable</span></code> interface in C# forms the basis of a
|
||
<a class="reference external" href="https://msdn.microsoft.com/en-us/library/system.iformattable.aspx">number of elements</a> of C#’s internationalization and localization
|
||
support.</p>
|
||
<p>This means that while this particular substitution syntax may not
|
||
currently be widely used for translation of <em>Python</em> applications (losing out
|
||
to traditional %-formatting and the designed-specifically-for-i18n
|
||
<code class="docutils literal notranslate"><span class="pre">string.Template</span></code> formatting), it <em>is</em> a popular translation format in the
|
||
wider software development ecosystem (since it is already the preferred
|
||
format for translating C# applications).</p>
|
||
</section>
|
||
</section>
|
||
<section id="acknowledgements">
|
||
<h2><a class="toc-backref" href="#acknowledgements" role="doc-backlink">Acknowledgements</a></h2>
|
||
<ul class="simple">
|
||
<li>Eric V. Smith for creating <a class="pep reference internal" href="../pep-0498/" title="PEP 498 – Literal String Interpolation">PEP 498</a> and demonstrating the feasibility of
|
||
arbitrary expression substitution in string interpolation</li>
|
||
<li>Barry Warsaw, Armin Ronacher, and Mike Miller for their contributions to
|
||
exploring the feasibility of using this model of delayed rendering in i18n
|
||
use cases (even though the ultimate conclusion was that it was a poor fit,
|
||
at least for current approaches to i18n in Python)</li>
|
||
</ul>
|
||
</section>
|
||
<section id="references">
|
||
<h2><a class="toc-backref" href="#references" role="doc-backlink">References</a></h2>
|
||
<ul class="simple">
|
||
<li><a class="reference external" href="https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting">%-formatting</a></li>
|
||
<li><a class="reference external" href="https://docs.python.org/3/library/string.html#formatstrings">str.format</a></li>
|
||
<li><a class="reference external" href="https://docs.python.org/3/library/string.html#template-strings">string.Template documentation</a></li>
|
||
<li><a class="pep reference internal" href="../pep-0215/" title="PEP 215 – String Interpolation">PEP 215</a>: String Interpolation</li>
|
||
<li><a class="pep reference internal" href="../pep-0292/" title="PEP 292 – Simpler String Substitutions">PEP 292</a>: Simpler String Substitutions</li>
|
||
<li><a class="pep reference internal" href="../pep-3101/" title="PEP 3101 – Advanced String Formatting">PEP 3101</a>: Advanced String Formatting</li>
|
||
<li><a class="pep reference internal" href="../pep-0498/" title="PEP 498 – Literal String Interpolation">PEP 498</a>: Literal string formatting</li>
|
||
<li><a class="reference external" href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated">FormattableString and C# native string interpolation</a></li>
|
||
<li><a class="reference external" href="https://docs.microsoft.com/en-us/dotnet/api/system.iformattable">IFormattable interface in C# (see remarks for globalization notes)</a></li>
|
||
<li><a class="reference external" href="https://docs.julialang.org/en/v1/manual/running-external-programs/">Running external commands in Julia</a></li>
|
||
</ul>
|
||
</section>
|
||
<section id="copyright">
|
||
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
|
||
<p>This document has been placed in the public domain.</p>
|
||
</section>
|
||
</section>
|
||
<hr class="docutils" />
|
||
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0501.rst">https://github.com/python/peps/blob/main/peps/pep-0501.rst</a></p>
|
||
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0501.rst">2023-10-11 12:05:51 GMT</a></p>
|
||
|
||
</article>
|
||
<nav id="pep-sidebar">
|
||
<h2>Contents</h2>
|
||
<ul>
|
||
<li><a class="reference internal" href="#abstract">Abstract</a></li>
|
||
<li><a class="reference internal" href="#pep-deferral">PEP Deferral</a></li>
|
||
<li><a class="reference internal" href="#summary-of-differences-from-pep-498">Summary of differences from PEP 498</a></li>
|
||
<li><a class="reference internal" href="#proposal">Proposal</a></li>
|
||
<li><a class="reference internal" href="#rationale">Rationale</a></li>
|
||
<li><a class="reference internal" href="#specification">Specification</a><ul>
|
||
<li><a class="reference internal" href="#conversion-specifiers">Conversion specifiers</a></li>
|
||
<li><a class="reference internal" href="#writing-custom-renderers">Writing custom renderers</a></li>
|
||
<li><a class="reference internal" href="#expression-evaluation">Expression evaluation</a></li>
|
||
<li><a class="reference internal" href="#handling-code-injection-attacks">Handling code injection attacks</a></li>
|
||
<li><a class="reference internal" href="#format-specifiers">Format specifiers</a></li>
|
||
<li><a class="reference internal" href="#error-handling">Error handling</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#possible-integration-with-the-logging-module">Possible integration with the logging module</a></li>
|
||
<li><a class="reference internal" href="#discussion">Discussion</a><ul>
|
||
<li><a class="reference internal" href="#deferring-support-for-binary-interpolation">Deferring support for binary interpolation</a></li>
|
||
<li><a class="reference internal" href="#interoperability-with-str-only-interfaces">Interoperability with str-only interfaces</a></li>
|
||
<li><a class="reference internal" href="#preserving-the-raw-template-string">Preserving the raw template string</a></li>
|
||
<li><a class="reference internal" href="#creating-a-rich-object-rather-than-a-global-name-lookup">Creating a rich object rather than a global name lookup</a></li>
|
||
<li><a class="reference internal" href="#building-atop-pep-498-rather-than-competing-with-it">Building atop PEP 498, rather than competing with it</a></li>
|
||
<li><a class="reference internal" href="#deferring-consideration-of-possible-use-in-i18n-use-cases">Deferring consideration of possible use in i18n use cases</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#acknowledgements">Acknowledgements</a></li>
|
||
<li><a class="reference internal" href="#references">References</a></li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
|
||
<br>
|
||
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0501.rst">Page Source (GitHub)</a>
|
||
</nav>
|
||
</section>
|
||
<script src="../_static/colour_scheme.js"></script>
|
||
<script src="../_static/wrap_tables.js"></script>
|
||
<script src="../_static/sticky_banner.js"></script>
|
||
</body>
|
||
</html> |