Skip to content

Commit a9f7d62

Browse files
committed
Backport of PEP 3101, Advanced String Formatting, from py3k.
Highlights: - Adding PyObject_Format. - Adding string.Format class. - Adding __format__ for str, unicode, int, long, float, datetime. - Adding builtin format. - Adding ''.format and u''.format. - str/unicode fixups for formatters. The files in Objects/stringlib that implement PEP 3101 (stringdefs.h, unicodedefs.h, formatter.h, string_format.h) are identical in trunk and py3k. Any changes from here on should be made to trunk, and changes will propogate to py3k).
1 parent e139688 commit a9f7d62

27 files changed

+3873
-23
lines changed

Include/abstract.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,13 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
529529
530530
*/
531531

532+
PyAPI_FUNC(PyObject *) PyObject_Format(PyObject* obj,
533+
PyObject *format_spec);
534+
/*
535+
Takes an arbitrary object and returns the result of
536+
calling obj.__format__(format_spec).
537+
*/
538+
532539
/* Iterators */
533540

534541
PyAPI_FUNC(PyObject *) PyObject_GetIter(PyObject *);

Include/formatter_string.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
PyObject *
2+
string__format__(PyObject *self, PyObject *args);
3+
4+
PyObject *
5+
string_long__format__(PyObject *self, PyObject *args);
6+
7+
PyObject *
8+
string_int__format__(PyObject *self, PyObject *args);
9+
10+
PyObject *
11+
string_float__format__(PyObject *self, PyObject *args);
12+

Include/formatter_unicode.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
PyObject *
2+
unicode__format__(PyObject *self, PyObject *args);
3+
4+
PyObject *
5+
unicode_long__format__(PyObject *self, PyObject *args);
6+
7+
PyObject *
8+
unicode_int__format__(PyObject *self, PyObject *args);
9+
10+
PyObject *
11+
unicode_float__format__(PyObject *self, PyObject *args);
12+

Lib/string.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,3 +527,115 @@ def replace(s, old, new, maxsplit=-1):
527527
letters = lowercase + uppercase
528528
except ImportError:
529529
pass # Use the original versions
530+
531+
########################################################################
532+
# the Formatter class
533+
# see PEP 3101 for details and purpose of this class
534+
535+
# The hard parts are reused from the C implementation. They're
536+
# exposed here via the sys module. sys was chosen because it's always
537+
# available and doesn't have to be dynamically loaded.
538+
539+
# The overall parser is implemented in str._formatter_parser.
540+
# The field name parser is implemented in str._formatter_field_name_split
541+
542+
class Formatter(object):
543+
def format(self, format_string, *args, **kwargs):
544+
return self.vformat(format_string, args, kwargs)
545+
546+
def vformat(self, format_string, args, kwargs):
547+
used_args = set()
548+
result = self._vformat(format_string, args, kwargs, used_args, 2)
549+
self.check_unused_args(used_args, args, kwargs)
550+
return result
551+
552+
def _vformat(self, format_string, args, kwargs, used_args, recursion_depth):
553+
if recursion_depth < 0:
554+
raise ValueError('Max string recursion exceeded')
555+
result = []
556+
for literal_text, field_name, format_spec, conversion in \
557+
self.parse(format_string):
558+
559+
# output the literal text
560+
if literal_text:
561+
result.append(literal_text)
562+
563+
# if there's a field, output it
564+
if field_name is not None:
565+
# this is some markup, find the object and do
566+
# the formatting
567+
568+
# given the field_name, find the object it references
569+
# and the argument it came from
570+
obj, arg_used = self.get_field(field_name, args, kwargs)
571+
used_args.add(arg_used)
572+
573+
# do any conversion on the resulting object
574+
obj = self.convert_field(obj, conversion)
575+
576+
# expand the format spec, if needed
577+
format_spec = self._vformat(format_spec, args, kwargs,
578+
used_args, recursion_depth-1)
579+
580+
# format the object and append to the result
581+
result.append(self.format_field(obj, format_spec))
582+
583+
return ''.join(result)
584+
585+
586+
def get_value(self, key, args, kwargs):
587+
if isinstance(key, (int, long)):
588+
return args[key]
589+
else:
590+
return kwargs[key]
591+
592+
593+
def check_unused_args(self, used_args, args, kwargs):
594+
pass
595+
596+
597+
def format_field(self, value, format_spec):
598+
return format(value, format_spec)
599+
600+
601+
def convert_field(self, value, conversion):
602+
# do any conversion on the resulting object
603+
if conversion == 'r':
604+
return repr(value)
605+
elif conversion == 's':
606+
return str(value)
607+
elif conversion is None:
608+
return value
609+
raise ValueError("Unknown converion specifier {0!s}".format(conversion))
610+
611+
612+
# returns an iterable that contains tuples of the form:
613+
# (literal_text, field_name, format_spec, conversion)
614+
# literal_text can be zero length
615+
# field_name can be None, in which case there's no
616+
# object to format and output
617+
# if field_name is not None, it is looked up, formatted
618+
# with format_spec and conversion and then used
619+
def parse(self, format_string):
620+
return format_string._formatter_parser()
621+
622+
623+
# given a field_name, find the object it references.
624+
# field_name: the field being looked up, e.g. "0.name"
625+
# or "lookup[3]"
626+
# used_args: a set of which args have been used
627+
# args, kwargs: as passed in to vformat
628+
def get_field(self, field_name, args, kwargs):
629+
first, rest = field_name._formatter_field_name_split()
630+
631+
obj = self.get_value(first, args, kwargs)
632+
633+
# loop through the rest of the field_name, doing
634+
# getattr or getitem as needed
635+
for is_attr, i in rest:
636+
if is_attr:
637+
obj = getattr(obj, i)
638+
else:
639+
obj = obj[i]
640+
641+
return obj, first

