[Python-checkins] peps: Flushed out more text. Other than removing !s, !r, and !a, I think I am mostly

eric.smith python-checkins at python.org
Sat Aug 22 00:35:03 CEST 2015


https://hg.python.org/peps/rev/9ecd060e9d3b
changeset:   5967:9ecd060e9d3b
user:        Eric V. Smith <eric at trueblade.com>
date:        Fri Aug 21 18:35:55 2015 -0400
summary:
  Flushed out more text. Other than removing !s, !r, and !a, I think I am mostly done.

files:
  pep-0498.txt |  157 +++++++++++++++++++++++++++++++-------
  1 files changed, 126 insertions(+), 31 deletions(-)


diff --git a/pep-0498.txt b/pep-0498.txt
--- a/pep-0498.txt
+++ b/pep-0498.txt
@@ -25,12 +25,12 @@
 This PEP does not propose to remove or deprecate any of the existing
 string formatting mechanisms.
 
-f-strings provide a way to combine string literals with Python
-expressions, using a minimal syntax. It should be noted that an
-f-string is really an expression evaluated at run time, not a constant
-value. An f-string is a string, prefixed with 'f', that contains
-expressions inside braces. The expressions are replaced with their
-values. Some examples are::
+f-strings provide a way to embed expressions inside string literals,
+using a minimal syntax. It should be noted that an f-string is really
+an expression evaluated at run time, not a constant value. In Python
+source code, an f-string is a literal string, prefixed with 'f', that
+contains expressions inside braces. The expressions are replaced with
+their values. Some examples are::
 
   >>> import datetime
   >>> name = 'Fred'
@@ -110,11 +110,11 @@
 
 In this sense, string.Template and %-formatting have similar
 shortcomings to str.format(), but also support fewer formatting
-options. In particular, they do not support __format__, so that there
-is no way to control how a specific object is converted to a string,
-nor can it be extended to additional types that want to control how
-they are converted to strings (such as Decimal and datetime). This
-example is not possible with string.Template::
+options. In particular, they do not support the __format__ protocol,
+so that there is no way to control how a specific object is converted
+to a string, nor can it be extended to additional types that want to
+control how they are converted to strings (such as Decimal and
+datetime). This example is not possible with string.Template::
 
   >>> value = 1234
   >>> f'input={value:#0.6x}'
@@ -164,6 +164,11 @@
 globals() has access to far more information than needed to do the
 string interpolation.
 
