mirror of
https://github.com/python/cpython.git
synced 2025-08-01 23:53:15 +00:00
gh-71494: string.Formatter: support keys/attributes in unnumbered fields (GH-21767)
This commit is contained in:
parent
0373926260
commit
22b2d37f42
3 changed files with 28 additions and 7 deletions
|
@ -212,19 +212,20 @@ class Formatter:
|
||||||
# this is some markup, find the object and do
|
# this is some markup, find the object and do
|
||||||
# the formatting
|
# the formatting
|
||||||
|
|
||||||
# handle arg indexing when empty field_names are given.
|
# handle arg indexing when empty field first parts are given.
|
||||||
if field_name == '':
|
field_first, _ = _string.formatter_field_name_split(field_name)
|
||||||
|
if field_first == '':
|
||||||
if auto_arg_index is False:
|
if auto_arg_index is False:
|
||||||
raise ValueError('cannot switch from manual field '
|
raise ValueError('cannot switch from manual field '
|
||||||
'specification to automatic field '
|
'specification to automatic field '
|
||||||
'numbering')
|
'numbering')
|
||||||
field_name = str(auto_arg_index)
|
field_name = str(auto_arg_index) + field_name
|
||||||
auto_arg_index += 1
|
auto_arg_index += 1
|
||||||
elif field_name.isdigit():
|
elif isinstance(field_first, int):
|
||||||
if auto_arg_index:
|
if auto_arg_index:
|
||||||
raise ValueError('cannot switch from manual field '
|
raise ValueError('cannot switch from automatic field '
|
||||||
'specification to automatic field '
|
'numbering to manual field '
|
||||||
'numbering')
|
'specification')
|
||||||
# disable auto arg incrementing, if it gets
|
# disable auto arg incrementing, if it gets
|
||||||
# used later on, then an exception will be raised
|
# used later on, then an exception will be raised
|
||||||
auto_arg_index = False
|
auto_arg_index = False
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import unittest
|
import unittest
|
||||||
import string
|
import string
|
||||||
from string import Template
|
from string import Template
|
||||||
|
import types
|
||||||
|
|
||||||
|
|
||||||
class ModuleTest(unittest.TestCase):
|
class ModuleTest(unittest.TestCase):
|
||||||
|
@ -101,6 +102,24 @@ class ModuleTest(unittest.TestCase):
|
||||||
with self.assertRaises(KeyError):
|
with self.assertRaises(KeyError):
|
||||||
fmt.format("{0[2]}{0[0]}", {})
|
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):
|
def test_override_get_value(self):
|
||||||
class NamespaceFormatter(string.Formatter):
|
class NamespaceFormatter(string.Formatter):
|
||||||
def __init__(self, namespace={}):
|
def __init__(self, namespace={}):
|
||||||
|
|
|
@ -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]}'.
|
Loading…
Add table
Add a link
Reference in a new issue