mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	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:
		
							parent
							
								
									e139688d34
								
							
						
					
					
						commit
						a9f7d62480
					
				
					 27 changed files with 3873 additions and 23 deletions
				
			
		|  | @ -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 */ | /* Iterators */ | ||||||
| 
 | 
 | ||||||
|      PyAPI_FUNC(PyObject *) PyObject_GetIter(PyObject *); |      PyAPI_FUNC(PyObject *) PyObject_GetIter(PyObject *); | ||||||
|  |  | ||||||
							
								
								
									
										12
									
								
								Include/formatter_string.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								Include/formatter_string.h
									
										
									
									
									
										Normal 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); | ||||||
|  | 
 | ||||||
							
								
								
									
										12
									
								
								Include/formatter_unicode.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								Include/formatter_unicode.h
									
										
									
									
									
										Normal 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); | ||||||
|  | 
 | ||||||
							
								
								
									
										112
									
								
								Lib/string.py
									
										
									
									
									
								
							
							
						
						
									
										112
									
								
								Lib/string.py
									
										
									
									
									
								
							|  | @ -527,3 +527,115 @@ def replace(s, old, new, maxsplit=-1): | ||||||
|     letters = lowercase + uppercase |     letters = lowercase + uppercase | ||||||
| except ImportError: | except ImportError: | ||||||
|     pass                                          # Use the original versions |     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 | ||||||
|  |  | ||||||
|  | @ -2012,6 +2012,101 @@ def test_baddecorator(self): | ||||||
|         data = 'The quick Brown fox Jumped over The lazy Dog'.split() |         data = 'The quick Brown fox Jumped over The lazy Dog'.split() | ||||||
|         self.assertRaises(TypeError, sorted, data, None, lambda x,y: 0) |         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): | def test_main(verbose=None): | ||||||
|     test_classes = (BuiltinTest, TestSorted) |     test_classes = (BuiltinTest, TestSorted) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -854,6 +854,32 @@ def test_strftime(self): | ||||||
|         # A naive object replaces %z and %Z w/ empty strings. |         # A naive object replaces %z and %Z w/ empty strings. | ||||||
|         self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''") |         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): |     def test_resolution_info(self): | ||||||
|         self.assert_(isinstance(self.theclass.min, self.theclass)) |         self.assert_(isinstance(self.theclass.min, self.theclass)) | ||||||
|         self.assert_(isinstance(self.theclass.max, self.theclass)) |         self.assert_(isinstance(self.theclass.max, self.theclass)) | ||||||
|  | @ -1136,6 +1162,32 @@ def test_isoformat(self): | ||||||
|         # str is ISO format with the separator forced to a blank. |         # str is ISO format with the separator forced to a blank. | ||||||
|         self.assertEqual(str(t), "0002-03-02 00:00:00") |         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): |     def test_more_ctime(self): | ||||||
|         # Test fields that TestDate doesn't touch. |         # Test fields that TestDate doesn't touch. | ||||||
|         import time |         import time | ||||||
|  | @ -1767,6 +1819,30 @@ def test_strftime(self): | ||||||
|         # A naive object replaces %z and %Z with empty strings. |         # A naive object replaces %z and %Z with empty strings. | ||||||
|         self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''") |         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): |     def test_str(self): | ||||||
|         self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004") |         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") |         self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000") | ||||||
|  |  | ||||||
|  | @ -183,6 +183,7 @@ def merge(self, other): | ||||||
|      '__delslice__', |      '__delslice__', | ||||||
|      '__doc__', |      '__doc__', | ||||||
|      '__eq__', |      '__eq__', | ||||||
|  |      '__format__', | ||||||
|      '__ge__', |      '__ge__', | ||||||
|      '__getattribute__', |      '__getattribute__', | ||||||
|      '__getitem__', |      '__getitem__', | ||||||
|  |  | ||||||
|  | @ -93,6 +93,264 @@ def test_expandtabs_overflows_gracefully(self): | ||||||
|             return |             return | ||||||
|         self.assertRaises(OverflowError, 't\tt\t'.expandtabs, sys.maxint) |         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(): | def test_main(): | ||||||
|     test_support.run_unittest(StrTest) |     test_support.run_unittest(StrTest) | ||||||
|  |  | ||||||
|  | @ -106,6 +106,92 @@ def test_capwords(self): | ||||||
|         self.assertEqual(string.capwords('ABC-DEF-GHI', '-'), 'Abc-Def-Ghi') |         self.assertEqual(string.capwords('ABC-DEF-GHI', '-'), 'Abc-Def-Ghi') | ||||||
|         self.assertEqual(string.capwords('ABC-def DEF-ghi GHI'), 'Abc-def Def-ghi 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): | class BytesAliasTest(unittest.TestCase): | ||||||
| 
 | 
 | ||||||
|     def test_builtin(self): |     def test_builtin(self): | ||||||
|  |  | ||||||
|  | @ -266,6 +266,257 @@ def test_buffers(self): | ||||||
|         except TypeError: pass |         except TypeError: pass | ||||||
|         else: self.fail("char buffer (at C level) not working") |         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(): | def test_main(): | ||||||
|     run_unittest(TypesTests) |     run_unittest(TypesTests) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -825,6 +825,268 @@ def test_expandtabs_overflows_gracefully(self): | ||||||
|             return |             return | ||||||
|         self.assertRaises(OverflowError, u't\tt\t'.expandtabs, sys.maxint) |         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(): | def test_main(): | ||||||
|     test_support.run_unittest(__name__) |     test_support.run_unittest(__name__) | ||||||
|  |  | ||||||
|  | @ -281,6 +281,8 @@ PYTHON_OBJS=	\ | ||||||
| 		Python/getopt.o \ | 		Python/getopt.o \ | ||||||
| 		Python/pystrcmp.o \ | 		Python/pystrcmp.o \ | ||||||
| 		Python/pystrtod.o \ | 		Python/pystrtod.o \ | ||||||
|  | 		Python/formatter_unicode.o \ | ||||||
|  | 		Python/formatter_string.o \ | ||||||
| 		Python/$(DYNLOADFILE) \ | 		Python/$(DYNLOADFILE) \ | ||||||
| 		$(LIBOBJS) \ | 		$(LIBOBJS) \ | ||||||
| 		$(MACHDEP_OBJS) \ | 		$(MACHDEP_OBJS) \ | ||||||
|  | @ -515,6 +517,20 @@ Python/importdl.o: $(srcdir)/Python/importdl.c | ||||||
| Objects/unicodectype.o:	$(srcdir)/Objects/unicodectype.c \ | Objects/unicodectype.o:	$(srcdir)/Objects/unicodectype.c \ | ||||||
| 				$(srcdir)/Objects/unicodetype_db.h | 				$(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 | # Header files | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2469,6 +2469,32 @@ date_strftime(PyDateTime_Date *self, PyObject *args, PyObject *kw) | ||||||
| 	return result; | 	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. */ | /* ISO methods. */ | ||||||
| 
 | 
 | ||||||
| static PyObject * | static PyObject * | ||||||
|  | @ -2633,6 +2659,9 @@ static PyMethodDef date_methods[] = { | ||||||
| 	{"strftime",   	(PyCFunction)date_strftime,	METH_VARARGS | METH_KEYWORDS, | 	{"strftime",   	(PyCFunction)date_strftime,	METH_VARARGS | METH_KEYWORDS, | ||||||
| 	 PyDoc_STR("format -> strftime() style string.")}, | 	 PyDoc_STR("format -> strftime() style string.")}, | ||||||
| 
 | 
 | ||||||
|  | 	{"__format__", 	(PyCFunction)date_format,	METH_VARARGS, | ||||||
|  | 	 PyDoc_STR("Formats self with strftime.")}, | ||||||
|  | 
 | ||||||
| 	{"timetuple",   (PyCFunction)date_timetuple,    METH_NOARGS, | 	{"timetuple",   (PyCFunction)date_timetuple,    METH_NOARGS, | ||||||
|          PyDoc_STR("Return time tuple, compatible with time.localtime().")}, |          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, | 	{"strftime",   	(PyCFunction)time_strftime,	METH_VARARGS | METH_KEYWORDS, | ||||||
| 	 PyDoc_STR("format -> strftime() style string.")}, | 	 PyDoc_STR("format -> strftime() style string.")}, | ||||||
| 
 | 
 | ||||||
|  | 	{"__format__", 	(PyCFunction)date_format,	METH_VARARGS, | ||||||
|  | 	 PyDoc_STR("Formats self with strftime.")}, | ||||||
|  | 
 | ||||||
| 	{"utcoffset",	(PyCFunction)time_utcoffset,	METH_NOARGS, | 	{"utcoffset",	(PyCFunction)time_utcoffset,	METH_NOARGS, | ||||||
| 	 PyDoc_STR("Return self.tzinfo.utcoffset(self).")}, | 	 PyDoc_STR("Return self.tzinfo.utcoffset(self).")}, | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -348,6 +348,138 @@ int PyObject_AsWriteBuffer(PyObject *obj, | ||||||
| 	return 0; | 	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 */ | /* Operations on numbers */ | ||||||
| 
 | 
 | ||||||
| int | int | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ | ||||||
| #include <ctype.h> | #include <ctype.h> | ||||||
| #include <float.h> | #include <float.h> | ||||||
| 
 | 
 | ||||||
|  | #include "formatter_string.h" | ||||||
| 
 | 
 | ||||||
| #if !defined(__STDC__) | #if !defined(__STDC__) | ||||||
| extern double fmod(double, double); | extern double fmod(double, double); | ||||||
|  | @ -1434,6 +1435,46 @@ float_getzero(PyObject *v, void *closure) | ||||||
| 	return PyFloat_FromDouble(0.0); | 	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[] = { | static PyMethodDef float_methods[] = { | ||||||
|   	{"conjugate",	(PyCFunction)float_float,	METH_NOARGS, |   	{"conjugate",	(PyCFunction)float_float,	METH_NOARGS, | ||||||
| 	 "Returns self, the complex conjugate of any float."}, | 	 "Returns self, the complex conjugate of any float."}, | ||||||
|  | @ -1446,6 +1487,8 @@ static PyMethodDef float_methods[] = { | ||||||
| 	 METH_O|METH_CLASS,		float_getformat_doc}, | 	 METH_O|METH_CLASS,		float_getformat_doc}, | ||||||
| 	{"__setformat__",	(PyCFunction)float_setformat,	 | 	{"__setformat__",	(PyCFunction)float_setformat,	 | ||||||
| 	 METH_VARARGS|METH_CLASS,	float_setformat_doc}, | 	 METH_VARARGS|METH_CLASS,	float_setformat_doc}, | ||||||
|  |         {"__format__",          (PyCFunction)float__format__, | ||||||
|  |          METH_VARARGS,                  float__format__doc}, | ||||||
| 	{NULL,		NULL}		/* sentinel */ | 	{NULL,		NULL}		/* sentinel */ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ | ||||||
| 
 | 
 | ||||||
| #include "Python.h" | #include "Python.h" | ||||||
| #include <ctype.h> | #include <ctype.h> | ||||||
|  | #include "formatter_string.h" | ||||||
| 
 | 
 | ||||||
| static PyObject *int_int(PyIntObject *v); | 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); | 	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[] = { | static PyMethodDef int_methods[] = { | ||||||
| 	{"conjugate",	(PyCFunction)int_int,	METH_NOARGS, | 	{"conjugate",	(PyCFunction)int_int,	METH_NOARGS, | ||||||
| 	 "Returns self, the complex conjugate of any int."}, | 	 "Returns self, the complex conjugate of any int."}, | ||||||
| 	{"__trunc__",	(PyCFunction)int_int,	METH_NOARGS, | 	{"__trunc__",	(PyCFunction)int_int,	METH_NOARGS, | ||||||
|          "Truncating an Integral returns itself."}, |          "Truncating an Integral returns itself."}, | ||||||
| 	{"__getnewargs__",	(PyCFunction)int_getnewargs,	METH_NOARGS}, | 	{"__getnewargs__",	(PyCFunction)int_getnewargs,	METH_NOARGS}, | ||||||
|  |         {"__format__", (PyCFunction)int__format__, METH_VARARGS}, | ||||||
| 	{NULL,		NULL}		/* sentinel */ | 	{NULL,		NULL}		/* sentinel */ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ | ||||||
| 
 | 
 | ||||||
| #include "Python.h" | #include "Python.h" | ||||||
| #include "longintrepr.h" | #include "longintrepr.h" | ||||||
|  | #include "formatter_string.h" | ||||||
| 
 | 
 | ||||||
| #include <ctype.h> | #include <ctype.h> | ||||||
| 
 | 
 | ||||||
|  | @ -3380,12 +3381,47 @@ long_getN(PyLongObject *v, void *context) { | ||||||
| 	return PyLong_FromLong((intptr_t)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[] = { | static PyMethodDef long_methods[] = { | ||||||
| 	{"conjugate",	(PyCFunction)long_long,	METH_NOARGS, | 	{"conjugate",	(PyCFunction)long_long,	METH_NOARGS, | ||||||
| 	 "Returns self, the complex conjugate of any long."}, | 	 "Returns self, the complex conjugate of any long."}, | ||||||
| 	{"__trunc__",	(PyCFunction)long_long,	METH_NOARGS, | 	{"__trunc__",	(PyCFunction)long_long,	METH_NOARGS, | ||||||
|          "Truncating an Integral returns itself."}, |          "Truncating an Integral returns itself."}, | ||||||
| 	{"__getnewargs__",	(PyCFunction)long_getnewargs,	METH_NOARGS}, | 	{"__getnewargs__",	(PyCFunction)long_getnewargs,	METH_NOARGS}, | ||||||
|  |         {"__format__", (PyCFunction)long__format__, METH_VARARGS}, | ||||||
| 	{NULL,		NULL}		/* sentinel */ | 	{NULL,		NULL}		/* sentinel */ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										980
									
								
								Objects/stringlib/formatter.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										980
									
								
								Objects/stringlib/formatter.h
									
										
									
									
									
										Normal 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 */ | ||||||
							
								
								
									
										1214
									
								
								Objects/stringlib/string_format.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1214
									
								
								Objects/stringlib/string_format.h
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										27
									
								
								Objects/stringlib/stringdefs.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								Objects/stringlib/stringdefs.h
									
										
									
									
									
										Normal 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 */ | ||||||
							
								
								
									
										52
									
								
								Objects/stringlib/unicodedefs.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								Objects/stringlib/unicodedefs.h
									
										
									
									
									
										Normal 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 */ | ||||||
|  | @ -4,6 +4,8 @@ | ||||||
| 
 | 
 | ||||||
| #include "Python.h" | #include "Python.h" | ||||||
| 
 | 
 | ||||||
|  | #include "formatter_string.h" | ||||||
|  | 
 | ||||||
| #include <ctype.h> | #include <ctype.h> | ||||||
| 
 | 
 | ||||||
| #ifdef COUNT_ALLOCS | #ifdef COUNT_ALLOCS | ||||||
|  | @ -771,15 +773,7 @@ PyString_AsStringAndSize(register PyObject *obj, | ||||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||||
| /* Methods */ | /* Methods */ | ||||||
| 
 | 
 | ||||||
| #define STRINGLIB_CHAR char | #include "stringlib/stringdefs.h" | ||||||
| 
 |  | ||||||
| #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/fastsearch.h" | #include "stringlib/fastsearch.h" | ||||||
| 
 | 
 | ||||||
| #include "stringlib/count.h" | #include "stringlib/count.h" | ||||||
|  | @ -3910,6 +3904,19 @@ string_getnewargs(PyStringObject *v) | ||||||
| 	return Py_BuildValue("(s#)", v->ob_sval, Py_SIZE(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 | static PyMethodDef | ||||||
| string_methods[] = { | string_methods[] = { | ||||||
|  | @ -3954,6 +3961,10 @@ string_methods[] = { | ||||||
| 	{"rjust", (PyCFunction)string_rjust, METH_VARARGS, rjust__doc__}, | 	{"rjust", (PyCFunction)string_rjust, METH_VARARGS, rjust__doc__}, | ||||||
| 	{"center", (PyCFunction)string_center, METH_VARARGS, center__doc__}, | 	{"center", (PyCFunction)string_center, METH_VARARGS, center__doc__}, | ||||||
| 	{"zfill", (PyCFunction)string_zfill, METH_VARARGS, zfill__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__}, | 	{"encode", (PyCFunction)string_encode, METH_VARARGS, encode__doc__}, | ||||||
| 	{"decode", (PyCFunction)string_decode, METH_VARARGS, decode__doc__}, | 	{"decode", (PyCFunction)string_decode, METH_VARARGS, decode__doc__}, | ||||||
| 	{"expandtabs", (PyCFunction)string_expandtabs, METH_VARARGS, | 	{"expandtabs", (PyCFunction)string_expandtabs, METH_VARARGS, | ||||||
|  |  | ||||||
|  | @ -3210,11 +3210,57 @@ object_reduce_ex(PyObject *self, PyObject *args) | ||||||
| 	return _common_reduce(self, proto); | 	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[] = { | static PyMethodDef object_methods[] = { | ||||||
| 	{"__reduce_ex__", object_reduce_ex, METH_VARARGS, | 	{"__reduce_ex__", object_reduce_ex, METH_VARARGS, | ||||||
| 	 PyDoc_STR("helper for pickle")}, | 	 PyDoc_STR("helper for pickle")}, | ||||||
| 	{"__reduce__", object_reduce, METH_VARARGS, | 	{"__reduce__", object_reduce, METH_VARARGS, | ||||||
| 	 PyDoc_STR("helper for pickle")}, | 	 PyDoc_STR("helper for pickle")}, | ||||||
|  |         {"__format__", object_format, METH_VARARGS, | ||||||
|  |          PyDoc_STR("default object formatter")}, | ||||||
| 	{0} | 	{0} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -42,6 +42,8 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||||
| #define PY_SSIZE_T_CLEAN | #define PY_SSIZE_T_CLEAN | ||||||
| #include "Python.h" | #include "Python.h" | ||||||
| 
 | 
 | ||||||
|  | #include "formatter_unicode.h" | ||||||
|  | 
 | ||||||
| #include "unicodeobject.h" | #include "unicodeobject.h" | ||||||
| #include "ucnhash.h" | #include "ucnhash.h" | ||||||
| 
 | 
 | ||||||
|  | @ -5059,21 +5061,8 @@ int PyUnicode_EncodeDecimal(Py_UNICODE *s, | ||||||
| 
 | 
 | ||||||
| /* --- Helpers ------------------------------------------------------------ */ | /* --- 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 | #define FROM_UNICODE | ||||||
| 
 | 
 | ||||||
| #include "stringlib/fastsearch.h" | #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 * | static PyObject * | ||||||
| unicode_getnewargs(PyUnicodeObject *v) | unicode_getnewargs(PyUnicodeObject *v) | ||||||
|  | @ -7855,6 +7857,10 @@ static PyMethodDef unicode_methods[] = { | ||||||
|     {"isalpha", (PyCFunction) unicode_isalpha, METH_NOARGS, isalpha__doc__}, |     {"isalpha", (PyCFunction) unicode_isalpha, METH_NOARGS, isalpha__doc__}, | ||||||
|     {"isalnum", (PyCFunction) unicode_isalnum, METH_NOARGS, isalnum__doc__}, |     {"isalnum", (PyCFunction) unicode_isalnum, METH_NOARGS, isalnum__doc__}, | ||||||
|     {"zfill", (PyCFunction) unicode_zfill, METH_VARARGS, zfill__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 | #if 0 | ||||||
|     {"capwords", (PyCFunction) unicode_capwords, METH_NOARGS, capwords__doc__}, |     {"capwords", (PyCFunction) unicode_capwords, METH_NOARGS, capwords__doc__}, | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -338,6 +338,24 @@ PyDoc_STRVAR(filter_doc, | ||||||
| "function is None, return the items that are true.  If sequence is a tuple\n" | "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."); | "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 * | static PyObject * | ||||||
| builtin_chr(PyObject *self, PyObject *args) | builtin_chr(PyObject *self, PyObject *args) | ||||||
| { | { | ||||||
|  | @ -2359,6 +2377,7 @@ static PyMethodDef builtin_methods[] = { | ||||||
|  	{"eval",	builtin_eval,       METH_VARARGS, eval_doc}, |  	{"eval",	builtin_eval,       METH_VARARGS, eval_doc}, | ||||||
|  	{"execfile",	builtin_execfile,   METH_VARARGS, execfile_doc}, |  	{"execfile",	builtin_execfile,   METH_VARARGS, execfile_doc}, | ||||||
|  	{"filter",	builtin_filter,     METH_VARARGS, filter_doc}, |  	{"filter",	builtin_filter,     METH_VARARGS, filter_doc}, | ||||||
|  |  	{"format",	builtin_format,     METH_VARARGS, format_doc}, | ||||||
|  	{"getattr",	builtin_getattr,    METH_VARARGS, getattr_doc}, |  	{"getattr",	builtin_getattr,    METH_VARARGS, getattr_doc}, | ||||||
|  	{"globals",	(PyCFunction)builtin_globals,    METH_NOARGS, globals_doc}, |  	{"globals",	(PyCFunction)builtin_globals,    METH_NOARGS, globals_doc}, | ||||||
|  	{"hasattr",	builtin_hasattr,    METH_VARARGS, hasattr_doc}, |  	{"hasattr",	builtin_hasattr,    METH_VARARGS, hasattr_doc}, | ||||||
|  |  | ||||||
							
								
								
									
										15
									
								
								Python/formatter_string.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								Python/formatter_string.c
									
										
									
									
									
										Normal 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" | ||||||
							
								
								
									
										13
									
								
								Python/formatter_unicode.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								Python/formatter_unicode.c
									
										
									
									
									
										Normal 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" | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Eric Smith
						Eric Smith