Index: Objects/unicodeobject.c =================================================================== --- Objects/unicodeobject.c (revision 88761) +++ Objects/unicodeobject.c (working copy) @@ -688,7 +688,7 @@ *fmt++ = '0'; fmt += sprintf(fmt, "%d", width); } - if (precision) + if (precision != -1) fmt += sprintf(fmt, ".%d", precision); if (longflag) *fmt++ = 'l'; @@ -728,9 +728,11 @@ width = 0; while (Py_ISDIGIT((unsigned)*f)) width = (width*10) + *f++ - '0'; - precision = 0; + precision = -1; if (*f == '.') { f++; + if Py_ISDIGIT((unsigned)*f) + precision = 0; while (Py_ISDIGIT((unsigned)*f)) precision = (precision*10) + *f++ - '0'; if (*f == '%') { @@ -791,6 +793,8 @@ plus 1 for the sign. 53/22 is an upper bound for log10(256). */ #define MAX_LONG_LONG_CHARS (2 + (SIZEOF_LONG_LONG*53-1) / 22) +#define MAX_SIZE(size1, size2) ((size1) >= (size2) ? (size1) : (size2)) + PyObject * PyUnicode_FromFormatV(const char *format, va_list vargs) { @@ -824,7 +828,7 @@ if (*f == '%') { /* skip width or width.precision (eg. "1.2" of "%1.2f") */ f = parse_format_flags(f, NULL, NULL, NULL, NULL, NULL); - if (*f == 's' || *f=='S' || *f=='R' || *f=='A' || *f=='V') + if (*f == 's' || *f=='S' || *f=='R' || *f=='A' || *f=='V' || *f=='U') ++callcount; } else if (128 <= (unsigned char)*f) { @@ -854,7 +858,7 @@ const char* p; p = f; - f = parse_format_flags(f, &width, NULL, + f = parse_format_flags(f, &width, &precision, NULL, &longlongflag, NULL); switch (*f) { @@ -902,7 +906,7 @@ PyObject *str = PyUnicode_DecodeUTF8(s, strlen(s), "replace"); if (!str) goto fail; - n += PyUnicode_GET_SIZE(str); + n += MAX_SIZE(width, PyUnicode_GET_SIZE(str)); /* Remember the str and switch to the next slot */ *callresult++ = str; break; @@ -911,7 +915,9 @@ { PyObject *obj = va_arg(count, PyObject *); assert(obj && PyUnicode_Check(obj)); - n += PyUnicode_GET_SIZE(obj); + n += MAX_SIZE(width, PyUnicode_GET_SIZE(obj)); + Py_INCREF(obj); + *callresult++ = obj; break; } case 'V': @@ -922,14 +928,15 @@ assert(obj || str); assert(!obj || PyUnicode_Check(obj)); if (obj) { - n += PyUnicode_GET_SIZE(obj); - *callresult++ = NULL; + n += MAX_SIZE(width, PyUnicode_GET_SIZE(obj)); + Py_INCREF(obj); + *callresult++ = obj; } else { str_obj = PyUnicode_DecodeUTF8(str, strlen(str), "replace"); if (!str_obj) goto fail; - n += PyUnicode_GET_SIZE(str_obj); + n += MAX_SIZE(width, PyUnicode_GET_SIZE(str_obj)); *callresult++ = str_obj; } break; @@ -942,7 +949,7 @@ str = PyObject_Str(obj); if (!str) goto fail; - n += PyUnicode_GET_SIZE(str); + n += MAX_SIZE(width, PyUnicode_GET_SIZE(str)); /* Remember the str and switch to the next slot */ *callresult++ = str; break; @@ -955,7 +962,7 @@ repr = PyObject_Repr(obj); if (!repr) goto fail; - n += PyUnicode_GET_SIZE(repr); + n += MAX_SIZE(width, PyUnicode_GET_SIZE(repr)); /* Remember the repr and switch to the next slot */ *callresult++ = repr; break; @@ -968,7 +975,7 @@ ascii = PyObject_ASCII(obj); if (!ascii) goto fail; - n += PyUnicode_GET_SIZE(ascii); + n += MAX_SIZE(width, PyUnicode_GET_SIZE(ascii)); /* Remember the repr and switch to the next slot */ *callresult++ = ascii; break; @@ -1082,59 +1089,36 @@ appendstring(realbuffer); break; case 's': - { - /* unused, since we already have the result */ - (void) va_arg(vargs, char *); - Py_UNICODE_COPY(s, PyUnicode_AS_UNICODE(*callresult), - PyUnicode_GET_SIZE(*callresult)); - s += PyUnicode_GET_SIZE(*callresult); - /* We're done with the unicode()/repr() => forget it */ - Py_DECREF(*callresult); - /* switch to next unicode()/repr() result */ - ++callresult; - break; - } case 'U': - { - PyObject *obj = va_arg(vargs, PyObject *); - Py_ssize_t size = PyUnicode_GET_SIZE(obj); - Py_UNICODE_COPY(s, PyUnicode_AS_UNICODE(obj), size); - s += size; - break; - } case 'V': - { - PyObject *obj = va_arg(vargs, PyObject *); - va_arg(vargs, const char *); - if (obj) { - Py_ssize_t size = PyUnicode_GET_SIZE(obj); - Py_UNICODE_COPY(s, PyUnicode_AS_UNICODE(obj), size); - s += size; - } else { - Py_UNICODE_COPY(s, PyUnicode_AS_UNICODE(*callresult), - PyUnicode_GET_SIZE(*callresult)); - s += PyUnicode_GET_SIZE(*callresult); - Py_DECREF(*callresult); - } - ++callresult; - break; - } case 'S': case 'R': case 'A': { - Py_UNICODE *ucopy; - Py_ssize_t usize; - Py_ssize_t upos; /* unused, since we already have the result */ - (void) va_arg(vargs, PyObject *); - ucopy = PyUnicode_AS_UNICODE(*callresult); - usize = PyUnicode_GET_SIZE(*callresult); - for (upos = 0; upos forget it */ - Py_DECREF(*callresult); - /* switch to next unicode()/repr() result */ + (void) va_arg(vargs, char *); + PyObject *unicode = *callresult; + Py_ssize_t space_count = 0; + Py_ssize_t copy_count = PyUnicode_GET_SIZE(unicode); + if (precision < 0 || precision >= PyUnicode_GET_SIZE(unicode)) { + if (width <= PyUnicode_GET_SIZE(unicode)) + goto copy_unicode; + else + precision = PyUnicode_GET_SIZE(unicode); + } + if (precision < PyUnicode_GET_SIZE(unicode) && width <= precision) { + copy_count = precision; + goto copy_unicode; + } + space_count = width - precision; + copy_count = precision; + + copy_unicode: + Py_UNICODE_FILL(s, ' ', space_count); + s += space_count; + Py_UNICODE_COPY(s, PyUnicode_AS_UNICODE(unicode), copy_count); + s += copy_count; + Py_DECREF(unicode); ++callresult; break; } @@ -1183,6 +1167,8 @@ return NULL; } +#undef MAX_SIZE + #undef appendstring PyObject * Index: Lib/test/test_unicode.py =================================================================== --- Lib/test/test_unicode.py (revision 88761) +++ Lib/test/test_unicode.py (working copy) @@ -1511,6 +1511,61 @@ self.assertEqual(PyUnicode_FromFormat(b'%+i', c_int(10)), '%+i') self.assertEqual(PyUnicode_FromFormat(b'%.%s', b'abc'), '%.%s') + # following tests comes from #7330 + # test width modifier and precision modifier with %S + text = PyUnicode_FromFormat(b'repr=%5S', 'xx') + self.assertEqual(text, "repr= xx") + text = PyUnicode_FromFormat(b'repr=%.2S', 'xxx') + self.assertEqual(text, "repr=xx") + text = PyUnicode_FromFormat(b'repr=%5.2S', 'xxx') + self.assertEqual(text, "repr= xx") + + # test width modifier and precision modifier with %R + text = PyUnicode_FromFormat(b'repr=%5R', 'xx') + self.assertEqual(text, "repr= 'xx'") + text = PyUnicode_FromFormat(b'repr=%.2R', 'xxx') + self.assertEqual(text, "repr='x") + text = PyUnicode_FromFormat(b'repr=%5.2R', 'xxx') + self.assertEqual(text, "repr= 'x") + + # test width modifier and precision modifier with %A + text = PyUnicode_FromFormat(b'repr=%5A', 'xx') + self.assertEqual(text, "repr= 'xx'") + text = PyUnicode_FromFormat(b'repr=%.2A', 'xxx') + self.assertEqual(text, "repr='x") + text = PyUnicode_FromFormat(b'repr=%5.2A', 'xxx') + self.assertEqual(text, "repr= 'x") + + # test width modifier and precision modifier with %s + text = PyUnicode_FromFormat(b'repr=%5s', b'xx') + self.assertEqual(text, "repr= xx") + text = PyUnicode_FromFormat(b'repr=%.2s', b'xxx') + self.assertEqual(text, "repr=xx") + text = PyUnicode_FromFormat(b'repr=%5.2s', b'xxx') + self.assertEqual(text, "repr= xx") + + # test width modifier and precision modifier with %U + text = PyUnicode_FromFormat(b'repr=%5U', 'xx') + self.assertEqual(text, "repr= xx") + text = PyUnicode_FromFormat(b'repr=%.2U', 'xxx') + self.assertEqual(text, "repr=xx") + text = PyUnicode_FromFormat(b'repr=%5.2U', 'xxx') + self.assertEqual(text, "repr= xx") + + # test width modifier and precision modifier with %V + text = PyUnicode_FromFormat(b'repr=%5V', 'xx', b'yy') + self.assertEqual(text, "repr= xx") + text = PyUnicode_FromFormat(b'repr=%.2V', 'xxx', b'yyy') + self.assertEqual(text, "repr=xx") + text = PyUnicode_FromFormat(b'repr=%5.2V', 'xxx', b'yyy') + self.assertEqual(text, "repr= xx") + text = PyUnicode_FromFormat(b'repr=%5V', None, b'yy') + self.assertEqual(text, "repr= yy") + text = PyUnicode_FromFormat(b'repr=%.2V', None, b'yyy') + self.assertEqual(text, "repr=yy") + text = PyUnicode_FromFormat(b'repr=%5.2V', None, b'yyy') + self.assertEqual(text, "repr= yy") + # Test PyUnicode_AsWideChar() def test_aswidechar(self): from _testcapi import unicode_aswidechar