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).
This commit is contained in:
Eric Smith 2008-02-17 19:46:49 +00:00
parent e139688d34
commit a9f7d62480
27 changed files with 3873 additions and 23 deletions

View File

@ -529,6 +529,13 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
*/
PyAPI_FUNC(PyObject *) PyObject_Format(PyObject* obj,
PyObject *format_spec);
/*
Takes an arbitrary object and returns the result of
calling obj.__format__(format_spec).
*/
/* Iterators */
PyAPI_FUNC(PyObject *) PyObject_GetIter(PyObject *);

View File

@ -0,0 +1,12 @@
PyObject *
string__format__(PyObject *self, PyObject *args);
PyObject *
string_long__format__(PyObject *self, PyObject *args);
PyObject *
string_int__format__(PyObject *self, PyObject *args);
PyObject *
string_float__format__(PyObject *self, PyObject *args);

View File

@ -0,0 +1,12 @@
PyObject *
unicode__format__(PyObject *self, PyObject *args);
PyObject *
unicode_long__format__(PyObject *self, PyObject *args);
PyObject *
unicode_int__format__(PyObject *self, PyObject *args);
PyObject *
unicode_float__format__(PyObject *self, PyObject *args);

View File

@ -527,3 +527,115 @@ try:
letters = lowercase + uppercase
except ImportError:
pass # Use the original versions
########################################################################
# the Formatter class
# see PEP 3101 for details and purpose of this class
# The hard parts are reused from the C implementation. They're
# exposed here via the sys module. sys was chosen because it's always
# available and doesn't have to be dynamically loaded.
# The overall parser is implemented in str._formatter_parser.
# The field name parser is implemented in str._formatter_field_name_split
class Formatter(object):
def format(self, format_string, *args, **kwargs):
return self.vformat(format_string, args, kwargs)
def vformat(self, format_string, args, kwargs):
used_args = set()
result = self._vformat(format_string, args, kwargs, used_args, 2)
self.check_unused_args(used_args, args, kwargs)
return result
def _vformat(self, format_string, args, kwargs, used_args, recursion_depth):
if recursion_depth < 0:
raise ValueError('Max string recursion exceeded')
result = []
for literal_text, field_name, format_spec, conversion in \
self.parse(format_string):
# output the literal text
if literal_text:
result.append(literal_text)
# if there's a field, output it
if field_name is not None:
# this is some markup, find the object and do
# the formatting
# given the field_name, find the object it references
# and the argument it came from
obj, arg_used = self.get_field(field_name, args, kwargs)
used_args.add(arg_used)
# do any conversion on the resulting object
obj = self.convert_field(obj, conversion)
# expand the format spec, if needed
format_spec = self._vformat(format_spec, args, kwargs,
used_args, recursion_depth-1)
# format the object and append to the result
result.append(self.format_field(obj, format_spec))
return ''.join(result)
def get_value(self, key, args, kwargs):
if isinstance(key, (int, long)):
return args[key]
else:
return kwargs[key]
def check_unused_args(self, used_args, args, kwargs):
pass
def format_field(self, value, format_spec):
return format(value, format_spec)
def convert_field(self, value, conversion):
# do any conversion on the resulting object
if conversion == 'r':
return repr(value)
elif conversion == 's':
return str(value)
elif conversion is None:
return value
raise ValueError("Unknown converion specifier {0!s}".format(conversion))
# returns an iterable that contains tuples of the form:
# (literal_text, field_name, format_spec, conversion)
# literal_text can be zero length
# field_name can be None, in which case there's no
# object to format and output
# if field_name is not None, it is looked up, formatted
# with format_spec and conversion and then used
def parse(self, format_string):
return format_string._formatter_parser()
# given a field_name, find the object it references.
# field_name: the field being looked up, e.g. "0.name"
# or "lookup[3]"
# used_args: a set of which args have been used
# args, kwargs: as passed in to vformat
def get_field(self, field_name, args, kwargs):
first, rest = field_name._formatter_field_name_split()
obj = self.get_value(first, args, kwargs)
# loop through the rest of the field_name, doing
# getattr or getitem as needed
for is_attr, i in rest:
if is_attr:
obj = getattr(obj, i)
else:
obj = obj[i]
return obj, first

View File

@ -2012,6 +2012,101 @@ class TestSorted(unittest.TestCase):
data = 'The quick Brown fox Jumped over The lazy Dog'.split()
self.assertRaises(TypeError, sorted, data, None, lambda x,y: 0)
def test_format(self):
# Test the basic machinery of the format() builtin. Don't test
# the specifics of the various formatters
self.assertEqual(format(3, ''), '3')
# Returns some classes to use for various tests. There's
# an old-style version, and a new-style version
def classes_new():
class A(object):
def __init__(self, x):
self.x = x
def __format__(self, format_spec):
return str(self.x) + format_spec
class DerivedFromA(A):
pass
class Simple(object): pass
class DerivedFromSimple(Simple):
def __init__(self, x):
self.x = x
def __format__(self, format_spec):
return str(self.x) + format_spec
class DerivedFromSimple2(DerivedFromSimple): pass
return A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2
# In 3.0, classes_classic has the same meaning as classes_new
def classes_classic():
class A:
def __init__(self, x):
self.x = x
def __format__(self, format_spec):
return str(self.x) + format_spec
class DerivedFromA(A):
pass
class Simple: pass
class DerivedFromSimple(Simple):
def __init__(self, x):
self.x = x
def __format__(self, format_spec):
return str(self.x) + format_spec
class DerivedFromSimple2(DerivedFromSimple): pass
return A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2
def class_test(A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2):
self.assertEqual(format(A(3), 'spec'), '3spec')
self.assertEqual(format(DerivedFromA(4), 'spec'), '4spec')
self.assertEqual(format(DerivedFromSimple(5), 'abc'), '5abc')
self.assertEqual(format(DerivedFromSimple2(10), 'abcdef'),
'10abcdef')
class_test(*classes_new())
class_test(*classes_classic())
def empty_format_spec(value):
# test that:
# format(x, '') == str(x)
# format(x) == str(x)
self.assertEqual(format(value, ""), str(value))
self.assertEqual(format(value), str(value))
# for builtin types, format(x, "") == str(x)
empty_format_spec(17**13)
empty_format_spec(1.0)
empty_format_spec(3.1415e104)
empty_format_spec(-3.1415e104)
empty_format_spec(3.1415e-104)
empty_format_spec(-3.1415e-104)
empty_format_spec(object)
empty_format_spec(None)
# TypeError because self.__format__ returns the wrong type
class BadFormatResult:
def __format__(self, format_spec):
return 1.0
self.assertRaises(TypeError, format, BadFormatResult(), "")
# TypeError because format_spec is not unicode or str
self.assertRaises(TypeError, format, object(), 4)
self.assertRaises(TypeError, format, object(), object())
# tests for object.__format__ really belong elsewhere, but
# there's no good place to put them
x = object().__format__('')
self.assert_(x.startswith('<object object at'))
# first argument to object.__format__ must be string
self.assertRaises(TypeError, object().__format__, 3)
self.assertRaises(TypeError, object().__format__, object())
self.assertRaises(TypeError, object().__format__, None)
# make sure we can take a subclass of str as a format spec
class DerivedFromStr(str): pass
self.assertEqual(format(0, DerivedFromStr('10')), ' 0')
def test_main(verbose=None):
test_classes = (BuiltinTest, TestSorted)

View File

@ -854,6 +854,32 @@ class TestDate(HarmlessMixedComparison, unittest.TestCase):
# A naive object replaces %z and %Z w/ empty strings.
self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
def test_format(self):
dt = self.theclass(2007, 9, 10)
self.assertEqual(dt.__format__(''), str(dt))
# check that a derived class's __str__() gets called
class A(self.theclass):
def __str__(self):
return 'A'
a = A(2007, 9, 10)
self.assertEqual(a.__format__(''), 'A')
# check that a derived class's strftime gets called
class B(self.theclass):
def strftime(self, format_spec):
return 'B'
b = B(2007, 9, 10)
self.assertEqual(b.__format__(''), str(dt))
for fmt in ["m:%m d:%d y:%y",
"m:%m d:%d y:%y H:%H M:%M S:%S",
"%z %Z",
]:
self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
self.assertEqual(b.__format__(fmt), 'B')
def test_resolution_info(self):
self.assert_(isinstance(self.theclass.min, self.theclass))
self.assert_(isinstance(self.theclass.max, self.theclass))
@ -1136,6 +1162,32 @@ class TestDateTime(TestDate):
# str is ISO format with the separator forced to a blank.
self.assertEqual(str(t), "0002-03-02 00:00:00")
def test_format(self):
dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
self.assertEqual(dt.__format__(''), str(dt))
# check that a derived class's __str__() gets called
class A(self.theclass):
def __str__(self):
return 'A'
a = A(2007, 9, 10, 4, 5, 1, 123)
self.assertEqual(a.__format__(''), 'A')
# check that a derived class's strftime gets called
class B(self.theclass):
def strftime(self, format_spec):
return 'B'
b = B(2007, 9, 10, 4, 5, 1, 123)
self.assertEqual(b.__format__(''), str(dt))
for fmt in ["m:%m d:%d y:%y",
"m:%m d:%d y:%y H:%H M:%M S:%S",
"%z %Z",
]:
self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
self.assertEqual(b.__format__(fmt), 'B')
def test_more_ctime(self):
# Test fields that TestDate doesn't touch.
import time
@ -1767,6 +1819,30 @@ class TestTime(HarmlessMixedComparison, unittest.TestCase):
# A naive object replaces %z and %Z with empty strings.
self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
def test_format(self):
t = self.theclass(1, 2, 3, 4)
self.assertEqual(t.__format__(''), str(t))
# check that a derived class's __str__() gets called
class A(self.theclass):
def __str__(self):
return 'A'
a = A(1, 2, 3, 4)
self.assertEqual(a.__format__(''), 'A')
# check that a derived class's strftime gets called
class B(self.theclass):
def strftime(self, format_spec):
return 'B'
b = B(1, 2, 3, 4)
self.assertEqual(b.__format__(''), str(t))
for fmt in ['%H %M %S',
]:
self.assertEqual(t.__format__(fmt), t.strftime(fmt))
self.assertEqual(a.__format__(fmt), t.strftime(fmt))
self.assertEqual(b.__format__(fmt), 'B')
def test_str(self):
self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")

View File

@ -183,6 +183,7 @@ Instead, you can get the same information from the list type:
'__delslice__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__getitem__',

View File

