mirror of https://github.com/python/cpython.git
Merged revisions 70364 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk ........ r70364 | eric.smith | 2009-03-14 07:57:26 -0400 (Sat, 14 Mar 2009) | 17 lines Issue 5237, Allow auto-numbered replacement fields in str.format() strings. For simple uses for str.format(), this makes the typing easier. Hopfully this will help in the adoption of str.format(). For example: 'The {} is {}'.format('sky', 'blue') You can mix and matcth auto-numbering and named replacement fields: 'The {} is {color}'.format('sky', color='blue') But you can't mix and match auto-numbering and specified numbering: 'The {0} is {}'.format('sky', 'blue') ValueError: cannot switch from manual field specification to automatic field numbering Will port to 3.1. ........
This commit is contained in:
parent
350370c25f
commit
8ec90443f5
|
@ -683,9 +683,9 @@ def __format__(self, format_spec):
|
|||
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)
|
||||
self.assertRaises(IndexError, "{:}".format)
|
||||
self.assertRaises(IndexError, "{:s}".format)
|
||||
self.assertRaises(IndexError, "{}".format)
|
||||
|
||||
# can't have a replacement on the field name portion
|
||||
self.assertRaises(TypeError, '{0[{1}]}'.format, 'abcdefg', 4)
|
||||
|
@ -704,6 +704,36 @@ def __format__(self, format_spec):
|
|||
self.assertRaises(ValueError, format, '', '#')
|
||||
self.assertRaises(ValueError, format, '', '#20')
|
||||
|
||||
def test_format_auto_numbering(self):
|
||||
class C:
|
||||
def __init__(self, x=100):
|
||||
self._x = x
|
||||
def __format__(self, spec):
|
||||
return spec
|
||||
|
||||
self.assertEqual('{}'.format(10), '10')
|
||||
self.assertEqual('{:5}'.format('s'), 's ')
|
||||
self.assertEqual('{!r}'.format('s'), "'s'")
|
||||
self.assertEqual('{._x}'.format(C(10)), '10')
|
||||
self.assertEqual('{[1]}'.format([1, 2]), '2')
|
||||
self.assertEqual('{[a]}'.format({'a':4, 'b':2}), '4')
|
||||
self.assertEqual('a{}b{}c'.format(0, 1), 'a0b1c')
|
||||
|
||||
self.assertEqual('a{:{}}b'.format('x', '^10'), 'a x b')
|
||||
self.assertEqual('a{:{}x}b'.format(20, '#'), 'a0x14b')
|
||||
|
||||
# can't mix and match numbering and auto-numbering
|
||||
self.assertRaises(ValueError, '{}{1}'.format, 1, 2)
|
||||
self.assertRaises(ValueError, '{1}{}'.format, 1, 2)
|
||||
self.assertRaises(ValueError, '{:{1}}'.format, 1, 2)
|
||||
self.assertRaises(ValueError, '{0:{}}'.format, 1, 2)
|
||||
|
||||
# can mix and match auto-numbering and named
|
||||
self.assertEqual('{f}{}'.format(4, f='test'), 'test4')
|
||||
self.assertEqual('{}{f}'.format(4, f='test'), '4test')
|
||||
self.assertEqual('{:{f}}{g}{}'.format(1, 3, g='g', f=2), ' 1g3')
|
||||
self.assertEqual('{f:{}}{}{g}'.format(2, 4, f=1, g='g'), ' 14g')
|
||||
|
||||
def test_formatting(self):
|
||||
string_tests.MixinStrUnicodeUserStringTest.test_formatting(self)
|
||||
# Testing Unicode formatting strings...
|
||||
|
|
|
@ -12,6 +12,9 @@ What's New in Python 3.1 alpha 2?
|
|||
Core and Builtins
|
||||
-----------------
|
||||
|
||||
- Issue #5237: Allow auto-numbered fields in str.format(). For
|
||||
example: '{} {}'.format(1, 2) == '1 2'.
|
||||
|
||||
- Issue #5392: when a very low recursion limit was set, the interpreter would
|
||||
abort with a fatal error after the recursion limit was hit twice.
|
||||
|
||||
|
@ -33,6 +36,7 @@ What's New in Python 3.1 alpha 1
|
|||
Core and Builtins
|
||||
-----------------
|
||||
|
||||
=======
|
||||
- The io module has been reimplemented in C for speed.
|
||||
|
||||
- Give dict views an informative __repr__.
|
||||
|
|
|
@ -31,10 +31,23 @@ typedef struct {
|
|||
} SubString;
|
||||
|
||||
|
||||
typedef enum {
|
||||
ANS_INIT,
|
||||
ANS_AUTO,
|
||||
ANS_MANUAL,
|
||||
} AutoNumberState; /* Keep track if we're auto-numbering fields */
|
||||
|
||||
/* Keeps track of our auto-numbering state, and which number field we're on */
|
||||
typedef struct {
|
||||
AutoNumberState an_state;
|
||||
int an_field_number;
|
||||
} AutoNumber;
|
||||
|
||||
|
||||
/* forward declaration for recursion */
|
||||
static PyObject *
|
||||
build_string(SubString *input, PyObject *args, PyObject *kwargs,
|
||||
int recursion_depth);
|
||||
int recursion_depth, AutoNumber *auto_number);
|
||||
|
||||
|
||||
|
||||
|
@ -42,6 +55,13 @@ build_string(SubString *input, PyObject *args, PyObject *kwargs,
|
|||
/************************** Utility functions ************************/
|
||||
/************************************************************************/
|
||||
|
||||
static void
|
||||
AutoNumber_Init(AutoNumber *auto_number)
|
||||
{
|
||||
auto_number->an_state = ANS_INIT;
|
||||
auto_number->an_field_number = 0;
|
||||
}
|
||||
|
||||
/* fill in a SubString from a pointer and length */
|
||||
Py_LOCAL_INLINE(void)
|
||||
SubString_init(SubString *str, STRINGLIB_CHAR *p, Py_ssize_t len)
|
||||
|
@ -74,6 +94,32 @@ SubString_new_object_or_empty(SubString *str)
|
|||
return STRINGLIB_NEW(str->ptr, str->end - str->ptr);
|
||||
}
|
||||
|
||||
/* Return 1 if an error has been detected switching between automatic
|
||||
field numbering and manual field specification, else return 0. Set
|
||||
ValueError on error. */
|
||||
static int
|
||||
autonumber_state_error(AutoNumberState state, int field_name_is_empty)
|
||||
{
|
||||
if (state == ANS_MANUAL) {
|
||||
if (field_name_is_empty) {
|
||||
PyErr_SetString(PyExc_ValueError, "cannot switch from "
|
||||
"manual field specification to "
|
||||
"automatic field numbering");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!field_name_is_empty) {
|
||||
PyErr_SetString(PyExc_ValueError, "cannot switch from "
|
||||
"automatic field numbering to "
|
||||
"manual field specification");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/*********** Output string management functions ****************/
|
||||
/************************************************************************/
|
||||
|
@ -352,11 +398,14 @@ FieldNameIterator_next(FieldNameIterator *self, int *is_attribute,
|
|||
*/
|
||||
static int
|
||||
field_name_split(STRINGLIB_CHAR *ptr, Py_ssize_t len, SubString *first,
|
||||
Py_ssize_t *first_idx, FieldNameIterator *rest)
|
||||
Py_ssize_t *first_idx, FieldNameIterator *rest,
|
||||
AutoNumber *auto_number)
|
||||
{
|
||||
STRINGLIB_CHAR c;
|
||||
STRINGLIB_CHAR *p = ptr;
|
||||
STRINGLIB_CHAR *end = ptr + len;
|
||||
int field_name_is_empty;
|
||||
int using_numeric_index;
|
||||
|
||||
/* find the part up until the first '.' or '[' */
|
||||
while (p < end) {
|
||||
|
@ -380,15 +429,41 @@ field_name_split(STRINGLIB_CHAR *ptr, Py_ssize_t len, SubString *first,
|
|||
/* see if "first" is an integer, in which case it's used as an index */
|
||||
*first_idx = get_integer(first);
|
||||
|
||||
/* zero length string is an error */
|
||||
if (first->ptr >= first->end) {
|
||||
PyErr_SetString(PyExc_ValueError, "empty field name");
|
||||
goto error;
|
||||
field_name_is_empty = first->ptr >= first->end;
|
||||
|
||||
/* If the field name is omitted or if we have a numeric index
|
||||
specified, then we're doing numeric indexing into args. */
|
||||
using_numeric_index = field_name_is_empty || *first_idx != -1;
|
||||
|
||||
/* We always get here exactly one time for each field we're
|
||||
processing. And we get here in field order (counting by left
|
||||
braces). So this is the perfect place to handle automatic field
|
||||
numbering if the field name is omitted. */
|
||||
|
||||
/* Check if we need to do the auto-numbering. It's not needed if
|
||||
we're called from string.Format routines, because it's handled
|
||||
in that class by itself. */
|
||||
if (auto_number) {
|
||||
/* Initialize our auto numbering state if this is the first
|
||||
time we're either auto-numbering or manually numbering. */
|
||||
if (auto_number->an_state == ANS_INIT && using_numeric_index)
|
||||
auto_number->an_state = field_name_is_empty ?
|
||||
ANS_AUTO : ANS_MANUAL;
|
||||
|
||||
/* Make sure our state is consistent with what we're doing
|
||||
this time through. Only check if we're using a numeric
|
||||
index. */
|
||||
if (using_numeric_index)
|
||||
if (autonumber_state_error(auto_number->an_state,
|
||||
field_name_is_empty))
|
||||
return 0;
|
||||
/* Zero length field means we want to do auto-numbering of the
|
||||
fields. */
|
||||
if (field_name_is_empty)
|
||||
*first_idx = (auto_number->an_field_number)++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
error:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -398,7 +473,8 @@ field_name_split(STRINGLIB_CHAR *ptr, Py_ssize_t len, SubString *first,
|
|||
the entire input string.
|
||||
*/
|
||||
static PyObject *
|
||||
get_field_object(SubString *input, PyObject *args, PyObject *kwargs)
|
||||
get_field_object(SubString *input, PyObject *args, PyObject *kwargs,
|
||||
AutoNumber *auto_number)
|
||||
{
|
||||
PyObject *obj = NULL;
|
||||
int ok;
|
||||
|
@ -409,7 +485,7 @@ get_field_object(SubString *input, PyObject *args, PyObject *kwargs)
|
|||
FieldNameIterator rest;
|
||||
|
||||
if (!field_name_split(input->ptr, input->end - input->ptr, &first,
|
||||
&index, &rest)) {
|
||||
&index, &rest, auto_number)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
@ -548,14 +624,18 @@ static int
|
|||
parse_field(SubString *str, SubString *field_name, SubString *format_spec,
|
||||
STRINGLIB_CHAR *conversion)
|
||||
{
|
||||
/* Note this function works if the field name is zero length,
|
||||
which is good. Zero length field names are handled later, in
|
||||
field_name_split. */
|
||||
|
||||
STRINGLIB_CHAR c = 0;
|
||||
|
||||
/* initialize these, as they may be empty */
|
||||
*conversion = '\0';
|
||||
SubString_init(format_spec, NULL, 0);
|
||||
|
||||
/* search for the field name. it's terminated by the end of the
|
||||
string, or a ':' or '!' */
|
||||
/* Search for the field name. it's terminated by the end of
|
||||
the string, or a ':' or '!' */
|
||||
field_name->ptr = str->ptr;
|
||||
while (str->ptr < str->end) {
|
||||
switch (c = *(str->ptr++)) {
|
||||
|
@ -598,15 +678,12 @@ parse_field(SubString *str, SubString *field_name, SubString *format_spec,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
else {
|
||||
else
|
||||
/* end of string, there's no format_spec or conversion */
|
||||
field_name->end = str->ptr;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
|
@ -633,8 +710,8 @@ MarkupIterator_init(MarkupIterator *self, STRINGLIB_CHAR *ptr, Py_ssize_t len)
|
|||
string (or something to be expanded) */
|
||||
static int
|
||||
MarkupIterator_next(MarkupIterator *self, SubString *literal,
|
||||
SubString *field_name, SubString *format_spec,
|
||||
STRINGLIB_CHAR *conversion,
|
||||
int *field_present, SubString *field_name,
|
||||
SubString *format_spec, STRINGLIB_CHAR *conversion,
|
||||
int *format_spec_needs_expanding)
|
||||
{
|
||||
int at_end;
|
||||
|
@ -650,6 +727,7 @@ MarkupIterator_next(MarkupIterator *self, SubString *literal,
|
|||
SubString_init(format_spec, NULL, 0);
|
||||
*conversion = '\0';
|
||||
*format_spec_needs_expanding = 0;
|
||||
*field_present = 0;
|
||||
|
||||
/* No more input, end of iterator. This is the normal exit
|
||||
path. */
|
||||
|
@ -711,6 +789,7 @@ MarkupIterator_next(MarkupIterator *self, SubString *literal,
|
|||
/* this is markup, find the end of the string by counting nested
|
||||
braces. note that this prohibits escaped braces, so that
|
||||
format_specs cannot have braces in them. */
|
||||
*field_present = 1;
|
||||
count = 1;
|
||||
|
||||
start = self->str.ptr;
|
||||
|
@ -735,13 +814,6 @@ MarkupIterator_next(MarkupIterator *self, SubString *literal,
|
|||
if (parse_field(&s, field_name, format_spec, conversion) == 0)
|
||||
return 0;
|
||||
|
||||
/* a zero length field_name is an error */
|
||||
if (field_name->ptr == field_name->end) {
|
||||
PyErr_SetString(PyExc_ValueError, "zero length field name "
|
||||
"in format");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* success */
|
||||
return 2;
|
||||
}
|
||||
|
@ -793,13 +865,17 @@ do_conversion(PyObject *obj, STRINGLIB_CHAR conversion)
|
|||
compute the result and write it to output.
|
||||
format_spec_needs_expanding is an optimization. if it's false,
|
||||
just output the string directly, otherwise recursively expand the
|
||||
format_spec string. */
|
||||
format_spec string.
|
||||
|
||||
field_name is allowed to be zero length, in which case we
|
||||
are doing auto field numbering.
|
||||
*/
|
||||
|
||||
static int
|
||||
output_markup(SubString *field_name, SubString *format_spec,
|
||||
int format_spec_needs_expanding, STRINGLIB_CHAR conversion,
|
||||
OutputString *output, PyObject *args, PyObject *kwargs,
|
||||
int recursion_depth)
|
||||
int recursion_depth, AutoNumber *auto_number)
|
||||
{
|
||||
PyObject *tmp = NULL;
|
||||
PyObject *fieldobj = NULL;
|
||||
|
@ -808,7 +884,7 @@ output_markup(SubString *field_name, SubString *format_spec,
|
|||
int result = 0;
|
||||
|
||||
/* convert field_name to an object */
|
||||
fieldobj = get_field_object(field_name, args, kwargs);
|
||||
fieldobj = get_field_object(field_name, args, kwargs, auto_number);
|
||||
if (fieldobj == NULL)
|
||||
goto done;
|
||||
|
||||
|
@ -825,7 +901,8 @@ output_markup(SubString *field_name, SubString *format_spec,
|
|||
|
||||
/* if needed, recurively compute the format_spec */
|
||||
if (format_spec_needs_expanding) {
|
||||
tmp = build_string(format_spec, args, kwargs, recursion_depth-1);
|
||||
tmp = build_string(format_spec, args, kwargs, recursion_depth-1,
|
||||
auto_number);
|
||||
if (tmp == NULL)
|
||||
goto done;
|
||||
|
||||
|
@ -859,26 +936,28 @@ output_markup(SubString *field_name, SubString *format_spec,
|
|||
*/
|
||||
static int
|
||||
do_markup(SubString *input, PyObject *args, PyObject *kwargs,
|
||||
OutputString *output, int recursion_depth)
|
||||
OutputString *output, int recursion_depth, AutoNumber *auto_number)
|
||||
{
|
||||
MarkupIterator iter;
|
||||
int format_spec_needs_expanding;
|
||||
int result;
|
||||
int field_present;
|
||||
SubString literal;
|
||||
SubString field_name;
|
||||
SubString format_spec;
|
||||
STRINGLIB_CHAR conversion;
|
||||
|
||||
MarkupIterator_init(&iter, input->ptr, input->end - input->ptr);
|
||||
while ((result = MarkupIterator_next(&iter, &literal, &field_name,
|
||||
&format_spec, &conversion,
|
||||
while ((result = MarkupIterator_next(&iter, &literal, &field_present,
|
||||
&field_name, &format_spec,
|
||||
&conversion,
|
||||
&format_spec_needs_expanding)) == 2) {
|
||||
if (!output_data(output, literal.ptr, literal.end - literal.ptr))
|
||||
return 0;
|
||||
if (field_name.ptr != field_name.end)
|
||||
if (field_present)
|
||||
if (!output_markup(&field_name, &format_spec,
|
||||
format_spec_needs_expanding, conversion, output,
|
||||
args, kwargs, recursion_depth))
|
||||
args, kwargs, recursion_depth, auto_number))
|
||||
return 0;
|
||||
}
|
||||
return result;
|
||||
|
@ -891,7 +970,7 @@ do_markup(SubString *input, PyObject *args, PyObject *kwargs,
|
|||
*/
|
||||
static PyObject *
|
||||
build_string(SubString *input, PyObject *args, PyObject *kwargs,
|
||||
int recursion_depth)
|
||||
int recursion_depth, AutoNumber *auto_number)
|
||||
{
|
||||
OutputString output;
|
||||
PyObject *result = NULL;
|
||||
|
@ -913,7 +992,8 @@ build_string(SubString *input, PyObject *args, PyObject *kwargs,
|
|||
INITIAL_SIZE_INCREMENT))
|
||||
goto done;
|
||||
|
||||
if (!do_markup(input, args, kwargs, &output, recursion_depth)) {
|
||||
if (!do_markup(input, args, kwargs, &output, recursion_depth,
|
||||
auto_number)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
@ -947,8 +1027,11 @@ do_string_format(PyObject *self, PyObject *args, PyObject *kwargs)
|
|||
*/
|
||||
int recursion_depth = 2;
|
||||
|
||||
AutoNumber auto_number;
|
||||
|
||||
AutoNumber_Init(&auto_number);
|
||||
SubString_init(&input, STRINGLIB_STR(self), STRINGLIB_LEN(self));
|
||||
return build_string(&input, args, kwargs, recursion_depth);
|
||||
return build_string(&input, args, kwargs, recursion_depth, &auto_number);
|
||||
}
|
||||
|
||||
|
||||
|
@ -993,8 +1076,9 @@ formatteriter_next(formatteriterobject *it)
|
|||
SubString format_spec;
|
||||
STRINGLIB_CHAR conversion;
|
||||
int format_spec_needs_expanding;
|
||||
int result = MarkupIterator_next(&it->it_markup, &literal, &field_name,
|
||||
&format_spec, &conversion,
|
||||
int field_present;
|
||||
int result = MarkupIterator_next(&it->it_markup, &literal, &field_present,
|
||||
&field_name, &format_spec, &conversion,
|
||||
&format_spec_needs_expanding);
|
||||
|
||||
/* all of the SubString objects point into it->str, so no
|
||||
|
@ -1009,7 +1093,6 @@ formatteriter_next(formatteriterobject *it)
|
|||
PyObject *format_spec_str = NULL;
|
||||
PyObject *conversion_str = NULL;
|
||||
PyObject *tuple = NULL;
|
||||
int has_field = field_name.ptr != field_name.end;
|
||||
|
||||
literal_str = SubString_new_object(&literal);
|
||||
if (literal_str == NULL)
|
||||
|
@ -1021,7 +1104,7 @@ formatteriter_next(formatteriterobject *it)
|
|||
|
||||
/* if field_name is non-zero length, return a string for
|
||||
format_spec (even if zero length), else return None */
|
||||
format_spec_str = (has_field ?
|
||||
format_spec_str = (field_present ?
|
||||
SubString_new_object_or_empty :
|
||||
SubString_new_object)(&format_spec);
|
||||
if (format_spec_str == NULL)
|
||||
|
@ -1245,9 +1328,11 @@ formatter_field_name_split(STRINGLIB_OBJECT *self)
|
|||
Py_INCREF(self);
|
||||
it->str = self;
|
||||
|
||||
/* Pass in auto_number = NULL. We'll return an empty string for
|
||||
first_obj in that case. */
|
||||
if (!field_name_split(STRINGLIB_STR(self),
|
||||
STRINGLIB_LEN(self),
|
||||
&first, &first_idx, &it->it_field))
|
||||
&first, &first_idx, &it->it_field, NULL))
|
||||
goto done;
|
||||
|
||||
/* first becomes an integer, if possible; else a string */
|
||||
|
|
Loading…
Reference in New Issue