https://github.com/python/cpython/commit/22b2d37f4211f51a3c90680edeb4c10261f58158
commit: 22b2d37f4211f51a3c90680edeb4c10261f58158
branch: main
author: qm2k <[email protected]>
committer: encukou <[email protected]>
date: 2025-01-31T13:16:24+01:00
summary:
gh-71494: string.Formatter: support keys/attributes in unnumbered fields
(GH-21767)
files:
A Misc/NEWS.d/next/Library/2020-08-07-16-55-57.bpo-27307.Xqzzda.rst
M Lib/string.py
M Lib/test/test_string.py
diff --git a/Lib/string.py b/Lib/string.py
index 2eab6d4f595c4e..c4f05c7223ce8a 100644
--- a/Lib/string.py
+++ b/Lib/string.py
@@ -212,19 +212,20 @@ def _vformat(self, format_string, args, kwargs,
used_args, recursion_depth,
# this is some markup, find the object and do
# the formatting
- # handle arg indexing when empty field_names are given.
- if field_name == '':
+ # handle arg indexing when empty field first parts are given.
+ field_first, _ = _string.formatter_field_name_split(field_name)
+ if field_first == '':
if auto_arg_index is False:
raise ValueError('cannot switch from manual field '
'specification to automatic field '
'numbering')
- field_name = str(auto_arg_index)
+ field_name = str(auto_arg_index) + field_name
auto_arg_index += 1
- elif field_name.isdigit():
+ elif isinstance(field_first, int):
if auto_arg_index:
- raise ValueError('cannot switch from manual field '
- 'specification to automatic field '
- 'numbering')
+ raise ValueError('cannot switch from automatic field '
+ 'numbering to manual field '
+ 'specification')
# disable auto arg incrementing, if it gets
# used later on, then an exception will be raised
auto_arg_index = False
diff --git a/Lib/test/test_string.py b/Lib/test/test_string.py
index 824b89ad517c12..f6d112d8a93ec4 100644
--- a/Lib/test/test_string.py
+++ b/Lib/test/test_string.py
@@ -1,6 +1,7 @@
import unittest
import string
from string import Template
+import types
class ModuleTest(unittest.TestCase):
@@ -101,6 +102,24 @@ def test_index_lookup(self):
with self.assertRaises(KeyError):
fmt.format("{0[2]}{0[0]}", {})
+ def test_auto_numbering_lookup(self):
+ fmt = string.Formatter()
+ namespace = types.SimpleNamespace(foo=types.SimpleNamespace(bar='baz'))
+ widths = [None, types.SimpleNamespace(qux=4)]
+ self.assertEqual(
+ fmt.format("{.foo.bar:{[1].qux}}", namespace, widths), 'baz ')
+
+ def test_auto_numbering_reenterability(self):
+ class ReenteringFormatter(string.Formatter):
+ def format_field(self, value, format_spec):
+ if format_spec.isdigit() and int(format_spec) > 0:
+ return self.format('{:{}}!', value, int(format_spec) - 1)
+ else:
+ return super().format_field(value, format_spec)
+ fmt = ReenteringFormatter()
+ x = types.SimpleNamespace(a='X')
+ self.assertEqual(fmt.format('{.a:{}}', x, 3), 'X!!!')
+
def test_override_get_value(self):
class NamespaceFormatter(string.Formatter):
def __init__(self, namespace={}):
diff --git a/Misc/NEWS.d/next/Library/2020-08-07-16-55-57.bpo-27307.Xqzzda.rst
b/Misc/NEWS.d/next/Library/2020-08-07-16-55-57.bpo-27307.Xqzzda.rst
new file mode 100644
index 00000000000000..6e7a856d994cb6
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-08-07-16-55-57.bpo-27307.Xqzzda.rst
@@ -0,0 +1 @@
+Add attribute and item access support to :class:`string.Formatter` in
auto-numbering mode, which allows format strings like '{.name}' and '{[1]}'.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]