Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7ade648

Browse files
committed
PEP 3101: Completed string.Formatter class. Reimplemented field_name to object transformation.
1 parent 2bf4d5b commit 7ade648

7 files changed

Lines changed: 497 additions & 197 deletions

File tree

Include/unicodeobject.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,8 +1440,7 @@ PyAPI_FUNC(Py_UNICODE*) Py_UNICODE_strchr(
14401440
PyObject *
14411441
_unicodeformatter_iterator(PyObject *str);
14421442
PyObject *
1443-
_unicodeformatter_lookup(PyObject *field_name, PyObject *args,
1444-
PyObject *kwargs);
1443+
_unicodeformatter_field_name_split(PyObject *field_name);
14451444

14461445
#ifdef __cplusplus
14471446
}

Lib/string.py

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -200,31 +200,59 @@ def convert(mo):
200200
# exposed here via the sys module. sys was chosen because it's always
201201
# available and doesn't have to be dynamically loaded.
202202

203-
# The parser is implemented in sys._formatter_parser.
204-
# The "object lookup" is implemented in sys._formatter_lookup
203+
# The overall parser is implemented in sys._formatter_parser.
204+
# The field name parser is implemented in sys._formatter_field_name_split
205205

206-
from sys import _formatter_parser, _formatter_lookup
206+
from sys import _formatter_parser, _formatter_field_name_split
207207

208208
class Formatter:
209209
def format(self, format_string, *args, **kwargs):
210210
return self.vformat(format_string, args, kwargs)
211211

212212
def vformat(self, format_string, args, kwargs):
213+
used_args = set()
213214
result = []
214215
for (is_markup, literal, field_name, format_spec, conversion) in \
215216
_formatter_parser(format_string):
216217
if is_markup:
217-
# find the object
218-
index, name, obj = _formatter_lookup(field_name, args, kwargs)
218+
# given the field_name, find the object it references
219+
220+
# split it into the first part, and and iterator that
221+
# looks over the rest
222+
first, rest = _formatter_field_name_split(field_name)
223+
224+
used_args.add(first)
225+
obj = self.get_value(first, args, kwargs)
226+
227+
# loop through the rest of the field_name, doing
228+
# getattr or getitem as needed
229+
for is_attr, i in rest:
230+
if is_attr:
231+
obj = getattr(obj, i)
232+
else:
233+
obj = obj[i]
234+
235+
# do any conversion on the resulting object
236+
if conversion == 'r':
237+
obj = repr(obj)
238+
elif conversion == 's':
239+
obj = str(obj)
240+
241+
# format the object and append to the result
242+
result.append(self.format_field(obj, format_spec))
219243
else:
220244
result.append(literal)
245+
self.check_unused_args(used_args, args, kwargs)
221246
return ''.join(result)
222247

223248
def get_value(self, key, args, kwargs):
224-
pass
249+
if isinstance(key, int):
250+
return args[key]
251+
else:
252+
return kwargs[key]
225253

226254
def check_unused_args(self, used_args, args, kwargs):
227255
pass
228256

229257
def format_field(self, value, format_spec):
230-
pass
258+
return format(value, format_spec)

Lib/test/test_string.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,27 @@ def test_formatter(self):
1919
fmt = string.Formatter()
2020
self.assertEqual(fmt.format("foo"), "foo")
2121

22-
# Formatter not working you for lookups
23-
#self.assertEqual(fmt.format("foo{0}", "bar"), "foobar")
22+
self.assertEqual(fmt.format("foo{0}", "bar"), "foobar")
23+
self.assertEqual(fmt.format("foo{1}{0}-{1}", "bar", 6), "foo6bar-6")
24+
self.assertEqual(fmt.format("-{arg!r}-", arg='test'), "-'test'-")
25+
26+
class NamespaceFormatter(string.Formatter):
27+
def __init__(self, namespace={}):
28+
string.Formatter.__init__(self)
29+
self.namespace = namespace
30+
31+
def get_value(self, key, args, kwds):
32+
if isinstance(key, str):
33+
try:
34+
# Check explicitly passed arguments first
35+
return kwds[key]
36+
except KeyError:
37+
return self.namespace[key]
38+
else:
39+
string.Formatter.get_value(key, args, kwds)
40+
41+
fmt = NamespaceFormatter({'greeting':'hello'})
42+
self.assertEqual(fmt.format("{greeting}, world!"), 'hello, world!')
2443

2544

2645
def test_maketrans(self):

Lib/test/test_unicode.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,10 @@ def __format__(self, format_spec):
449449
self.assertEqual('}}{{'.format(), '}{')
450450
self.assertEqual('}}x{{'.format(), '}x{')
451451

452+
# weird field names
453+
self.assertEqual("{0[foo-bar]}".format({'foo-bar':'baz'}), 'baz')
454+
self.assertEqual("{0[foo bar]}".format({'foo bar':'baz'}), 'baz')
455+
452456
self.assertEqual('{foo._x}'.format(foo=C(20)), '20')
453457
self.assertEqual('{1}{0}'.format(D(10), D(20)), '2010')
454458
self.assertEqual('{0._x.x}'.format(C(D('abc'))), 'abc')
@@ -539,7 +543,11 @@ def __format__(self, format_spec):
539543
self.assertRaises(ValueError, "}".format)
540544
self.assertRaises(ValueError, "abc{0:{}".format)
541545
self.assertRaises(ValueError, "{0".format)
546+
self.assertRaises(ValueError, "{0.}".format)
547+
self.assertRaises(ValueError, "{0[}".format)
548+
self.assertRaises(ValueError, "{0]}".format)
542549
self.assertRaises(ValueError, "{0.[]}".format)
550+
self.assertRaises(ValueError, "{0..foo}".format, 0)
543551
self.assertRaises(ValueError, "{0[0}".format)
544552
self.assertRaises(ValueError, "{0[0:foo}".format)
545553
self.assertRaises(ValueError, "{c]}".format)
@@ -551,6 +559,7 @@ def __format__(self, format_spec):
551559
self.assertRaises(ValueError, "{0!rs}".format)
552560
self.assertRaises(ValueError, "{!}".format)
553561
self.assertRaises(ValueError, "{:}".format)
562+
self.assertRaises(ValueError, "{:s}".format)
554563
self.assertRaises(ValueError, "{}".format)
555564

556565
# can't have a replacement on the field name portion

0 commit comments

Comments
 (0)