+If locals() and globals() were used, and if a future extension to this
+PEP would add an internationalization layer before str.interpolate()
+is called, a malicious translator could get access to additional
+variables in the callers context.
+
 Guido stated [#]_ that any solution to better string interpolation
 would not use locals() or globals().
 
@@ -214,20 +219,44 @@
 str.interpolate()
 -----------------
 
-str.interpolate(mapping) will be a new method. It takes one argument:
-a mapping of field names to values. This method is the same as
-str.format_map() [#]_, with one difference: it does not interpret the
-field_name [#]_ in any way. The field_name is only used to look up the
-replacement value in the supplied mapping object. Like str.format()
-and str.format_map(), str.interpolate() does interpret and apply the
-optional conversion and format_spec. Thus, a field_name may not
-contain the characters ':' or '}', nor the strings '!s', '!r', or
-'!a'.
+str.interpolate(mapping) will be a new method on the str type. It
+takes one argument: a mapping of field names to values. This method is
+the same as str.format_map() [#]_, with one difference: it does not
+interpret the field_name [#]_ in any way. The field_name is only used
+to look up the replacement value in the supplied mapping object. Like
+str.format() and str.format_map(), str.interpolate() does interpret
+and apply the optional conversion and format_spec. Thus, a field_name
+may not contain the characters ':' or '}', nor the strings '!s', '!r',
+or '!a'.
+
+Examples::
+
+  >>> name = 'Guido'
+  >>> 'name={name}'.interpolate({'name': name})
+  'name=Guido'
+
+  >>> '{date} was on a {date:%A}. It was {weather}.'.interpolate({'weather': 'sunny', 'date': datetime.date(1991, 10, 12)})
+  '1991-10-12 was on a Saturday. It was sunny.'
+
+Like str.format_map(), only the field_name portion inside braces is
+used to look up values in the mapping. The format_spec is not used as
+part of this lookup. Thus::
+
+  >>> 'name={name:10}'.interpolate({'name': name})
+  'name=Guido     '
+
+But::
+
+  >>> 'name={name:10}'.interpolate({'name:10': name})
+  'name=Guido     '
+  Traceback (most recent call last):
+    File "<stdin>", line 1, in <module>
+  KeyError: 'name'
 
 Code equivalence
 ----------------
 
-An f-string is evaluated at run time as a call to str.interpolate().
+An f-string is evaluated at run time using a call to str.interpolate().
 
 For example, this code::
 
@@ -401,13 +430,77 @@
 Discussion
 ==========
 
-Most of the discussions on python-ideas [#]_ focused on a few issues.
+python-ideas discussion
+-----------------------
 
+Most of the discussions on python-ideas [#]_ focused on three issues:
+
+ - How to denote f-strings,
+ - How to specify the location of expressions in f-strings, and
  - Whether to allow full Python expressions.
- - How to designate f-strings, and how to specify the location of
-   expressions in them.
 
-XXX: more on the above issues.
+How to denote f-strings
+***********************
+
+Because the compiler must be involved in evaluating the expressions
+contained in the interpolated strings, there must be some way to
+denote to the compiler which strings should be evaluated. This PEP
+chose a leading 'f' character preceeding the string literal. This is
+similar to how 'b' and 'r' prefixes change the meaning of the string
+itself, at compile time. Other prefixes were suggested, such as 'i'. No
+option seemed better than the other, so 'f' was chosen.
+
+Another option was to support special functions, known to the
+compiler, such as Format(). This seems like too much magic for Python:
+not only is there a chance for collision with existing identifiers,
+the PEP author feels that it's better to signify the magic with a
+string prefix character.
+
+How to specify the location of expressions in f-strings
+*******************************************************
+
+This PEP supports the same syntax as str.format() for distinguishing
+replacement text inside strings: expressions are contained inside
+braces. There were other options suggested, such as string.Template's
+$identifier or ${expression}.
+
+While $identifier is no doubt more familiar to shell scripters and
+users of some other languages, in Python str.format() is heavily
+used. A quick search of Python's standard library shows only a handful
+of uses of string.Template, but over 1000 uses of str.format().
+
+Another proposed alternative was to have the substituted text between
+\{ and } or between \{ and \}. While this syntax would probably be
+desirable if all string literals were to support interpolation, this
+PEP only supports strings that are already marked with the leading
+'f'. As such, the PEP is using unadorned braces to denoted substituted
+text, in order to leverage end user familiarity with str.format().
+
+Supporting full Python expressions
+**********************************
+
+Many people on the python-ideas discussion wanted support for either
+only single identifiers, or a limited subset of Python expressions
+(such as the subset supported by str.format()). This PEP supports full
+Python expressions inside the braces. Without full expressions, some
+desirable usage would be forbidden::
+
+  >>> f'Column={col_idx+1}'
+  >>> f'number of items: {len(items)}'
+
+would become::
+
+  >>> col_number = col_idx+1
+  >>> f'Column={col_number}'
+  >>> n_items = len(items)
+  >>> f'number of items: {n_items}'
+
+While it's true that very ugly expressions could be included in the
+f-strings, this PEP takes the position that such uses should be
+addressed in a linter or code review::
+
+  >>> f'mapping is { {a:b for (a, b) in ((1, 2), (3, 4))}}'
+  'mapping is {1: 2, 3: 4}'
 
 Similar support in other languages
 ----------------------------------
@@ -528,13 +621,13 @@
 Future extensions:
 ==================
 
-XXX: By using another leading character (say, 'i'), we could extend
-this proposal to cover internationalization and localization. The idea
-is that the string would be passed to some lookup function before
+By using another leading character (say, 'i'), we could extend this
+proposal to cover internationalization and localization. The idea is
+that the string would be passed to some lookup function before
 .interpolate() is called on it::
 
    >>> name = 'Eric'
-   >>> fi'Name: {name}'
+   >>> i'Name: {name}'
 
 Could be translated as::
 
@@ -551,8 +644,10 @@
 be: use the field_name, use a default (possibly empty) string, or
 raise an exception.
 
-References
-==========
+There would obviously need to be some way to specify to the compiler
+what lookup function would be called.
+
+References ==========
 
 .. [#] %-formatting
        (https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting)

-- 
Repository URL: https://hg.python.org/peps


More information about the Python-checkins mailing list