Index: Objects/stringlib/string_format.h =================================================================== --- Objects/stringlib/string_format.h (revisiĆ³n: 82074) +++ Objects/stringlib/string_format.h (copia de trabajo) @@ -192,22 +192,38 @@ /*********** Format string parsing -- integers and identifiers *********/ /************************************************************************/ -static Py_ssize_t -get_integer(const SubString *str) +/* + Try to parse an integer from a SubString. + Valid integers matches: (+|-)([0-9]+) + Returns 0 if parsing failed, 1 if successed. +*/ +static int +get_integer(const SubString *str, Py_ssize_t *result) { Py_ssize_t accumulator = 0; + Py_ssize_t sign = 1; Py_ssize_t digitval; Py_ssize_t oldaccumulator; STRINGLIB_CHAR *p; /* empty string is an error */ if (str->ptr >= str->end) - return -1; + return 0; - for (p = str->ptr; p < str->end; p++) { + /* take care of the sign, if present */ + p = str->ptr; + switch(*p) { + case '-': + sign = -1; + case '+': + ++p; + break; + } + + for ( ; p < str->end; p++) { digitval = STRINGLIB_TODECIMAL(*p); if (digitval < 0) - return -1; + return 0; /* This trick was copied from old Unicode format code. It's cute, but would really suck on an old machine with a slow divide @@ -219,11 +235,12 @@ if ((accumulator+10)/10 != oldaccumulator+1) { PyErr_Format(PyExc_ValueError, "Too many decimal digits in format string"); - return -1; + return 0; } accumulator += digitval; } - return accumulator; + *result = sign * accumulator; + return 1; } /************************************************************************/ @@ -355,7 +372,8 @@ /* returns 0 on error, 1 on non-error termination, and 2 if it returns a value */ static int FieldNameIterator_next(FieldNameIterator *self, int *is_attribute, - Py_ssize_t *name_idx, SubString *name) + int *index_is_an_integer, Py_ssize_t *name_idx, + SubString *name) { /* check at end of input */ if (self->ptr >= self->str.end) @@ -367,13 +385,14 @@ if (_FieldNameIterator_attr(self, name) == 0) return 0; *name_idx = -1; + *index_is_an_integer = 0; break; case '[': *is_attribute = 0; if (_FieldNameIterator_item(self, name) == 0) return 0; - *name_idx = get_integer(name); - if (*name_idx == -1 && PyErr_Occurred()) + *index_is_an_integer = get_integer(name, name_idx); + if (*index_is_an_integer == 0 && PyErr_Occurred()) return 0; break; default: @@ -395,14 +414,15 @@ /* input: field_name output: 'first' points to the part before the first '[' or '.' - 'first_idx' is -1 if 'first' is not an integer, otherwise - it's the value of first converted to an integer + 'first_idx_is_an_integer' is 1 if 'first' is an integer, 0 if not. + 'first_idx' is the value of first converted to an integer (only if + 'first_idx_is_an_integer' is 1, -1 otherwise) 'rest' is an iterator to return the rest */ static int field_name_split(STRINGLIB_CHAR *ptr, Py_ssize_t len, SubString *first, - Py_ssize_t *first_idx, FieldNameIterator *rest, - AutoNumber *auto_number) + int *first_idx_is_an_integer, Py_ssize_t *first_idx, + FieldNameIterator *rest, AutoNumber *auto_number) { STRINGLIB_CHAR c; STRINGLIB_CHAR *p = ptr; @@ -430,15 +450,15 @@ FieldNameIterator_init(rest, p, end - p); /* see if "first" is an integer, in which case it's used as an index */ - *first_idx = get_integer(first); - if (*first_idx == -1 && PyErr_Occurred()) + *first_idx_is_an_integer = get_integer(first, first_idx); + if (*first_idx_is_an_integer == 0 && PyErr_Occurred()) return 0; 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; + using_numeric_index = field_name_is_empty || *first_idx_is_an_integer; /* We always get here exactly one time for each field we're processing. And we get here in field order (counting by left @@ -464,8 +484,10 @@ return 0; /* Zero length field means we want to do auto-numbering of the fields. */ - if (field_name_is_empty) + if (field_name_is_empty) { *first_idx = (auto_number->an_field_number)++; + *first_idx_is_an_integer = 1; + } } return 1; @@ -486,15 +508,16 @@ int is_attribute; SubString name; SubString first; + int index_is_an_integer; Py_ssize_t index; FieldNameIterator rest; if (!field_name_split(input->ptr, input->end - input->ptr, &first, - &index, &rest, auto_number)) { + &index_is_an_integer, &index, &rest, auto_number)) { goto error; } - if (index == -1) { + if (index_is_an_integer == 0) { /* look up in kwargs */ PyObject *key = SubString_new_object(&first); if (key == NULL) @@ -515,7 +538,8 @@ } /* iterate over the rest of the field_name */ - while ((ok = FieldNameIterator_next(&rest, &is_attribute, &index, + while ((ok = FieldNameIterator_next(&rest, &is_attribute, + &index_is_an_integer, &index, &name)) == 2) { PyObject *tmp; @@ -524,7 +548,7 @@ tmp = getattr(obj, &name); else /* getitem lookup "[]" */ - if (index == -1) + if (index_is_an_integer == 0) tmp = getitem_str(obj, &name); else if (PySequence_Check(obj)) @@ -1241,11 +1265,12 @@ { int result; int is_attr; + int index_is_int; Py_ssize_t idx; SubString name; result = FieldNameIterator_next(&it->it_field, &is_attr, - &idx, &name); + &index_is_int, &idx, &name); if (result == 0 || result == 1) /* if 0, error has already been set, if 1, iterator is empty */ return NULL; @@ -1259,7 +1284,7 @@ goto done; /* either an integer or a string */ - if (idx != -1) + if (index_is_int) obj = PyLong_FromSsize_t(idx); else obj = SubString_new_object(&name); @@ -1324,6 +1349,7 @@ { SubString first; Py_ssize_t first_idx; + int first_idx_is_int; fieldnameiterobject *it; PyObject *first_obj = NULL; @@ -1342,11 +1368,12 @@ first_obj in that case. */ if (!field_name_split(STRINGLIB_STR(self), STRINGLIB_LEN(self), - &first, &first_idx, &it->it_field, NULL)) + &first, &first_idx_is_int, &first_idx, + &it->it_field, NULL)) goto done; /* first becomes an integer, if possible; else a string */ - if (first_idx != -1) + if (first_idx_is_int) first_obj = PyLong_FromSsize_t(first_idx); else /* convert "first" into a string object */ Index: Lib/test/test_str.py =================================================================== --- Lib/test/test_str.py (revisiĆ³n: 82074) +++ Lib/test/test_str.py (copia de trabajo) @@ -368,6 +368,16 @@ self.assertRaises(ValueError, format, "", "-") self.assertRaises(ValueError, "{0:=s}".format, '') + # issue 7951 + self.assertEqual('{0[-1]}'.format(['abc', 'def']), 'def') + self.assertEqual('{0[-2]}'.format(['abc', 'def']), 'abc') + self.assertEqual('{0[-1][-1]}'.format(['abc', ['def']]), 'def') + self.assertEqual('{0[-1][-1].x}'.format(['abc', [D('def')]]), 'def') + self.assertEqual('{-1}'.format('abc', 'def'), 'def') + self.assertEqual('{-2}'.format('abc', 'def'), 'abc') + self.assertRaises(IndexError, '{-1}'.format) + self.assertRaises(IndexError, '{-2}'.format, 'abc') + def test_format_auto_numbering(self): class C: def __init__(self, x=100):