Index: Objects/unicodeobject.c =================================================================== --- Objects/unicodeobject.c (revision 88497) +++ Objects/unicodeobject.c (working copy) @@ -752,7 +752,7 @@ if (*f == '%') { if (*(f+1)=='%') continue; - if (*(f+1)=='S' || *(f+1)=='R' || *(f+1)=='A') + if (*(f+1)=='S' || *(f+1)=='R' || *(f+1)=='A' || *(f+1) == 'V') ++callcount; while (Py_ISDIGIT((unsigned)*f)) width = (width*10) + *f++ - '0'; @@ -872,12 +872,20 @@ { PyObject *obj = va_arg(count, PyObject *); const char *str = va_arg(count, const char *); + PyObject *str_obj; assert(obj || str); assert(!obj || PyUnicode_Check(obj)); - if (obj) + if (obj) { n += PyUnicode_GET_SIZE(obj); - else - n += strlen(str); + *callresult++ = NULL; + } + else { + str_obj = PyUnicode_DecodeUTF8(str, strlen(str), "replace"); + if (!str_obj) + goto fail; + n += PyUnicode_GET_SIZE(str_obj); + *callresult++ = str_obj; + } break; } case 'S': @@ -1080,13 +1088,18 @@ case 'V': { PyObject *obj = va_arg(vargs, PyObject *); - const char *str = va_arg(vargs, const char *); + 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; + ++callresult; } else { - appendstring(str); + Py_UNICODE_COPY(s, PyUnicode_AS_UNICODE(*callresult), + PyUnicode_GET_SIZE(*callresult)); + s += PyUnicode_GET_SIZE(*callresult); + Py_DECREF(*callresult); + ++callresult; } break; } @@ -1144,7 +1157,7 @@ if (callresults) { PyObject **callresult2 = callresults; while (callresult2 < callresult) { - Py_DECREF(*callresult2); + Py_XDECREF(*callresult2); ++callresult2; } PyObject_Free(callresults); Index: Lib/test/test_unicode.py =================================================================== --- Lib/test/test_unicode.py (revision 88497) +++ Lib/test/test_unicode.py (working copy) @@ -1459,6 +1459,19 @@ text = PyUnicode_FromFormat(b'%%A:%A', 'abc\xe9\uabcd\U0010ffff') self.assertEqual(text, r"%A:'abc\xe9\uabcd\U0010ffff'") + text = PyUnicode_FromFormat(b'repr=%V', 'abc', b'xyz') + self.assertEqual(text, 'repr=abc') + + # Test string decode from parameter of %s using utf-8. + # b'\xe4\xba\xba\xe6\xb0\x91' is utf-8 encoded byte sequence of + # '\u4eba\u6c11' + text = PyUnicode_FromFormat(b'repr=%V', None, b'\xe4\xba\xba\xe6\xb0\x91') + self.assertEqual(text, 'repr=\u4eba\u6c11') + + #Test replace error handler. + text = PyUnicode_FromFormat(b'repr=%V', None, b'abc\xff') + self.assertEqual(text, 'repr=abc\ufffd') + # Test PyUnicode_AsWideChar() def test_aswidechar(self): from _testcapi import unicode_aswidechar