Lib/test/test_builtin.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2012,6 +2012,101 @@ def test_baddecorator(self):
20122012
data = 'The quick Brown fox Jumped over The lazy Dog'.split()
20132013
self.assertRaises(TypeError, sorted, data, None, lambda x,y: 0)
20142014

2015+
def test_format(self):
2016+
# Test the basic machinery of the format() builtin. Don't test
2017+
# the specifics of the various formatters
2018+
self.assertEqual(format(3, ''), '3')
2019+
2020+
# Returns some classes to use for various tests. There's
2021+
# an old-style version, and a new-style version
2022+
def classes_new():
2023+
class A(object):
2024+
def __init__(self, x):
2025+
self.x = x
2026+
def __format__(self, format_spec):
2027+
return str(self.x) + format_spec
2028+
class DerivedFromA(A):
2029+
pass
2030+
2031+
class Simple(object): pass
2032+
class DerivedFromSimple(Simple):
2033+
def __init__(self, x):
2034+
self.x = x
2035+
def __format__(self, format_spec):
2036+
return str(self.x) + format_spec
2037+
class DerivedFromSimple2(DerivedFromSimple): pass
2038+
return A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2
2039+
2040+
# In 3.0, classes_classic has the same meaning as classes_new
2041+
def classes_classic():
2042+
class A:
2043+
def __init__(self, x):
2044+
self.x = x
2045+
def __format__(self, format_spec):
2046+
return str(self.x) + format_spec
2047+
class DerivedFromA(A):
2048+
pass
2049+
2050+
class Simple: pass
2051+
class DerivedFromSimple(Simple):
2052+
def __init__(self, x):
2053+
self.x = x
2054+
def __format__(self, format_spec):
2055+
return str(self.x) + format_spec
2056+
class DerivedFromSimple2(DerivedFromSimple): pass
2057+
return A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2
2058+
2059+
def class_test(A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2):
2060+
self.assertEqual(format(A(3), 'spec'), '3spec')
2061+
self.assertEqual(format(DerivedFromA(4), 'spec'), '4spec')
2062+
self.assertEqual(format(DerivedFromSimple(5), 'abc'), '5abc')
2063+
self.assertEqual(format(DerivedFromSimple2(10), 'abcdef'),
2064+
'10abcdef')
2065+
2066+
class_test(*classes_new())
2067+
class_test(*classes_classic())
2068+
2069+
def empty_format_spec(value):
2070+
# test that:
2071+
# format(x, '') == str(x)
2072+
# format(x) == str(x)
2073+
self.assertEqual(format(value, ""), str(value))
2074+
self.assertEqual(format(value), str(value))
2075+
2076+
# for builtin types, format(x, "") == str(x)
2077+
empty_format_spec(17**13)
2078+
empty_format_spec(1.0)
2079+
empty_format_spec(3.1415e104)
2080+
empty_format_spec(-3.1415e104)
2081+
empty_format_spec(3.1415e-104)
2082+
empty_format_spec(-3.1415e-104)
2083+
empty_format_spec(object)
2084+
empty_format_spec(None)
2085+
2086+
# TypeError because self.__format__ returns the wrong type
2087+
class BadFormatResult:
2088+
def __format__(self, format_spec):
2089+
return 1.0
2090+
self.assertRaises(TypeError, format, BadFormatResult(), "")
2091+
2092+
# TypeError because format_spec is not unicode or str
2093+
self.assertRaises(TypeError, format, object(), 4)
2094+
self.assertRaises(TypeError, format, object(), object())
2095+
2096+
# tests for object.__format__ really belong elsewhere, but
2097+
# there's no good place to put them
2098+
x = object().__format__('')
2099+
self.assert_(x.startswith('<object object at'))
2100+
2101+
# first argument to object.__format__ must be string
2102+
self.assertRaises(TypeError, object().__format__, 3)
2103+
self.assertRaises(TypeError, object().__format__, object())
2104+
self.assertRaises(TypeError, object().__format__, None)
2105+
2106+
# make sure we can take a subclass of str as a format spec
2107+
class DerivedFromStr(str): pass
2108+
self.assertEqual(format(0, DerivedFromStr('10')), ' 0')
2109+
20152110
def test_main(verbose=None):
20162111
test_classes = (BuiltinTest, TestSorted)
20172112