@ -93,6 +93,264 @@ class StrTest(
return
self.assertRaises(OverflowError, 't\tt\t'.expandtabs, sys.maxint)
def test__format__(self):
def test(value, format, expected):
# test both with and without the trailing 's'
self.assertEqual(value.__format__(format), expected)
self.assertEqual(value.__format__(format + 's'), expected)
test('', '', '')
test('abc', '', 'abc')
test('abc', '.3', 'abc')
test('ab', '.3', 'ab')
test('abcdef', '.3', 'abc')
test('abcdef', '.0', '')
test('abc', '3.3', 'abc')
test('abc', '2.3', 'abc')
test('abc', '2.2', 'ab')
test('abc', '3.2', 'ab ')
test('result', 'x<0', 'result')
test('result', 'x<5', 'result')
test('result', 'x<6', 'result')
test('result', 'x<7', 'resultx')
test('result', 'x<8', 'resultxx')
test('result', ' <7', 'result ')
test('result', '<7', 'result ')
test('result', '>7', ' result')
test('result', '>8', ' result')
test('result', '^8', ' result ')
test('result', '^9', ' result ')
test('result', '^10', ' result ')
test('a', '10000', 'a' + ' ' * 9999)
test('', '10000', ' ' * 10000)
test('', '10000000', ' ' * 10000000)
def test_format(self):
self.assertEqual(''.format(), '')
self.assertEqual('a'.format(), 'a')
self.assertEqual('ab'.format(), 'ab')
self.assertEqual('a{{'.format(), 'a{')
self.assertEqual('a}}'.format(), 'a}')
self.assertEqual('{{b'.format(), '{b')
self.assertEqual('}}b'.format(), '}b')
self.assertEqual('a{{b'.format(), 'a{b')
# examples from the PEP:
import datetime
self.assertEqual("My name is {0}".format('Fred'), "My name is Fred")
self.assertEqual("My name is {0[name]}".format(dict(name='Fred')),
"My name is Fred")
self.assertEqual("My name is {0} :-{{}}".format('Fred'),
"My name is Fred :-{}")
d = datetime.date(2007, 8, 18)
self.assertEqual("The year is {0.year}".format(d),
"The year is 2007")
# classes we'll use for testing
class C:
def __init__(self, x=100):
self._x = x
def __format__(self, spec):
return spec
class D:
def __init__(self, x):
self.x = x
def __format__(self, spec):
return str(self.x)
# class with __str__, but no __format__
class E:
def __init__(self, x):
self.x = x
def __str__(self):
return 'E(' + self.x + ')'
# class with __repr__, but no __format__ or __str__
class F:
def __init__(self, x):
self.x = x
def __repr__(self):
return 'F(' + self.x + ')'
# class with __format__ that forwards to string, for some format_spec's
class G:
def __init__(self, x):
self.x = x
def __str__(self):
return "string is " + self.x
def __format__(self, format_spec):
if format_spec == 'd':
return 'G(' + self.x + ')'
return object.__format__(self, format_spec)
# class that returns a bad type from __format__
class H:
def __format__(self, format_spec):
return 1.0
class I(datetime.date):
def __format__(self, format_spec):
return self.strftime(format_spec)
class J(int):
def __format__(self, format_spec):
return int.__format__(self * 2, format_spec)
self.assertEqual(''.format(), '')
self.assertEqual('abc'.format(), 'abc')
self.assertEqual('{0}'.format('abc'), 'abc')
self.assertEqual('{0:}'.format('abc'), 'abc')
self.assertEqual('X{0}'.format('abc'), 'Xabc')
self.assertEqual('{0}X'.format('abc'), 'abcX')
self.assertEqual('X{0}Y'.format('abc'), 'XabcY')
self.assertEqual('{1}'.format(1, 'abc'), 'abc')
self.assertEqual('X{1}'.format(1, 'abc'), 'Xabc')
self.assertEqual('{1}X'.format(1, 'abc'), 'abcX')
self.assertEqual('X{1}Y'.format(1, 'abc'), 'XabcY')
self.assertEqual('{0}'.format(-15), '-15')
self.assertEqual('{0}{1}'.format(-15, 'abc'), '-15abc')
self.assertEqual('{0}X{1}'.format(-15, 'abc'), '-15Xabc')
self.assertEqual('{{'.format(), '{')
self.assertEqual('}}'.format(), '}')
self.assertEqual('{{}}'.format(), '{}')
self.assertEqual('{{x}}'.format(), '{x}')
self.assertEqual('{{{0}}}'.format(123), '{123}')
self.assertEqual('{{{{0}}}}'.format(), '{{0}}')
self.assertEqual('}}{{'.format(), '}{')
self.assertEqual('}}x{{'.format(), '}x{')
# weird field names
self.assertEqual("{0[foo-bar]}".format({'foo-bar':'baz'}), 'baz')
self.assertEqual("{0[foo bar]}".format({'foo bar':'baz'}), 'baz')
self.assertEqual("{0[ ]}".format({' ':3}), '3')
self.assertEqual('{foo._x}'.format(foo=C(20)), '20')
self.assertEqual('{1}{0}'.format(D(10), D(20)), '2010')
self.assertEqual('{0._x.x}'.format(C(D('abc'))), 'abc')
self.assertEqual('{0[0]}'.format(['abc', 'def']), 'abc')
self.assertEqual('{0[1]}'.format(['abc', 'def']), 'def')
self.assertEqual('{0[1][0]}'.format(['abc', ['def']]), 'def')
self.assertEqual('{0[1][0].x}'.format(['abc', [D('def')]]), 'def')
# strings
self.assertEqual('{0:.3s}'.format('abc'), 'abc')
self.assertEqual('{0:.3s}'.format('ab'), 'ab')
self.assertEqual('{0:.3s}'.format('abcdef'), 'abc')
self.assertEqual('{0:.0s}'.format('abcdef'), '')
self.assertEqual('{0:3.3s}'.format('abc'), 'abc')
self.assertEqual('{0:2.3s}'.format('abc'), 'abc')
self.assertEqual('{0:2.2s}'.format('abc'), 'ab')
self.assertEqual('{0:3.2s}'.format('abc'), 'ab ')
self.assertEqual('{0:x<0s}'.format('result'), 'result')
self.assertEqual('{0:x<5s}'.format('result'), 'result')
self.assertEqual('{0:x<6s}'.format('result'), 'result')
self.assertEqual('{0:x<7s}'.format('result'), 'resultx')
self.assertEqual('{0:x<8s}'.format('result'), 'resultxx')
self.assertEqual('{0: <7s}'.format('result'), 'result ')
self.assertEqual('{0:<7s}'.format('result'), 'result ')
self.assertEqual('{0:>7s}'.format('result'), ' result')
self.assertEqual('{0:>8s}'.format('result'), ' result')
self.assertEqual('{0:^8s}'.format('result'), ' result ')
self.assertEqual('{0:^9s}'.format('result'), ' result ')
self.assertEqual('{0:^10s}'.format('result'), ' result ')
self.assertEqual('{0:10000}'.format('a'), 'a' + ' ' * 9999)
self.assertEqual('{0:10000}'.format(''), ' ' * 10000)
self.assertEqual('{0:10000000}'.format(''), ' ' * 10000000)
# format specifiers for user defined type
self.assertEqual('{0:abc}'.format(C()), 'abc')
# !r and !s coersions
self.assertEqual('{0!s}'.format('Hello'), 'Hello')
self.assertEqual('{0!s:}'.format('Hello'), 'Hello')
self.assertEqual('{0!s:15}'.format('Hello'), 'Hello ')
self.assertEqual('{0!s:15s}'.format('Hello'), 'Hello ')
self.assertEqual('{0!r}'.format('Hello'), "'Hello'")
self.assertEqual('{0!r:}'.format('Hello'), "'Hello'")
self.assertEqual('{0!r}'.format(F('Hello')), 'F(Hello)')
# test fallback to object.__format__
self.assertEqual('{0}'.format({}), '{}')
self.assertEqual('{0}'.format([]), '[]')
self.assertEqual('{0}'.format([1]), '[1]')
self.assertEqual('{0}'.format(E('data')), 'E(data)')
self.assertEqual('{0:^10}'.format(E('data')), ' E(data) ')
self.assertEqual('{0:^10s}'.format(E('data')), ' E(data) ')
self.assertEqual('{0:d}'.format(G('data')), 'G(data)')
self.assertEqual('{0:>15s}'.format(G('data')), ' string is data')
self.assertEqual('{0!s}'.format(G('data')), 'string is data')
self.assertEqual("{0:date: %Y-%m-%d}".format(I(year=2007,
month=8,
day=27)),
"date: 2007-08-27")
# test deriving from a builtin type and overriding __format__
self.assertEqual("{0}".format(J(10)), "20")
# string format specifiers
self.assertEqual('{0:}'.format('a'), 'a')
# computed format specifiers
self.assertEqual("{0:.{1}}".format('hello world', 5), 'hello')
self.assertEqual("{0:.{1}s}".format('hello world', 5), 'hello')
self.assertEqual("{0:.{precision}s}".format('hello world', precision=5), 'hello')
self.assertEqual("{0:{width}.{precision}s}".format('hello world', width=10, precision=5), 'hello ')
self.assertEqual("{0:{width}.{precision}s}".format('hello world', width='10', precision='5'), 'hello ')
# test various errors
self.assertRaises(ValueError, '{'.format)
self.assertRaises(ValueError, '}'.format)
self.assertRaises(ValueError, 'a{'.format)
self.assertRaises(ValueError, 'a}'.format)
self.assertRaises(ValueError, '{a'.format)
self.assertRaises(ValueError, '}a'.format)
self.assertRaises(IndexError, '{0}'.format)
self.assertRaises(IndexError, '{1}'.format, 'abc')
self.assertRaises(KeyError, '{x}'.format)
self.assertRaises(ValueError, "}{".format)
self.assertRaises(ValueError, "{".format)
self.assertRaises(ValueError, "}".format)
self.assertRaises(ValueError, "abc{0:{}".format)
self.assertRaises(ValueError, "{0".format)
self.assertRaises(IndexError, "{0.}".format)
self.assertRaises(ValueError, "{0.}".format, 0)
self.assertRaises(IndexError, "{0[}".format)
self.assertRaises(ValueError, "{0[}".format, [])
self.assertRaises(KeyError, "{0]}".format)
self.assertRaises(ValueError, "{0.[]}".format, 0)
self.assertRaises(ValueError, "{0..foo}".format, 0)
self.assertRaises(ValueError, "{0[0}".format, 0)
self.assertRaises(ValueError, "{0[0:foo}".format, 0)
self.assertRaises(KeyError, "{c]}".format)
self.assertRaises(ValueError, "{{ {{{0}}".format, 0)
self.assertRaises(ValueError, "{0}}".format, 0)
self.assertRaises(KeyError, "{foo}".format, bar=3)
self.assertRaises(ValueError, "{0!x}".format, 3)
self.assertRaises(ValueError, "{0!}".format, 0)
self.assertRaises(ValueError, "{0!rs}".format, 0)
self.assertRaises(ValueError, "{!}".format)
self.assertRaises(ValueError, "{:}".format)
self.assertRaises(ValueError, "{:s}".format)
self.assertRaises(ValueError, "{}".format)
# can't have a replacement on the field name portion
self.assertRaises(TypeError, '{0[{1}]}'.format, 'abcdefg', 4)
# exceed maximum recursion depth
self.assertRaises(ValueError, "{0:{1:{2}}}".format, 'abc', 's', '')
self.assertRaises(ValueError, "{0:{1:{2:{3:{4:{5:{6}}}}}}}".format,
0, 1, 2, 3, 4, 5, 6, 7)
# string format spec errors
self.assertRaises(ValueError, "{0:-s}".format, '')
self.assertRaises(ValueError, format, "", "-")
self.assertRaises(ValueError, "{0:=s}".format, '')
def test_main():
test_support.run_unittest(StrTest)

View File

@ -106,6 +106,92 @@ class ModuleTest(unittest.TestCase):
self.assertEqual(string.capwords('ABC-DEF-GHI', '-'), 'Abc-Def-Ghi')
self.assertEqual(string.capwords('ABC-def DEF-ghi GHI'), 'Abc-def Def-ghi Ghi')
def test_formatter(self):
fmt = string.Formatter()
self.assertEqual(fmt.format("foo"), "foo")
self.assertEqual(fmt.format("foo{0}", "bar"), "foobar")
self.assertEqual(fmt.format("foo{1}{0}-{1}", "bar", 6), "foo6bar-6")
self.assertEqual(fmt.format("-{arg!r}-", arg='test'), "-'test'-")
# override get_value ############################################
class NamespaceFormatter(string.Formatter):
def __init__(self, namespace={}):
string.Formatter.__init__(self)
self.namespace = namespace
def get_value(self, key, args, kwds):
if isinstance(key, str):
try:
# Check explicitly passed arguments first
return kwds[key]
except KeyError:
return self.namespace[key]
else:
string.Formatter.get_value(key, args, kwds)
fmt = NamespaceFormatter({'greeting':'hello'})
self.assertEqual(fmt.format("{greeting}, world!"), 'hello, world!')
# override format_field #########################################
class CallFormatter(string.Formatter):
def format_field(self, value, format_spec):
return format(value(), format_spec)
fmt = CallFormatter()
self.assertEqual(fmt.format('*{0}*', lambda : 'result'), '*result*')
# override convert_field ########################################
class XFormatter(string.Formatter):
def convert_field(self, value, conversion):
if conversion == 'x':
return None
return super(XFormatter, self).convert_field(value, conversion)
fmt = XFormatter()
self.assertEqual(fmt.format("{0!r}:{0!x}", 'foo', 'foo'), "'foo':None")
# override parse ################################################
class BarFormatter(string.Formatter):
# returns an iterable that contains tuples of the form:
# (literal_text, field_name, format_spec, conversion)
def parse(self, format_string):
for field in format_string.split('|'):
if field[0] == '+':
# it's markup
field_name, _, format_spec = field[1:].partition(':')
yield '', field_name, format_spec, None
else:
yield field, None, None, None
fmt = BarFormatter()
self.assertEqual(fmt.format('*|+0:^10s|*', 'foo'), '* foo *')
# test all parameters used
class CheckAllUsedFormatter(string.Formatter):
def check_unused_args(self, used_args, args, kwargs):
# Track which arguments actuallly got used
unused_args = set(kwargs.keys())
unused_args.update(range(0, len(args)))
for arg in used_args:
unused_args.remove(arg)
if unused_args:
raise ValueError("unused arguments")
fmt = CheckAllUsedFormatter()
self.assertEqual(fmt.format("{0}", 10), "10")
self.assertEqual(fmt.format("{0}{i}", 10, i=100), "10100")
self.assertEqual(fmt.format("{0}{i}{1}", 10, 20, i=100), "1010020")
self.assertRaises(ValueError, fmt.format, "{0}{i}{1}", 10, 20, i=100, j=0)
self.assertRaises(ValueError, fmt.format, "{0}", 10, 20)
self.assertRaises(ValueError, fmt.format, "{0}", 10, 20, i=100)
self.assertRaises(ValueError, fmt.format, "{i}", 10, 20, i=100)
class BytesAliasTest(unittest.TestCase):
def test_builtin(self):

View File

@ -266,6 +266,257 @@ class TypesTests(unittest.TestCase):
except TypeError: pass
else: self.fail("char buffer (at C level) not working")
def test_int__format__(self):
def test(i, format_spec, result):
# just make sure I'm not accidentally checking longs
assert type(i) == int
assert type(format_spec) == str
self.assertEqual(i.__format__(format_spec), result)
self.assertEqual(i.__format__(unicode(format_spec)), result)
test(123456789, 'd', '123456789')
test(123456789, 'd', '123456789')
test(1, 'c', '\01')
# sign and aligning are interdependent
test(1, "-", '1')
test(-1, "-", '-1')
test(1, "-3", ' 1')
test(-1, "-3", ' -1')
test(1, "+3", ' +1')
test(-1, "+3", ' -1')
test(1, " 3", ' 1')
test(-1, " 3", ' -1')
test(1, " ", ' 1')
test(-1, " ", '-1')
# hex
test(3, "x", "3")
test(3, "X", "3")
test(1234, "x", "4d2")
test(-1234, "x", "-4d2")
test(1234, "8x", " 4d2")
test(-1234, "8x", " -4d2")
test(1234, "x", "4d2")
test(-1234, "x", "-4d2")
test(-3, "x", "-3")
test(-3, "X", "-3")
test(int('be', 16), "x", "be")
test(int('be', 16), "X", "BE")
test(-int('be', 16), "x", "-be")
test(-int('be', 16), "X", "-BE")
# octal
test(3, "o", "3")
test(-3, "o", "-3")
test(65, "o", "101")
test(-65, "o", "-101")
test(1234, "o", "2322")
test(-1234, "o", "-2322")
test(1234, "-o", "2322")
test(-1234, "-o", "-2322")
test(1234, " o", " 2322")
test(-1234, " o", "-2322")
test(1234, "+o", "+2322")
test(-1234, "+o", "-2322")
# binary
test(3, "b", "11")
test(-3, "b", "-11")
test(1234, "b", "10011010010")
test(-1234, "b", "-10011010010")
test(1234, "-b", "10011010010")
test(-1234, "-b", "-10011010010")
test(1234, " b", " 10011010010")
test(-1234, " b", "-10011010010")
test(1234, "+b", "+10011010010")
test(-1234, "+b", "-10011010010")
# make sure these are errors
# precision disallowed
self.assertRaises(ValueError, 3 .__format__, "1.3")
# sign not allowed with 'c'
self.assertRaises(ValueError, 3 .__format__, "+c")
# format spec must be string
self.assertRaises(TypeError, 3 .__format__, None)
self.assertRaises(TypeError, 3 .__format__, 0)
# ensure that only int and float type specifiers work
for format_spec in ([chr(x) for x in range(ord('a'), ord('z')+1)] +
[chr(x) for x in range(ord('A'), ord('Z')+1)]):
if not format_spec in 'bcdoxXeEfFgGn%':
self.assertRaises(ValueError, 0 .__format__, format_spec)
self.assertRaises(ValueError, 1 .__format__, format_spec)
self.assertRaises(ValueError, (-1) .__format__, format_spec)
# ensure that float type specifiers work; format converts
# the int to a float
for format_spec in 'eEfFgGn%':
for value in [0, 1, -1, 100, -100, 1234567890, -1234567890]:
self.assertEqual(value.__format__(format_spec),
float(value).__format__(format_spec))
def test_long__format__(self):
def test(i, format_spec, result):
# make sure we're not accidentally checking ints
assert type(i) == long
assert type(format_spec) == str
self.assertEqual(i.__format__(format_spec), result)
self.assertEqual(i.__format__(unicode(format_spec)), result)
test(10**100, 'd', '1' + '0' * 100)
test(10**100+100, 'd', '1' + '0' * 97 + '100')
test(123456789L, 'd', '123456789')
test(123456789L, 'd', '123456789')
# sign and aligning are interdependent
test(1L, "-", '1')
test(-1L, "-", '-1')
test(1L, "-3", ' 1')
test(-1L, "-3", ' -1')
test(1L, "+3", ' +1')
test(-1L, "+3", ' -1')
test(1L, " 3", ' 1')
test(-1L, " 3", ' -1')
test(1L, " ", ' 1')
test(-1L, " ", '-1')
test(1L, 'c', '\01')
# hex
test(3L, "x", "3")
test(3L, "X", "3")
test(1234L, "x", "4d2")
test(-1234L, "x", "-4d2")
test(1234L, "8x", " 4d2")
test(-1234L, "8x", " -4d2")
test(1234L, "x", "4d2")
test(-1234L, "x", "-4d2")
test(-3L, "x", "-3")
test(-3L, "X", "-3")
test(long('be', 16), "x", "be")
test(long('be', 16), "X", "BE")
test(-long('be', 16), "x", "-be")
test(-long('be', 16), "X", "-BE")
# octal
test(3L, "o", "3")
test(-3L, "o", "-3")
test(65L, "o", "101")
test(-65L, "o", "-101")
test(1234L, "o", "2322")
test(-1234L, "o", "-2322")
test(1234L, "-o", "2322")
test(-1234L, "-o", "-2322")
test(1234L, " o", " 2322")
test(-1234L, " o", "-2322")
test(1234L, "+o", "+2322")
test(-1234L, "+o", "-2322")
# binary
test(3L, "b", "11")
test(-3L, "b", "-11")
test(1234L, "b", "10011010010")
test(-1234L, "b", "-10011010010")
test(1234L, "-b", "10011010010")
test(-1234L, "-b", "-10011010010")
test(1234L, " b", " 10011010010")
test(-1234L, " b", "-10011010010")
test(1234L, "+b", "+10011010010")
test(-1234L, "+b", "-10011010010")
# make sure these are errors
# precision disallowed
self.assertRaises(ValueError, 3L .__format__, "1.3")
# sign not allowed with 'c'
self.assertRaises(ValueError, 3L .__format__, "+c")
# format spec must be string
self.assertRaises(TypeError, 3L .__format__, None)
self.assertRaises(TypeError, 3L .__format__, 0)
# ensure that only int and float type specifiers work
for format_spec in ([chr(x) for x in range(ord('a'), ord('z')+1)] +
[chr(x) for x in range(ord('A'), ord('Z')+1)]):
if not format_spec in 'bcdoxXeEfFgGn%':
self.assertRaises(ValueError, 0L .__format__, format_spec)
self.assertRaises(ValueError, 1L .__format__, format_spec)
self.assertRaises(ValueError, (-1L) .__format__, format_spec)
# ensure that float type specifiers work; format converts
# the long to a float
for format_spec in 'eEfFgGn%':
for value in [0L, 1L, -1L, 100L, -100L, 1234567890L, -1234567890L]:
self.assertEqual(value.__format__(format_spec),
float(value).__format__(format_spec))
def test_float__format__(self):
# these should be rewritten to use both format(x, spec) and
# x.__format__(spec)
def test(f, format_spec, result):
assert type(f) == float
assert type(format_spec) == str
self.assertEqual(f.__format__(format_spec), result)
self.assertEqual(f.__format__(unicode(format_spec)), result)
test(0.0, 'f', '0.000000')
# the default is 'g', except for empty format spec
test(0.0, '', '0.0')
test(0.01, '', '0.01')
test(0.01, 'g', '0.01')
test( 1.0, ' g', ' 1')
test(-1.0, ' g', '-1')
test( 1.0, '+g', '+1')
test(-1.0, '+g', '-1')
test(1.1234e200, 'g', '1.1234e+200')
test(1.1234e200, 'G', '1.1234E+200')
test(1.0, 'f', '1.000000')
test(-1.0, 'f', '-1.000000')
test( 1.0, ' f', ' 1.000000')
test(-1.0, ' f', '-1.000000')
test( 1.0, '+f', '+1.000000')
test(-1.0, '+f', '-1.000000')
test(1.1234e200, 'f', '1.1234e+200')
test(1.1234e200, 'F', '1.1234e+200')
test( 1.0, 'e', '1.000000e+00')
test(-1.0, 'e', '-1.000000e+00')
test( 1.0, 'E', '1.000000E+00')
test(-1.0, 'E', '-1.000000E+00')
test(1.1234e20, 'e', '1.123400e+20')
test(1.1234e20, 'E', '1.123400E+20')
# % formatting
test(-1.0, '%', '-100.000000%')
# format spec must be string
self.assertRaises(TypeError, 3.0.__format__, None)
self.assertRaises(TypeError, 3.0.__format__, 0)
# other format specifiers shouldn't work on floats,
# in particular int specifiers
for format_spec in ([chr(x) for x in range(ord('a'), ord('z')+1)] +
[chr(x) for x in range(ord('A'), ord('Z')+1)]):
if not format_spec in 'eEfFgGn%':
self.assertRaises(ValueError, format, 0.0, format_spec)
self.assertRaises(ValueError, format, 1.0, format_spec)
self.assertRaises(ValueError, format, -1.0, format_spec)
self.assertRaises(ValueError, format, 1e100, format_spec)
self.assertRaises(ValueError, format, -1e100, format_spec)
self.assertRaises(ValueError, format, 1e-100, format_spec)
self.assertRaises(ValueError, format, -1e-100, format_spec)
def test_main():
run_unittest(TypesTests)

View File

@ -825,6 +825,268 @@ class UnicodeTest(
return
self.assertRaises(OverflowError, u't\tt\t'.expandtabs, sys.maxint)
def test__format__(self):
def test(value, format, expected):
# test both with and without the trailing 's'
self.assertEqual(value.__format__(format), expected)
self.assertEqual(value.__format__(format + u's'), expected)
test(u'', u'', u'')
test(u'abc', u'', u'abc')
test(u'abc', u'.3', u'abc')
test(u'ab', u'.3', u'ab')
test(u'abcdef', u'.3', u'abc')
test(u'abcdef', u'.0', u'')
test(u'abc', u'3.3', u'abc')
test(u'abc', u'2.3', u'abc')
test(u'abc', u'2.2', u'ab')
test(u'abc', u'3.2', u'ab ')
test(u'result', u'x<0', u'result')
test(u'result', u'x<5', u'result')
test(u'result', u'x<6', u'result')
test(u'result', u'x<7', u'resultx')
test(u'result', u'x<8', u'resultxx')
test(u'result', u' <7', u'result ')
test(u'result', u'<7', u'result ')
test(u'result', u'>7', u' result')
test(u'result', u'>8', u' result')
test(u'result', u'^8', u' result ')
test(u'result', u'^9', u' result ')
test(u'result', u'^10', u' result ')
test(u'a', u'10000', u'a' + u' ' * 9999)
test(u'', u'10000', u' ' * 10000)
test(u'', u'10000000', u' ' * 10000000)
# test mixing unicode and str
self.assertEqual(u'abc'.__format__('s'), u'abc')
self.assertEqual(u'abc'.__format__('->10s'), u'-------abc')
def test_format(self):
self.assertEqual(u''.format(), u'')
self.assertEqual(u'a'.format(), u'a')
self.assertEqual(u'ab'.format(), u'ab')
self.assertEqual(u'a{{'.format(), u'a{')
self.assertEqual(u'a}}'.format(), u'a}')
self.assertEqual(u'{{b'.format(), u'{b')
self.assertEqual(u'}}b'.format(), u'}b')
self.assertEqual(u'a{{b'.format(), u'a{b')
# examples from the PEP:
import datetime
self.assertEqual(u"My name is {0}".format(u'Fred'), u"My name is Fred")
self.assertEqual(u"My name is {0[name]}".format(dict(name=u'Fred')),
u"My name is Fred")
self.assertEqual(u"My name is {0} :-{{}}".format(u'Fred'),
u"My name is Fred :-{}")
# datetime.__format__ doesn't work with unicode
#d = datetime.date(2007, 8, 18)
#self.assertEqual("The year is {0.year}".format(d),
# "The year is 2007")
# classes we'll use for testing
class C:
def __init__(self, x=100):
self._x = x
def __format__(self, spec):
return spec
class D:
def __init__(self, x):
self.x = x
def __format__(self, spec):
return str(self.x)
# class with __str__, but no __format__
class E:
def __init__(self, x):
self.x = x
def __str__(self):
return u'E(' + self.x + u')'
# class with __repr__, but no __format__ or __str__
class F:
def __init__(self, x):
self.x = x
def __repr__(self):
return u'F(' + self.x + u')'
# class with __format__ that forwards to string, for some format_spec's
class G:
def __init__(self, x):
self.x = x
def __str__(self):
return u"string is " + self.x
def __format__(self, format_spec):
if format_spec == 'd':
return u'G(' + self.x + u')'
return object.__format__(self, format_spec)
# class that returns a bad type from __format__
class H:
def __format__(self, format_spec):
return 1.0
class I(datetime.date):
def __format__(self, format_spec):
return self.strftime(format_spec)
class J(int):
def __format__(self, format_spec):
return int.__format__(self * 2, format_spec)
self.assertEqual(u''.format(), u'')
self.assertEqual(u'abc'.format(), u'abc')
self.assertEqual(u'{0}'.format(u'abc'), u'abc')
self.assertEqual(u'{0:}'.format(u'abc'), u'abc')
self.assertEqual(u'X{0}'.format(u'abc'), u'Xabc')
self.assertEqual(u'{0}X'.format(u'abc'), u'abcX')
self.assertEqual(u'X{0}Y'.format(u'abc'), u'XabcY')
self.assertEqual(u'{1}'.format(1, u'abc'), u'abc')
self.assertEqual(u'X{1}'.format(1, u'abc'), u'Xabc')
self.assertEqual(u'{1}X'.format(1, u'abc'), u'abcX')
self.assertEqual(u'X{1}Y'.format(1, u'abc'), u'XabcY')
self.assertEqual(u'{0}'.format(-15), u'-15')
self.assertEqual(u'{0}{1}'.format(-15, u'abc'), u'-15abc')
self.assertEqual(u'{0}X{1}'.format(-15, u'abc'), u'-15Xabc')
self.assertEqual(u'{{'.format(), u'{')
self.assertEqual(u'}}'.format(), u'}')
self.assertEqual(u'{{}}'.format(), u'{}')
self.assertEqual(u'{{x}}'.format(), u'{x}')
self.assertEqual(u'{{{0}}}'.format(123), u'{123}')
self.assertEqual(u'{{{{0}}}}'.format(), u'{{0}}')
self.assertEqual(u'}}{{'.format(), u'}{')
self.assertEqual(u'}}x{{'.format(), u'}x{')
# weird field names
self.assertEqual(u"{0[foo-bar]}".format({u'foo-bar':u'baz'}), u'baz')
self.assertEqual(u"{0[foo bar]}".format({u'foo bar':u'baz'}), u'baz')
self.assertEqual(u"{0[ ]}".format({u' ':3}), u'3')
self.assertEqual(u'{foo._x}'.format(foo=C(20)), u'20')
self.assertEqual(u'{1}{0}'.format(D(10), D(20)), u'2010')
self.assertEqual(u'{0._x.x}'.format(C(D(u'abc'))), u'abc')
self.assertEqual(u'{0[0]}'.format([u'abc', u'def']), u'abc')
self.assertEqual(u'{0[1]}'.format([u'abc', u'def']), u'def')
self.assertEqual(u'{0[1][0]}'.format([u'abc', [u'def']]), u'def')
self.assertEqual(u'{0[1][0].x}'.format(['abc', [D(u'def')]]), u'def')
# strings
self.assertEqual(u'{0:.3s}'.format(u'abc'), u'abc')
self.assertEqual(u'{0:.3s}'.format(u'ab'), u'ab')
self.assertEqual(u'{0:.3s}'.format(u'abcdef'), u'abc')
self.assertEqual(u'{0:.0s}'.format(u'abcdef'), u'')
self.assertEqual(u'{0:3.3s}'.format(u'abc'), u'abc')
self.assertEqual(u'{0:2.3s}'.format(u'abc'), u'abc')
self.assertEqual(u'{0:2.2s}'.format(u'abc'), u'ab')
self.assertEqual(u'{0:3.2s}'.format(u'abc'), u'ab ')
self.assertEqual(u'{0:x<0s}'.format(u'result'), u'result')
self.assertEqual(u'{0:x<5s}'.format(u'result'), u'result')
self.assertEqual(u'{0:x<6s}'.format(u'result'), u'result')
self.assertEqual(u'{0:x<7s}'.format(u'result'), u'resultx')
self.assertEqual(u'{0:x<8s}'.format(u'result'), u'resultxx')
self.assertEqual(u'{0: <7s}'.format(u'result'), u'result ')
self.assertEqual(u'{0:<7s}'.format(u'result'), u'result ')
self.assertEqual(u'{0:>7s}'.format(u'result'), u' result')
self.assertEqual(u'{0:>8s}'.format(u'result'), u' result')
self.assertEqual(u'{0:^8s}'.format(u'result'), u' result ')
self.assertEqual(u'{0:^9s}'.format(u'result'), u' result ')
self.assertEqual(u'{0:^10s}'.format(u'result'), u' result ')
self.assertEqual(u'{0:10000}'.format(u'a'), u'a' + u' ' * 9999)
self.assertEqual(u'{0:10000}'.format(u''), u' ' * 10000)
self.assertEqual(u'{0:10000000}'.format(u''), u' ' * 10000000)
# format specifiers for user defined type
self.assertEqual(u'{0:abc}'.format(C()), u'abc')
# !r and !s coersions
self.assertEqual(u'{0!s}'.format(u'Hello'), u'Hello')
self.assertEqual(u'{0!s:}'.format(u'Hello'), u'Hello')
self.assertEqual(u'{0!s:15}'.format(u'Hello'), u'Hello ')
self.assertEqual(u'{0!s:15s}'.format(u'Hello'), u'Hello ')
self.assertEqual(u'{0!r}'.format(u'Hello'), u"u'Hello'")
self.assertEqual(u'{0!r:}'.format(u'Hello'), u"u'Hello'")
self.assertEqual(u'{0!r}'.format(F(u'Hello')), u'F(Hello)')
# test fallback to object.__format__
self.assertEqual(u'{0}'.format({}), u'{}')
self.assertEqual(u'{0}'.format([]), u'[]')
self.assertEqual(u'{0}'.format([1]), u'[1]')
self.assertEqual(u'{0}'.format(E(u'data')), u'E(data)')
self.assertEqual(u'{0:^10}'.format(E(u'data')), u' E(data) ')
self.assertEqual(u'{0:^10s}'.format(E(u'data')), u' E(data) ')
self.assertEqual(u'{0:d}'.format(G(u'data')), u'G(data)')
self.assertEqual(u'{0:>15s}'.format(G(u'data')), u' string is data')
self.assertEqual(u'{0!s}'.format(G(u'data')), u'string is data')
self.assertEqual("{0:date: %Y-%m-%d}".format(I(year=2007,
month=8,
day=27)),
"date: 2007-08-27")
# test deriving from a builtin type and overriding __format__
self.assertEqual("{0}".format(J(10)), "20")
# string format specifiers
self.assertEqual('{0:}'.format('a'), 'a')
# computed format specifiers
self.assertEqual("{0:.{1}}".format('hello world', 5), 'hello')
self.assertEqual("{0:.{1}s}".format('hello world', 5), 'hello')
self.assertEqual("{0:.{precision}s}".format('hello world', precision=5), 'hello')
self.assertEqual("{0:{width}.{precision}s}".format('hello world', width=10, precision=5), 'hello ')
self.assertEqual("{0:{width}.{precision}s}".format('hello world', width='10', precision='5'), 'hello ')
# test various errors
self.assertRaises(ValueError, '{'.format)
self.assertRaises(ValueError, '}'.format)
self.assertRaises(ValueError, 'a{'.format)
self.assertRaises(ValueError, 'a}'.format)
self.assertRaises(ValueError, '{a'.format)
self.assertRaises(ValueError, '}a'.format)
self.assertRaises(IndexError, '{0}'.format)
self.assertRaises(IndexError, '{1}'.format, 'abc')
self.assertRaises(KeyError, '{x}'.format)
self.assertRaises(ValueError, "}{".format)
self.assertRaises(ValueError, "{".format)
self.assertRaises(ValueError, "}".format)
self.assertRaises(ValueError, "abc{0:{}".format)
self.assertRaises(ValueError, "{0".format)
self.assertRaises(IndexError, "{0.}".format)
self.assertRaises(ValueError, "{0.}".format, 0)
self.assertRaises(IndexError, "{0[}".format)
self.assertRaises(ValueError, "{0[}".format, [])
self.assertRaises(KeyError, "{0]}".format)
self.assertRaises(ValueError, "{0.[]}".format, 0)
self.assertRaises(ValueError, "{0..foo}".format, 0)
self.assertRaises(ValueError, "{0[0}".format, 0)
self.assertRaises(ValueError, "{0[0:foo}".format, 0)
self.assertRaises(KeyError, "{c]}".format)
self.assertRaises(ValueError, "{{ {{{0}}".format, 0)
self.assertRaises(ValueError, "{0}}".format, 0)
self.assertRaises(KeyError, "{foo}".format, bar=3)
self.assertRaises(ValueError, "{0!x}".format, 3)
self.assertRaises(ValueError, "{0!}".format, 0)
self.assertRaises(ValueError, "{0!rs}".format, 0)
self.assertRaises(ValueError, "{!}".format)
self.assertRaises(ValueError, "{:}".format)
self.assertRaises(ValueError, "{:s}".format)
self.assertRaises(ValueError, "{}".format)
# can't have a replacement on the field name portion
self.assertRaises(TypeError, '{0[{1}]}'.format, 'abcdefg', 4)
# exceed maximum recursion depth
self.assertRaises(ValueError, "{0:{1:{2}}}".format, 'abc', 's', '')
self.assertRaises(ValueError, "{0:{1:{2:{3:{4:{5:{6}}}}}}}".format,
0, 1, 2, 3, 4, 5, 6, 7)
# string format spec errors
self.assertRaises(ValueError, "{0:-s}".format, '')
self.assertRaises(ValueError, format, "", "-")
self.assertRaises(ValueError, "{0:=s}".format, '')
def test_main():
test_support.run_unittest(__name__)

View File

@ -281,6 +281,8 @@ PYTHON_OBJS= \
Python/getopt.o \
Python/pystrcmp.o \
Python/pystrtod.o \
Python/formatter_unicode.o \
Python/formatter_string.o \
Python/$(DYNLOADFILE) \
$(LIBOBJS) \
$(MACHDEP_OBJS) \
@ -515,6 +517,20 @@ Python/importdl.o: $(srcdir)/Python/importdl.c
Objects/unicodectype.o: $(srcdir)/Objects/unicodectype.c \
$(srcdir)/Objects/unicodetype_db.h
Objects/unicodeobject.o: $(srcdir)/Objects/unicodeobject.c \
$(srcdir)/Objects/stringlib/string_format.h \
$(srcdir)/Objects/stringlib/unicodedefs.h \
$(srcdir)/Objects/stringlib/fastsearch.h \
$(srcdir)/Objects/stringlib/count.h \
$(srcdir)/Objects/stringlib/find.h \
$(srcdir)/Objects/stringlib/partition.h
Python/formatter_unicode.o: $(srcdir)/Python/formatter_unicode.c \
$(srcdir)/Objects/stringlib/formatter.h
Python/formatter_string.o: $(srcdir)/Python/formatter_string.c \
$(srcdir)/Objects/stringlib/formatter.h
############################################################################
# Header files

View File

@ -2469,6 +2469,32 @@ date_strftime(PyDateTime_Date *self, PyObject *args, PyObject *kw)
return result;
}
static PyObject *
date_format(PyDateTime_Date *self, PyObject *args)
{
PyObject *format;
if (!PyArg_ParseTuple(args, "O:__format__", &format))
return NULL;
/* Check for str or unicode */
if (PyString_Check(format)) {
/* If format is zero length, return str(self) */
if (PyString_GET_SIZE(format) == 0)
return PyObject_Str((PyObject *)self);
} else if (PyUnicode_Check(format)) {
/* If format is zero length, return str(self) */
if (PyUnicode_GET_SIZE(format) == 0)
return PyObject_Unicode((PyObject *)self);
} else {
PyErr_Format(PyExc_ValueError,
"__format__ expects str or unicode, not %.200s",
Py_TYPE(format)->tp_name);
return NULL;
}
return PyObject_CallMethod((PyObject *)self, "strftime", "O", format);
}
/* ISO methods. */
static PyObject *
@ -2633,6 +2659,9 @@ static PyMethodDef date_methods[] = {
{"strftime", (PyCFunction)date_strftime, METH_VARARGS | METH_KEYWORDS,
PyDoc_STR("format -> strftime() style string.")},
{"__format__", (PyCFunction)date_format, METH_VARARGS,
PyDoc_STR("Formats self with strftime.")},
{"timetuple", (PyCFunction)date_timetuple, METH_NOARGS,
PyDoc_STR("Return time tuple, compatible with time.localtime().")},
@ -3418,6 +3447,9 @@ static PyMethodDef time_methods[] = {
{"strftime", (PyCFunction)time_strftime, METH_VARARGS | METH_KEYWORDS,
PyDoc_STR("format -> strftime() style string.")},
{"__format__", (PyCFunction)date_format, METH_VARARGS,
PyDoc_STR("Formats self with strftime.")},
{"utcoffset", (PyCFunction)time_utcoffset, METH_NOARGS,
PyDoc_STR("Return self.tzinfo.utcoffset(self).")},

View File

@ -348,6 +348,138 @@ int PyObject_AsWriteBuffer(PyObject *obj,
return 0;
}
PyObject *
PyObject_Format(PyObject* obj, PyObject *format_spec)
{
static PyObject * str__format__ = NULL;
PyObject *empty = NULL;
PyObject *result = NULL;
int spec_is_unicode;
int result_is_unicode;
/* Initialize cached value */
if (str__format__ == NULL) {
/* Initialize static variable needed by _PyType_Lookup */
str__format__ = PyString_InternFromString("__format__");
if (str__format__ == NULL)
goto done;
}
/* If no format_spec is provided, use an empty string */
if (format_spec == NULL) {
empty = PyString_FromStringAndSize(NULL, 0);
format_spec = empty;
}
/* Check the format_spec type, and make sure it's str or unicode */
if (PyUnicode_Check(format_spec))
spec_is_unicode = 1;
else if (PyString_Check(format_spec))
spec_is_unicode = 0;
else {
PyErr_Format(PyExc_TypeError,
"format expects arg 2 to be string "
"or unicode, not %.100s", Py_TYPE(format_spec)->tp_name);
goto done;
}
/* Make sure the type is initialized. float gets initialized late */
if (Py_TYPE(obj)->tp_dict == NULL)
if (PyType_Ready(Py_TYPE(obj)) < 0)
goto done;
/* Check for a __format__ method and call it. */
if (PyInstance_Check(obj)) {
/* We're an instance of a classic class */
PyObject *bound_method = PyObject_GetAttr(obj,
str__format__);
if (bound_method != NULL) {
result = PyObject_CallFunctionObjArgs(bound_method,
format_spec,
NULL);
Py_DECREF(bound_method);
} else {
PyObject *self_as_str;
PyObject *format_method;
PyErr_Clear();
/* Per the PEP, convert to str (or unicode,
depending on the type of the format
specifier). For new-style classes, this
logic is done by object.__format__(). */
if (spec_is_unicode)
self_as_str = PyObject_Unicode(obj);
else
self_as_str = PyObject_Str(obj);
if (self_as_str == NULL)
goto done;
/* Then call str.__format__ on that result */
format_method = PyObject_GetAttr(self_as_str,
str__format__);
if (format_method == NULL) {
Py_DECREF(self_as_str);
goto done;
}
result = PyObject_CallFunctionObjArgs(format_method,
format_spec,
NULL);
Py_DECREF(self_as_str);
Py_DECREF(format_method);
if (result == NULL)
goto done;
}
} else {
/* Not an instance of a classic class, use the code
from py3k */
/* Find the (unbound!) __format__ method (a borrowed
reference) */
PyObject *method = _PyType_Lookup(Py_TYPE(obj),
str__format__);
if (method == NULL) {
PyErr_Format(PyExc_TypeError,
"Type %.100s doesn't define __format__",
Py_TYPE(obj)->tp_name);
goto done;
}
/* And call it, binding it to the value */
result = PyObject_CallFunctionObjArgs(method, obj,
format_spec, NULL);
}
if (result == NULL)
goto done;
/* Check the result type, and make sure it's str or unicode */
if (PyUnicode_Check(result))
result_is_unicode = 1;
else if (PyString_Check(result))
result_is_unicode = 0;
else {
PyErr_Format(PyExc_TypeError,
"%.100s.__format__ must return string or "
"unicode, not %.100s", Py_TYPE(obj)->tp_name,
Py_TYPE(result)->tp_name);
Py_DECREF(result);
result = NULL;
goto done;
}
/* Convert to unicode, if needed. Required if spec is unicode
and result is str */
if (spec_is_unicode && !result_is_unicode) {
PyObject *tmp = PyObject_Unicode(result);
/* This logic works whether or not tmp is NULL */
Py_DECREF(result);
result = tmp;
}
done:
Py_XDECREF(empty);
return result;
}
/* Operations on numbers */
int

View File

@ -10,6 +10,7 @@
#include <ctype.h>
#include <float.h>
#include "formatter_string.h"
#if !defined(__STDC__)
extern double fmod(double, double);
@ -1434,6 +1435,46 @@ float_getzero(PyObject *v, void *closure)
return PyFloat_FromDouble(0.0);
}
static PyObject *
float__format__(PyObject *self, PyObject *args)
{
PyObject *format_spec;
if (!PyArg_ParseTuple(args, "O:__format__", &format_spec))
return NULL;
if (PyString_Check(format_spec))
return string_float__format__(self, args);
if (PyUnicode_Check(format_spec)) {
/* Convert format_spec to a str */
PyObject *result = NULL;
PyObject *newargs = NULL;
PyObject *string_format_spec = NULL;
string_format_spec = PyObject_Str(format_spec);
if (string_format_spec == NULL)
goto done;
newargs = Py_BuildValue("(O)", string_format_spec);
if (newargs == NULL)
goto done;
result = string_float__format__(self, newargs);
done:
Py_XDECREF(string_format_spec);
Py_XDECREF(newargs);
return result;
}
PyErr_SetString(PyExc_TypeError, "__format__ requires str or unicode");
return NULL;
}
PyDoc_STRVAR(float__format__doc,
"float.__format__(format_spec) -> string\n"
"\n"
"Formats the float according to format_spec.");
static PyMethodDef float_methods[] = {
{"conjugate", (PyCFunction)float_float, METH_NOARGS,
"Returns self, the complex conjugate of any float."},
@ -1446,6 +1487,8 @@ static PyMethodDef float_methods[] = {
METH_O|METH_CLASS, float_getformat_doc},
{"__setformat__", (PyCFunction)float_setformat,
METH_VARARGS|METH_CLASS, float_setformat_doc},
{"__format__", (PyCFunction)float__format__,
METH_VARARGS, float__format__doc},
{NULL, NULL} /* sentinel */
};

View File

@ -3,6 +3,7 @@
#include "Python.h"
#include <ctype.h>
#include "formatter_string.h"
static PyObject *int_int(PyIntObject *v);
@ -1108,12 +1109,47 @@ _PyInt_Format(PyIntObject *v, int base, int newstyle)
return PyString_FromStringAndSize(p, &buf[sizeof(buf)] - p);
}
static PyObject *
int__format__(PyObject *self, PyObject *args)
{
PyObject *format_spec;
if (!PyArg_ParseTuple(args, "O:__format__", &format_spec))
return NULL;
if (PyString_Check(format_spec))
return string_int__format__(self, args);
if (PyUnicode_Check(format_spec)) {
/* Convert format_spec to a str */
PyObject *result = NULL;
PyObject *newargs = NULL;
PyObject *string_format_spec = NULL;
string_format_spec = PyObject_Str(format_spec);
if (string_format_spec == NULL)
goto done;
newargs = Py_BuildValue("(O)", string_format_spec);
if (newargs == NULL)
goto done;
result = string_int__format__(self, newargs);
done:
Py_XDECREF(string_format_spec);
Py_XDECREF(newargs);
return result;
}
PyErr_SetString(PyExc_TypeError, "__format__ requires str or unicode");
return NULL;
}
static PyMethodDef int_methods[] = {
{"conjugate", (PyCFunction)int_int, METH_NOARGS,
"Returns self, the complex conjugate of any int."},
{"__trunc__", (PyCFunction)int_int, METH_NOARGS,
"Truncating an Integral returns itself."},
{"__getnewargs__", (PyCFunction)int_getnewargs, METH_NOARGS},
{"__format__", (PyCFunction)int__format__, METH_VARARGS},
{NULL, NULL} /* sentinel */
};

View File

@ -6,6 +6,7 @@
#include "Python.h"
#include "longintrepr.h"
#include "formatter_string.h"
#include <ctype.h>
@ -3380,12 +3381,47 @@ long_getN(PyLongObject *v, void *context) {
return PyLong_FromLong((intptr_t)context);
}
static PyObject *
long__format__(PyObject *self, PyObject *args)
{
PyObject *format_spec;
if (!PyArg_ParseTuple(args, "O:__format__", &format_spec))
return NULL;
if (PyString_Check(format_spec))
return string_long__format__(self, args);
if (PyUnicode_Check(format_spec)) {
/* Convert format_spec to a str */
PyObject *result = NULL;
PyObject *newargs = NULL;
PyObject *string_format_spec = NULL;
string_format_spec = PyObject_Str(format_spec);
if (string_format_spec == NULL)
goto done;
newargs = Py_BuildValue("(O)", string_format_spec);
if (newargs == NULL)
goto done;
result = string_long__format__(self, newargs);
done:
Py_XDECREF(string_format_spec);
Py_XDECREF(newargs);
return result;
}
PyErr_SetString(PyExc_TypeError, "__format__ requires str or unicode");
return NULL;
}
static PyMethodDef long_methods[] = {
{"conjugate", (PyCFunction)long_long, METH_NOARGS,
"Returns self, the complex conjugate of any long."},
{"__trunc__", (PyCFunction)long_long, METH_NOARGS,
"Truncating an Integral returns itself."},
{"__getnewargs__", (PyCFunction)long_getnewargs, METH_NOARGS},
{"__format__", (PyCFunction)long__format__, METH_VARARGS},
{NULL, NULL} /* sentinel */
};

View File

@ -0,0 +1,980 @@
/* implements the string, long, and float formatters. that is,
string.__format__, etc. */
/* Before including this, you must include either:
stringlib/unicodedefs.h
stringlib/stringdefs.h
Also, you should define the names:
FORMAT_STRING
FORMAT_LONG
FORMAT_FLOAT
to be whatever you want the public names of these functions to
be. These are the only non-static functions defined here.
*/
#define ALLOW_PARENS_FOR_SIGN 0
/*
get_integer consumes 0 or more decimal digit characters from an
input string, updates *result with the corresponding positive
integer, and returns the number of digits consumed.
returns -1 on error.
*/
static int
get_integer(STRINGLIB_CHAR **ptr, STRINGLIB_CHAR *end,
Py_ssize_t *result)
{
Py_ssize_t accumulator, digitval, oldaccumulator;
int numdigits;
accumulator = numdigits = 0;
for (;;(*ptr)++, numdigits++) {
if (*ptr >= end)
break;
digitval = STRINGLIB_TODECIMAL(**ptr);
if (digitval < 0)
break;
/*
This trick was copied from old Unicode format code. It's cute,
but would really suck on an old machine with a slow divide
implementation. Fortunately, in the normal case we do not
expect too many digits.
*/
oldaccumulator = accumulator;
accumulator *= 10;
if ((accumulator+10)/10 != oldaccumulator+1) {
PyErr_Format(PyExc_ValueError,
"Too many decimal digits in format string");
return -1;
}
accumulator += digitval;
}
*result = accumulator;
return numdigits;
}
/************************************************************************/
/*********** standard format specifier parsing **************************/
/************************************************************************/
/* returns true if this character is a specifier alignment token */
Py_LOCAL_INLINE(int)
is_alignment_token(STRINGLIB_CHAR c)
{
switch (c) {
case '<': case '>': case '=': case '^':
return 1;
default:
return 0;
}
}
/* returns true if this character is a sign element */
Py_LOCAL_INLINE(int)
is_sign_element(STRINGLIB_CHAR c)
{
switch (c) {
case ' ': case '+': case '-':
#if ALLOW_PARENS_FOR_SIGN
case '(':
#endif
return 1;
default:
return 0;
}
}
typedef struct {
STRINGLIB_CHAR fill_char;
STRINGLIB_CHAR align;
STRINGLIB_CHAR sign;
Py_ssize_t width;
Py_ssize_t precision;
STRINGLIB_CHAR type;
} InternalFormatSpec;
/*
ptr points to the start of the format_spec, end points just past its end.
fills in format with the parsed information.
returns 1 on success, 0 on failure.
if failure, sets the exception
*/
static int
parse_internal_render_format_spec(PyObject *format_spec,
InternalFormatSpec *format,
char default_type)
{
STRINGLIB_CHAR *ptr = STRINGLIB_STR(format_spec);
STRINGLIB_CHAR *end = ptr + STRINGLIB_LEN(format_spec);
/* end-ptr is used throughout this code to specify the length of
the input string */
Py_ssize_t specified_width;
format->fill_char = '\0';
format->align = '\0';
format->sign = '\0';
format->width = -1;
format->precision = -1;
format->type = default_type;
/* If the second char is an alignment token,
then parse the fill char */
if (end-ptr >= 2 && is_alignment_token(ptr[1])) {
format->align = ptr[1];
format->fill_char = ptr[0];
ptr += 2;
}
else if (end-ptr >= 1 && is_alignment_token(ptr[0])) {
format->align = ptr[0];
ptr++;
}
/* Parse the various sign options */
if (end-ptr >= 1 && is_sign_element(ptr[0])) {
format->sign = ptr[0];
ptr++;
#if ALLOW_PARENS_FOR_SIGN
if (end-ptr >= 1 && ptr[0] == ')') {
ptr++;
}
#endif
}
/* The special case for 0-padding (backwards compat) */
if (format->fill_char == '\0' && end-ptr >= 1 && ptr[0] == '0') {
format->fill_char = '0';
if (format->align == '\0') {
format->align = '=';
}
ptr++;
}
/* XXX add error checking */
specified_width = get_integer(&ptr, end, &format->width);
/* if specified_width is 0, we didn't consume any characters for
the width. in that case, reset the width to -1, because
get_integer() will have set it to zero */
if (specified_width == 0) {
format->width = -1;
}
/* Parse field precision */
if (end-ptr && ptr[0] == '.') {
ptr++;
/* XXX add error checking */
specified_width = get_integer(&ptr, end, &format->precision);
/* not having a precision after a dot is an error */
if (specified_width == 0) {
PyErr_Format(PyExc_ValueError,
"Format specifier missing precision");
return 0;
}
}
/* Finally, parse the type field */
if (end-ptr > 1) {
/* invalid conversion spec */
PyErr_Format(PyExc_ValueError, "Invalid conversion specification");
return 0;
}
if (end-ptr == 1) {
format->type = ptr[0];
ptr++;
}
return 1;
}
#if defined FORMAT_FLOAT || defined FORMAT_LONG
/************************************************************************/
/*********** common routines for numeric formatting *********************/
/************************************************************************/
/* describes the layout for an integer, see the comment in
_calc_integer_widths() for details */
typedef struct {
Py_ssize_t n_lpadding;
Py_ssize_t n_spadding;
Py_ssize_t n_rpadding;
char lsign;
Py_ssize_t n_lsign;
char rsign;
Py_ssize_t n_rsign;
Py_ssize_t n_total; /* just a convenience, it's derivable from the
other fields */
} NumberFieldWidths;
/* not all fields of format are used. for example, precision is
unused. should this take discrete params in order to be more clear
about what it does? or is passing a single format parameter easier
and more efficient enough to justify a little obfuscation? */
static void
calc_number_widths(NumberFieldWidths *r, STRINGLIB_CHAR actual_sign,
Py_ssize_t n_digits, const InternalFormatSpec *format)
{
r->n_lpadding = 0;
r->n_spadding = 0;
r->n_rpadding = 0;
r->lsign = '\0';
r->n_lsign = 0;
r->rsign = '\0';
r->n_rsign = 0;
/* the output will look like:
| |
| <lpadding> <lsign> <spadding> <digits> <rsign> <rpadding> |
| |
lsign and rsign are computed from format->sign and the actual
sign of the number
digits is already known
the total width is either given, or computed from the
actual digits
only one of lpadding, spadding, and rpadding can be non-zero,
and it's calculated from the width and other fields
*/
/* compute the various parts we're going to write */
if (format->sign == '+') {
/* always put a + or - */
r->n_lsign = 1;
r->lsign = (actual_sign == '-' ? '-' : '+');
}
#if ALLOW_PARENS_FOR_SIGN
else if (format->sign == '(') {
if (actual_sign == '-') {
r->n_lsign = 1;
r->lsign = '(';
r->n_rsign = 1;
r->rsign = ')';
}
}
#endif
else if (format->sign == ' ') {
r->n_lsign = 1;
r->lsign = (actual_sign == '-' ? '-' : ' ');
}
else {
/* non specified, or the default (-) */
if (actual_sign == '-') {
r->n_lsign = 1;
r->lsign = '-';
}
}
/* now the number of padding characters */
if (format->width == -1) {
/* no padding at all, nothing to do */
}
else {
/* see if any padding is needed */
if (r->n_lsign + n_digits + r->n_rsign >= format->width) {
/* no padding needed, we're already bigger than the
requested width */
}
else {
/* determine which of left, space, or right padding is
needed */
Py_ssize_t padding = format->width -
(r->n_lsign + n_digits + r->n_rsign);
if (format->align == '<')
r->n_rpadding = padding;
else if (format->align == '>')
r->n_lpadding = padding;
else if (format->align == '^') {
r->n_lpadding = padding / 2;
r->n_rpadding = padding - r->n_lpadding;
}
else if (format->align == '=')
r->n_spadding = padding;
else
r->n_lpadding = padding;
}
}
r->n_total = r->n_lpadding + r->n_lsign + r->n_spadding +
n_digits + r->n_rsign + r->n_rpadding;
}
/* fill in the non-digit parts of a numbers's string representation,
as determined in _calc_integer_widths(). returns the pointer to
where the digits go. */
static STRINGLIB_CHAR *
fill_number(STRINGLIB_CHAR *p_buf, const NumberFieldWidths *spec,
Py_ssize_t n_digits, STRINGLIB_CHAR fill_char)
{
STRINGLIB_CHAR* p_digits;
if (spec->n_lpadding) {
STRINGLIB_FILL(p_buf, fill_char, spec->n_lpadding);
p_buf += spec->n_lpadding;
}
if (spec->n_lsign == 1) {
*p_buf++ = spec->lsign;
}
if (spec->n_spadding) {
STRINGLIB_FILL(p_buf, fill_char, spec->n_spadding);
p_buf += spec->n_spadding;
}
p_digits = p_buf;
p_buf += n_digits;
if (spec->n_rsign == 1) {
*p_buf++ = spec->rsign;
}
if (spec->n_rpadding) {
STRINGLIB_FILL(p_buf, fill_char, spec->n_rpadding);
p_buf += spec->n_rpadding;
}
return p_digits;
}
#endif /* FORMAT_FLOAT || FORMAT_LONG */
/************************************************************************/
/*********** string formatting ******************************************/
/************************************************************************/
static PyObject *
format_string_internal(PyObject *value, const InternalFormatSpec *format)
{
Py_ssize_t width; /* total field width */
Py_ssize_t lpad;
STRINGLIB_CHAR *dst;
STRINGLIB_CHAR *src = STRINGLIB_STR(value);
Py_ssize_t len = STRINGLIB_LEN(value);
PyObject *result = NULL;
/* sign is not allowed on strings */
if (format->sign != '\0') {
PyErr_SetString(PyExc_ValueError,
"Sign not allowed in string format specifier");
goto done;
}
/* '=' alignment not allowed on strings */
if (format->align == '=') {
PyErr_SetString(PyExc_ValueError,
"'=' alignment not allowed "
"in string format specifier");
goto done;
}
/* if precision is specified, output no more that format.precision
characters */
if (format->precision >= 0 && len >= format->precision) {
len = format->precision;
}
if (format->width >= 0) {
width = format->width;
/* but use at least len characters */
if (len > width) {
width = len;
}
}
else {
/* not specified, use all of the chars and no more */
width = len;
}
/* allocate the resulting string */
result = STRINGLIB_NEW(NULL, width);
if (result == NULL)
goto done;
/* now write into that space */
dst = STRINGLIB_STR(result);
/* figure out how much leading space we need, based on the
aligning */
if (format->align == '>')
lpad = width - len;
else if (format->align == '^')
lpad = (width - len) / 2;
else
lpad = 0;
/* if right aligning, increment the destination allow space on the
left */
memcpy(dst + lpad, src, len * sizeof(STRINGLIB_CHAR));
/* do any padding */
if (width > len) {
STRINGLIB_CHAR fill_char = format->fill_char;
if (fill_char == '\0') {
/* use the default, if not specified */
fill_char = ' ';
}
/* pad on left */
if (lpad)
STRINGLIB_FILL(dst, fill_char, lpad);
/* pad on right */
if (width - len - lpad)
STRINGLIB_FILL(dst + len + lpad, fill_char, width - len - lpad);
}
done:
return result;
}
/************************************************************************/
/*********** long formatting ********************************************/
/************************************************************************/
#if defined FORMAT_LONG || defined FORMAT_INT
typedef PyObject*
(*IntOrLongToString)(PyObject *value, int base);
static PyObject *
format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format,
IntOrLongToString tostring)
{
PyObject *result = NULL;
PyObject *tmp = NULL;
STRINGLIB_CHAR *pnumeric_chars;
STRINGLIB_CHAR numeric_char;
STRINGLIB_CHAR sign = '\0';
STRINGLIB_CHAR *p;
Py_ssize_t n_digits; /* count of digits need from the computed
string */
Py_ssize_t n_leading_chars;
NumberFieldWidths spec;
long x;
/* no precision allowed on integers */
if (format->precision != -1) {
PyErr_SetString(PyExc_ValueError,
"Precision not allowed in integer format specifier");
goto done;
}
/* special case for character formatting */
if (format->type == 'c') {
/* error to specify a sign */
if (format->sign != '\0') {
PyErr_SetString(PyExc_ValueError,
"Sign not allowed with integer"
" format specifier 'c'");
goto done;
}
/* taken from unicodeobject.c formatchar() */
/* Integer input truncated to a character */
/* XXX: won't work for int */
x = PyLong_AsLong(value);
if (x == -1 && PyErr_Occurred())
goto done;
#ifdef Py_UNICODE_WIDE
if (x < 0 || x > 0x10ffff) {
PyErr_SetString(PyExc_OverflowError,
"%c arg not in range(0x110000) "
"(wide Python build)");
goto done;
}
#else
if (x < 0 || x > 0xffff) {
PyErr_SetString(PyExc_OverflowError,
"%c arg not in range(0x10000) "
"(narrow Python build)");
goto done;
}
#endif
numeric_char = (STRINGLIB_CHAR)x;
pnumeric_chars = &numeric_char;
n_digits = 1;
}
else {
int base;
int leading_chars_to_skip; /* Number of characters added by
PyNumber_ToBase that we want to
skip over. */
/* Compute the base and how many characters will be added by
PyNumber_ToBase */
switch (format->type) {
case 'b':
base = 2;
leading_chars_to_skip = 2; /* 0b */
break;
case 'o':
base = 8;
leading_chars_to_skip = 2; /* 0o */
break;
case 'x':
case 'X':
base = 16;
leading_chars_to_skip = 2; /* 0x */
break;
default: /* shouldn't be needed, but stops a compiler warning */
case 'd':
base = 10;
leading_chars_to_skip = 0;
break;
}
/* Do the hard part, converting to a string in a given base */
tmp = tostring(value, base);
if (tmp == NULL)
goto done;
pnumeric_chars = STRINGLIB_STR(tmp);
n_digits = STRINGLIB_LEN(tmp);
/* Remember not to modify what pnumeric_chars points to. it
might be interned. Only modify it after we copy it into a
newly allocated output buffer. */
/* Is a sign character present in the output? If so, remember it
and skip it */
sign = pnumeric_chars[0];
if (sign == '-') {
++leading_chars_to_skip;
}
/* Skip over the leading chars (0x, 0b, etc.) */
n_digits -= leading_chars_to_skip;
pnumeric_chars += leading_chars_to_skip;
}
/* Calculate the widths of the various leading and trailing parts */
calc_number_widths(&spec, sign, n_digits, format);
/* Allocate a new string to hold the result */
result = STRINGLIB_NEW(NULL, spec.n_total);
if (!result)
goto done;
p = STRINGLIB_STR(result);
/* Fill in the digit parts */
n_leading_chars = spec.n_lpadding + spec.n_lsign + spec.n_spadding;
memmove(p + n_leading_chars,
pnumeric_chars,
n_digits * sizeof(STRINGLIB_CHAR));
/* if X, convert to uppercase */
if (format->type == 'X') {
Py_ssize_t t;
for (t = 0; t < n_digits; t++)
p[t + n_leading_chars] = STRINGLIB_TOUPPER(p[t + n_leading_chars]);
}
/* Fill in the non-digit parts */
fill_number(p, &spec, n_digits,
format->fill_char == '\0' ? ' ' : format->fill_char);
done:
Py_XDECREF(tmp);
return result;
}
#endif /* defined FORMAT_LONG || defined FORMAT_INT */
/************************************************************************/
/*********** float formatting *******************************************/
/************************************************************************/
#ifdef FORMAT_FLOAT
#if STRINGLIB_IS_UNICODE
/* taken from unicodeobject.c */
static Py_ssize_t
strtounicode(Py_UNICODE *buffer, const char *charbuffer)
{
register Py_ssize_t i;
Py_ssize_t len = strlen(charbuffer);
for (i = len - 1; i >= 0; i--)
buffer[i] = (Py_UNICODE) charbuffer[i];
return len;
}
#endif
/* the callback function to call to do the actual float formatting.
it matches the definition of PyOS_ascii_formatd */
typedef char*
(*DoubleSnprintfFunction)(char *buffer, size_t buf_len,
const char *format, double d);
/* just a wrapper to make PyOS_snprintf look like DoubleSnprintfFunction */
static char*
snprintf_double(char *buffer, size_t buf_len, const char *format, double d)
{
PyOS_snprintf(buffer, buf_len, format, d);
return NULL;
}
/* see FORMATBUFLEN in unicodeobject.c */
#define FLOAT_FORMATBUFLEN 120
/* much of this is taken from unicodeobject.c */
/* use type instead of format->type, so that it can be overridden by
format_number() */
static PyObject *
_format_float(STRINGLIB_CHAR type, PyObject *value,
const InternalFormatSpec *format,
DoubleSnprintfFunction snprintf)
{
/* fmt = '%.' + `prec` + `type` + '%%'
worst case length = 2 + 10 (len of INT_MAX) + 1 + 2 = 15 (use 20)*/
char fmt[20];
/* taken from unicodeobject.c */
/* Worst case length calc to ensure no buffer overrun:
'g' formats:
fmt = %#.<prec>g
buf = '-' + [0-9]*prec + '.' + 'e+' + (longest exp
for any double rep.)
len = 1 + prec + 1 + 2 + 5 = 9 + prec
'f' formats:
buf = '-' + [0-9]*x + '.' + [0-9]*prec (with x < 50)
len = 1 + 50 + 1 + prec = 52 + prec
If prec=0 the effective precision is 1 (the leading digit is
always given), therefore increase the length by one.
*/
char charbuf[FLOAT_FORMATBUFLEN];
Py_ssize_t n_digits;
double x;
Py_ssize_t precision = format->precision;
PyObject *result = NULL;
STRINGLIB_CHAR sign;
char* trailing = "";
STRINGLIB_CHAR *p;
NumberFieldWidths spec;
#if STRINGLIB_IS_UNICODE
Py_UNICODE unicodebuf[FLOAT_FORMATBUFLEN];
#endif
/* first, do the conversion as 8-bit chars, using the platform's
snprintf. then, if needed, convert to unicode. */
/* 'F' is the same as 'f', per the PEP */
if (type == 'F')
type = 'f';
x = PyFloat_AsDouble(value);
if (x == -1.0 && PyErr_Occurred())
goto done;
if (type == '%') {
type = 'f';
x *= 100;
trailing = "%";
}
if (precision < 0)
precision = 6;
if (type == 'f' && (fabs(x) / 1e25) >= 1e25)
type = 'g';
/* cast "type", because if we're in unicode we need to pass a
8-bit char. this is safe, because we've restricted what "type"
can be */
PyOS_snprintf(fmt, sizeof(fmt), "%%.%" PY_FORMAT_SIZE_T "d%c", precision,
(char)type);
/* call the passed in function to do the actual formatting */
snprintf(charbuf, sizeof(charbuf), fmt, x);
/* adding trailing to fmt with PyOS_snprintf doesn't work, not
sure why. we'll just concatentate it here, no harm done. we
know we can't have a buffer overflow from the fmt size
analysis */
strcat(charbuf, trailing);
/* rather than duplicate the code for snprintf for both unicode
and 8 bit strings, we just use the 8 bit version and then
convert to unicode in a separate code path. that's probably
the lesser of 2 evils. */
#if STRINGLIB_IS_UNICODE
n_digits = strtounicode(unicodebuf, charbuf);
p = unicodebuf;
#else
/* compute the length. I believe this is done because the return
value from snprintf above is unreliable */
n_digits = strlen(charbuf);
p = charbuf;
#endif
/* is a sign character present in the output? if so, remember it
and skip it */
sign = p[0];
if (sign == '-') {
p++;
n_digits--;
}
calc_number_widths(&spec, sign, n_digits, format);
/* allocate a string with enough space */
result = STRINGLIB_NEW(NULL, spec.n_total);
if (result == NULL)
goto done;
/* fill in the non-digit parts */
fill_number(STRINGLIB_STR(result), &spec, n_digits,
format->fill_char == '\0' ? ' ' : format->fill_char);
/* fill in the digit parts */
memmove(STRINGLIB_STR(result) +
(spec.n_lpadding + spec.n_lsign + spec.n_spadding),
p,
n_digits * sizeof(STRINGLIB_CHAR));
done:
return result;
}
static PyObject *
format_float_internal(PyObject *value, const InternalFormatSpec *format)
{
if (format->type == 'n')
return _format_float('f', value, format, snprintf_double);
else
return _format_float(format->type, value, format, PyOS_ascii_formatd);
}
#endif /* FORMAT_FLOAT */
/************************************************************************/
/*********** built in formatters ****************************************/
/************************************************************************/
#ifdef FORMAT_STRING
PyObject *
FORMAT_STRING(PyObject* value, PyObject* args)
{
PyObject *format_spec;
PyObject *result = NULL;
#if PY_VERSION_HEX < 0x03000000
PyObject *tmp = NULL;
#endif
InternalFormatSpec format;
/* If 2.x, we accept either str or unicode, and try to convert it
to the right type. In 3.x, we insist on only unicode */
#if PY_VERSION_HEX >= 0x03000000
if (!PyArg_ParseTuple(args, STRINGLIB_PARSE_CODE ":__format__",
&format_spec))
goto done;
#else
/* If 2.x, convert format_spec to the same type as value */
/* This is to allow things like u''.format('') */
if (!PyArg_ParseTuple(args, "O:__format__", &format_spec))
goto done;
if (!(PyString_Check(format_spec) || PyUnicode_Check(format_spec))) {
PyErr_Format(PyExc_TypeError, "__format__ arg must be str "
"or unicode, not %s", Py_TYPE(format_spec)->tp_name);
goto done;
}
tmp = STRINGLIB_TOSTR(format_spec);
if (tmp == NULL)
goto done;
format_spec = tmp;
#endif
/* check for the special case of zero length format spec, make
it equivalent to str(value) */
if (STRINGLIB_LEN(format_spec) == 0) {
result = STRINGLIB_TOSTR(value);
goto done;
}
/* parse the format_spec */
if (!parse_internal_render_format_spec(format_spec, &format, 's'))
goto done;
/* type conversion? */
switch (format.type) {
case 's':
/* no type conversion needed, already a string. do the formatting */
result = format_string_internal(value, &format);
break;
default:
/* unknown */
PyErr_Format(PyExc_ValueError, "Unknown conversion type %c",
format.type);
goto done;
}
done:
#if PY_VERSION_HEX < 0x03000000
Py_XDECREF(tmp);
#endif
return result;
}
#endif /* FORMAT_STRING */
#if defined FORMAT_LONG || defined FORMAT_INT
static PyObject*
format_int_or_long(PyObject* value, PyObject* args, IntOrLongToString tostring)
{
PyObject *format_spec;
PyObject *result = NULL;
PyObject *tmp = NULL;
InternalFormatSpec format;
if (!PyArg_ParseTuple(args, STRINGLIB_PARSE_CODE ":__format__",
&format_spec))
goto done;
/* check for the special case of zero length format spec, make
it equivalent to str(value) */
if (STRINGLIB_LEN(format_spec) == 0) {
result = STRINGLIB_TOSTR(value);
goto done;
}
/* parse the format_spec */
if (!parse_internal_render_format_spec(format_spec, &format, 'd'))
goto done;
/* type conversion? */
switch (format.type) {
case 'b':
case 'c':
case 'd':
case 'o':
case 'x':
case 'X':
/* no type conversion needed, already an int (or long). do
the formatting */
result = format_int_or_long_internal(value, &format, tostring);
break;
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
case 'n':
case '%':
/* convert to float */
tmp = PyNumber_Float(value);
if (tmp == NULL)
goto done;
result = format_float_internal(value, &format);
break;
default:
/* unknown */
PyErr_Format(PyExc_ValueError, "Unknown conversion type %c",
format.type);
goto done;
}
done:
Py_XDECREF(tmp);
return result;
}
#endif /* FORMAT_LONG || defined FORMAT_INT */
#ifdef FORMAT_LONG
/* Need to define long_format as a function that will convert a long
to a string. In 3.0, _PyLong_Format has the correct signature. In
2.x, we need to fudge a few parameters */
#if PY_VERSION_HEX >= 0x03000000
#define long_format _PyLong_Format
#else
static PyObject*
long_format(PyObject* value, int base)
{
/* Convert to base, don't add trailing 'L', and use the new octal
format. We already know this is a long object */
assert(PyLong_Check(value));
/* convert to base, don't add 'L', and use the new octal format */
return _PyLong_Format(value, base, 0, 1);
}
#endif
PyObject *
FORMAT_LONG(PyObject* value, PyObject* args)
{
return format_int_or_long(value, args, long_format);
}
#endif /* FORMAT_LONG */
#ifdef FORMAT_INT
/* this is only used for 2.x, not 3.0 */
static PyObject*
int_format(PyObject* value, int base)
{
/* Convert to base, and use the new octal format. We already
know this is an int object */
assert(PyInt_Check(value));
return _PyInt_Format((PyIntObject*)value, base, 1);
}
PyObject *
FORMAT_INT(PyObject* value, PyObject* args)
{
return format_int_or_long(value, args, int_format);
}
#endif /* FORMAT_INT */
#ifdef FORMAT_FLOAT
PyObject *
FORMAT_FLOAT(PyObject *value, PyObject *args)
{
PyObject *format_spec;
PyObject *result = NULL;
InternalFormatSpec format;
if (!PyArg_ParseTuple(args, STRINGLIB_PARSE_CODE ":__format__", &format_spec))
goto done;
/* check for the special case of zero length format spec, make
it equivalent to str(value) */
if (STRINGLIB_LEN(format_spec) == 0) {
result = STRINGLIB_TOSTR(value);
goto done;
}
/* parse the format_spec */
if (!parse_internal_render_format_spec(format_spec, &format, 'g'))
goto done;
/* type conversion? */
switch (format.type) {
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
case 'n':
case '%':
/* no conversion, already a float. do the formatting */
result = format_float_internal(value, &format);
break;
default:
/* unknown */
PyErr_Format(PyExc_ValueError, "Unknown conversion type %c",
format.type);
goto done;
}
done:
return result;
}
#endif /* FORMAT_FLOAT */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,27 @@
#ifndef STRINGLIB_STRINGDEFS_H
#define STRINGLIB_STRINGDEFS_H
/* this is sort of a hack. there's at least one place (formatting
floats) where some stringlib code takes a different path if it's
compiled as unicode. */
#define STRINGLIB_IS_UNICODE 0
#define STRINGLIB_OBJECT PyStringObject
#define STRINGLIB_CHAR char
#define STRINGLIB_TYPE_NAME "string"
#define STRINGLIB_PARSE_CODE "S"
#define STRINGLIB_EMPTY nullstring
#define STRINGLIB_ISDECIMAL(x) ((x >= '0') && (x <= '9'))
#define STRINGLIB_TODECIMAL(x) (STRINGLIB_ISDECIMAL(x) ? (x - '0') : -1)
#define STRINGLIB_TOUPPER toupper
#define STRINGLIB_TOLOWER tolower
#define STRINGLIB_FILL memset
#define STRINGLIB_STR PyString_AS_STRING
#define STRINGLIB_LEN PyString_GET_SIZE
#define STRINGLIB_NEW PyString_FromStringAndSize
#define STRINGLIB_RESIZE _PyString_Resize
#define STRINGLIB_CHECK PyString_Check
#define STRINGLIB_CMP memcmp
#define STRINGLIB_TOSTR PyObject_Str
#endif /* !STRINGLIB_STRINGDEFS_H */

View File

@ -0,0 +1,52 @@
#ifndef STRINGLIB_UNICODEDEFS_H
#define STRINGLIB_UNICODEDEFS_H
/* this is sort of a hack. there's at least one place (formatting
floats) where some stringlib code takes a different path if it's
compiled as unicode. */
#define STRINGLIB_IS_UNICODE 1
#define STRINGLIB_OBJECT PyUnicodeObject
#define STRINGLIB_CHAR Py_UNICODE
#define STRINGLIB_TYPE_NAME "unicode"
#define STRINGLIB_PARSE_CODE "U"
#define STRINGLIB_EMPTY unicode_empty
#define STRINGLIB_ISDECIMAL Py_UNICODE_ISDECIMAL
#define STRINGLIB_TODECIMAL Py_UNICODE_TODECIMAL
#define STRINGLIB_TOUPPER Py_UNICODE_TOUPPER
#define STRINGLIB_TOLOWER Py_UNICODE_TOLOWER
#define STRINGLIB_FILL Py_UNICODE_FILL
#define STRINGLIB_STR PyUnicode_AS_UNICODE
#define STRINGLIB_LEN PyUnicode_GET_SIZE
#define STRINGLIB_NEW PyUnicode_FromUnicode
#define STRINGLIB_RESIZE PyUnicode_Resize
#define STRINGLIB_CHECK PyUnicode_Check
#if PY_VERSION_HEX < 0x03000000
#define STRINGLIB_TOSTR PyObject_Unicode
#else
#define STRINGLIB_TOSTR PyObject_Str
#endif
#define STRINGLIB_WANT_CONTAINS_OBJ 1
/* STRINGLIB_CMP was defined as:
Py_LOCAL_INLINE(int)
STRINGLIB_CMP(const Py_UNICODE* str, const Py_UNICODE* other, Py_ssize_t len)
{
if (str[0] != other[0])
return 1;
return memcmp((void*) str, (void*) other, len * sizeof(Py_UNICODE));
}
but unfortunately that gives a error if the function isn't used in a file that
includes this file. So, reluctantly convert it to a macro instead. */
#define STRINGLIB_CMP(str, other, len) \
(((str)[0] != (other)[0]) ? \
1 : \
memcmp((void*) (str), (void*) (other), (len) * sizeof(Py_UNICODE)))
#endif /* !STRINGLIB_UNICODEDEFS_H */