Lib/test/test_datetime.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,32 @@ def test_strftime(self):
854854
# A naive object replaces %z and %Z w/ empty strings.
855855
self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
856856

857+
def test_format(self):
858+
dt = self.theclass(2007, 9, 10)
859+
self.assertEqual(dt.__format__(''), str(dt))
860+
861+
# check that a derived class's __str__() gets called
862+
class A(self.theclass):
863+
def __str__(self):
864+
return 'A'
865+
a = A(2007, 9, 10)
866+
self.assertEqual(a.__format__(''), 'A')
867+
868+
# check that a derived class's strftime gets called
869+
class B(self.theclass):
870+
def strftime(self, format_spec):
871+
return 'B'
872+
b = B(2007, 9, 10)
873+
self.assertEqual(b.__format__(''), str(dt))
874+
875+
for fmt in ["m:%m d:%d y:%y",
876+
"m:%m d:%d y:%y H:%H M:%M S:%S",
877+
"%z %Z",
878+
]:
879+
self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
880+
self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
881+
self.assertEqual(b.__format__(fmt), 'B')
882+
857883
def test_resolution_info(self):
858884
self.assert_(isinstance(self.theclass.min, self.theclass))
859885
self.assert_(isinstance(self.theclass.max, self.theclass))
@@ -1136,6 +1162,32 @@ def test_isoformat(self):
11361162
# str is ISO format with the separator forced to a blank.
11371163
self.assertEqual(str(t), "0002-03-02 00:00:00")
11381164

1165+
def test_format(self):
1166+
dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
1167+
self.assertEqual(dt.__format__(''), str(dt))
1168+
1169+
# check that a derived class's __str__() gets called
1170+
class A(self.theclass):
1171+
def __str__(self):
1172+
return 'A'
1173+
a = A(2007, 9, 10, 4, 5, 1, 123)
1174+
self.assertEqual(a.__format__(''), 'A')
1175+
1176+
# check that a derived class's strftime gets called
1177+
class B(self.theclass):
1178+
def strftime(self, format_spec):
1179+
return 'B'
1180+
b = B(2007, 9, 10, 4, 5, 1, 123)
1181+
self.assertEqual(b.__format__(''), str(dt))
1182+
1183+
for fmt in ["m:%m d:%d y:%y",
1184+
"m:%m d:%d y:%y H:%H M:%M S:%S",
1185+
"%z %Z",
1186+
]:
1187+
self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1188+
self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1189+
self.assertEqual(b.__format__(fmt), 'B')
1190+
11391191
def test_more_ctime(self):
11401192
# Test fields that TestDate doesn't touch.
11411193
import time
@@ -1767,6 +1819,30 @@ def test_strftime(self):
17671819
# A naive object replaces %z and %Z with empty strings.
17681820
self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
17691821

1822+
def test_format(self):
1823+
t = self.theclass(1, 2, 3, 4)
1824+
self.assertEqual(t.__format__(''), str(t))
1825+
1826+
# check that a derived class's __str__() gets called
1827+
class A(self.theclass):
1828+
def __str__(self):
1829+
return 'A'
1830+
a = A(1, 2, 3, 4)
1831+
self.assertEqual(a.__format__(''), 'A')
1832+
1833+
# check that a derived class's strftime gets called
1834+
class B(self.theclass):
1835+
def strftime(self, format_spec):
1836+
return 'B'
1837+
b = B(1, 2, 3, 4)
1838+
self.assertEqual(b.__format__(''), str(t))
1839+
1840+
for fmt in ['%H %M %S',
1841+
]:
1842+
self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1843+
self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1844+
self.assertEqual(b.__format__(fmt), 'B')
1845+
17701846
def test_str(self):
17711847
self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
17721848
self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")

Lib/test/test_descrtut.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ def merge(self, other):
183183
'__delslice__',
184184
'__doc__',
185185
'__eq__',
186+
'__format__',
186187
'__ge__',
187188
'__getattribute__',
188189
'__getitem__',

0 commit comments

Comments
 (0)