View File

@ -4,6 +4,8 @@
#include "Python.h"
#include "formatter_string.h"
#include <ctype.h>
#ifdef COUNT_ALLOCS
@ -771,15 +773,7 @@ PyString_AsStringAndSize(register PyObject *obj,
/* -------------------------------------------------------------------- */
/* Methods */
#define STRINGLIB_CHAR char
#define STRINGLIB_CMP memcmp
#define STRINGLIB_LEN PyString_GET_SIZE
#define STRINGLIB_NEW PyString_FromStringAndSize
#define STRINGLIB_STR PyString_AS_STRING
#define STRINGLIB_EMPTY nullstring
#include "stringlib/stringdefs.h"
#include "stringlib/fastsearch.h"
#include "stringlib/count.h"
@ -3910,6 +3904,19 @@ string_getnewargs(PyStringObject *v)
return Py_BuildValue("(s#)", v->ob_sval, Py_SIZE(v));
}
#include "stringlib/string_format.h"
PyDoc_STRVAR(format__doc__,
"S.format(*args, **kwargs) -> unicode\n\
\n\
");
PyDoc_STRVAR(p_format__doc__,
"S.__format__(format_spec) -> unicode\n\
\n\
");
static PyMethodDef
string_methods[] = {
@ -3954,6 +3961,10 @@ string_methods[] = {
{"rjust", (PyCFunction)string_rjust, METH_VARARGS, rjust__doc__},
{"center", (PyCFunction)string_center, METH_VARARGS, center__doc__},
{"zfill", (PyCFunction)string_zfill, METH_VARARGS, zfill__doc__},
{"format", (PyCFunction) do_string_format, METH_VARARGS | METH_KEYWORDS, format__doc__},
{"__format__", (PyCFunction) string__format__, METH_VARARGS, p_format__doc__},
{"_formatter_field_name_split", (PyCFunction) formatter_field_name_split, METH_NOARGS},
{"_formatter_parser", (PyCFunction) formatter_parser, METH_NOARGS},
{"encode", (PyCFunction)string_encode, METH_VARARGS, encode__doc__},
{"decode", (PyCFunction)string_decode, METH_VARARGS, decode__doc__},
{"expandtabs", (PyCFunction)string_expandtabs, METH_VARARGS,

View File

@ -3210,11 +3210,57 @@ object_reduce_ex(PyObject *self, PyObject *args)
return _common_reduce(self, proto);
}
/*
from PEP 3101, this code implements:
class object:
def __format__(self, format_spec):
if isinstance(format_spec, str):
return format(str(self), format_spec)
elif isinstance(format_spec, unicode):
return format(unicode(self), format_spec)
*/
static PyObject *
object_format(PyObject *self, PyObject *args)
{
PyObject *format_spec;
PyObject *self_as_str = NULL;
PyObject *result = NULL;
PyObject *format_meth = NULL;
if (!PyArg_ParseTuple(args, "O:__format__", &format_spec))
return NULL;
if (PyUnicode_Check(format_spec)) {
self_as_str = PyObject_Unicode(self);
} else if (PyString_Check(format_spec)) {
self_as_str = PyObject_Str(self);
} else {
PyErr_SetString(PyExc_TypeError, "argument to __format__ must be unicode or str");
return NULL;
}
if (self_as_str != NULL) {
/* find the format function */
format_meth = PyObject_GetAttrString(self_as_str, "__format__");
if (format_meth != NULL) {
/* and call it */
result = PyObject_CallFunctionObjArgs(format_meth, format_spec, NULL);
}
}
Py_XDECREF(self_as_str);
Py_XDECREF(format_meth);
return result;
}
static PyMethodDef object_methods[] = {
{"__reduce_ex__", object_reduce_ex, METH_VARARGS,
PyDoc_STR("helper for pickle")},
{"__reduce__", object_reduce, METH_VARARGS,
PyDoc_STR("helper for pickle")},
{"__format__", object_format, METH_VARARGS,
PyDoc_STR("default object formatter")},
{0}
};

View File

@ -42,6 +42,8 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#define PY_SSIZE_T_CLEAN
#include "Python.h"
#include "formatter_unicode.h"
#include "unicodeobject.h"
#include "ucnhash.h"
@ -5059,21 +5061,8 @@ int PyUnicode_EncodeDecimal(Py_UNICODE *s,
/* --- Helpers ------------------------------------------------------------ */
#define STRINGLIB_CHAR Py_UNICODE
#include "stringlib/unicodedefs.h"
#define STRINGLIB_LEN PyUnicode_GET_SIZE
#define STRINGLIB_NEW PyUnicode_FromUnicode
#define STRINGLIB_STR PyUnicode_AS_UNICODE
Py_LOCAL_INLINE(int)
STRINGLIB_CMP(const Py_UNICODE* str, const Py_UNICODE* other, Py_ssize_t len)
{
if (str[0] != other[0])
return 1;
return memcmp((void*) str, (void*) other, len * sizeof(Py_UNICODE));
}
#define STRINGLIB_EMPTY unicode_empty
#define FROM_UNICODE
#include "stringlib/fastsearch.h"
@ -7802,6 +7791,19 @@ unicode_endswith(PyUnicodeObject *self,
}
/* Implements do_string_format, which is unicode because of stringlib */
#include "stringlib/string_format.h"
PyDoc_STRVAR(format__doc__,
"S.format(*args, **kwargs) -> unicode\n\
\n\
");
PyDoc_STRVAR(p_format__doc__,
"S.__format__(format_spec) -> unicode\n\
\n\
");
static PyObject *
unicode_getnewargs(PyUnicodeObject *v)
@ -7855,6 +7857,10 @@ static PyMethodDef unicode_methods[] = {
{"isalpha", (PyCFunction) unicode_isalpha, METH_NOARGS, isalpha__doc__},
{"isalnum", (PyCFunction) unicode_isalnum, METH_NOARGS, isalnum__doc__},
{"zfill", (PyCFunction) unicode_zfill, METH_VARARGS, zfill__doc__},
{"format", (PyCFunction) do_string_format, METH_VARARGS | METH_KEYWORDS, format__doc__},
{"__format__", (PyCFunction) unicode__format__, METH_VARARGS, p_format__doc__},
{"_formatter_field_name_split", (PyCFunction) formatter_field_name_split, METH_NOARGS},
{"_formatter_parser", (PyCFunction) formatter_parser, METH_NOARGS},
#if 0
{"capwords", (PyCFunction) unicode_capwords, METH_NOARGS, capwords__doc__},
#endif

View File

@ -338,6 +338,24 @@ PyDoc_STRVAR(filter_doc,
"function is None, return the items that are true. If sequence is a tuple\n"
"or string, return the same type, else return a list.");
static PyObject *
builtin_format(PyObject *self, PyObject *args)
{
PyObject *value;
PyObject *format_spec = NULL;
if (!PyArg_ParseTuple(args, "O|O:format", &value, &format_spec))
return NULL;
return PyObject_Format(value, format_spec);
}
PyDoc_STRVAR(format_doc,
"format(value[, format_spec]) -> string\n\
\n\
Returns value.__format__(format_spec)\n\
format_spec defaults to \"\"");
static PyObject *
builtin_chr(PyObject *self, PyObject *args)
{
@ -2359,6 +2377,7 @@ static PyMethodDef builtin_methods[] = {
{"eval", builtin_eval, METH_VARARGS, eval_doc},
{"execfile", builtin_execfile, METH_VARARGS, execfile_doc},
{"filter", builtin_filter, METH_VARARGS, filter_doc},
{"format", builtin_format, METH_VARARGS, format_doc},
{"getattr", builtin_getattr, METH_VARARGS, getattr_doc},
{"globals", (PyCFunction)builtin_globals, METH_NOARGS, globals_doc},
{"hasattr", builtin_hasattr, METH_VARARGS, hasattr_doc},

15
Python/formatter_string.c Normal file
View File

@ -0,0 +1,15 @@
/***********************************************************************/
/* Implements the string (as opposed to unicode) version of the
built-in formatters for string, int, float. That is, the versions
of int.__float__, etc., that take and return string objects */
#include "Python.h"
#include "formatter_string.h"
#include "../Objects/stringlib/stringdefs.h"
#define FORMAT_STRING string__format__
#define FORMAT_LONG string_long__format__
#define FORMAT_INT string_int__format__
#define FORMAT_FLOAT string_float__format__
#include "../Objects/stringlib/formatter.h"

View File

@ -0,0 +1,13 @@
/* Implements the unicode (as opposed to string) version of the
built-in formatter for unicode. That is, unicode.__format__(). */
#include "Python.h"
#include "formatter_unicode.h"
#include "../Objects/stringlib/unicodedefs.h"
#define FORMAT_STRING unicode__format__
/* don't define FORMAT_LONG and FORMAT_FLOAT, since we can live
with only the string versions of those. The builtin format()
will convert them to unicode. */
#include "../Objects/stringlib/formatter.h"