From d2099ed2cc4d36e8c4a85d6f3382cf8e89b33496 Mon Sep 17 00:00:00 2001 From: philg314 <110174000+philg314@users.noreply.github.com> Date: Fri, 29 Jul 2022 21:28:09 +0200 Subject: [PATCH 1/3] Fix negative numbers in PyUnicode_FromFormat --- Doc/whatsnew/3.12.rst | 2 + Misc/ACKS | 2 +- ...2-07-31-21-58-27.gh-issue-95504.wy7B1F.rst | 2 + Modules/_testcapi/unicode.c | 60 +++++++++++++++++++ Objects/unicodeobject.c | 25 ++++++-- 5 files changed, 84 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2022-07-31-21-58-27.gh-issue-95504.wy7B1F.rst diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 6df122acba71d5..9a7798ca0bfbb9 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -475,6 +475,8 @@ Porting to Python 3.12 copied as-is to the result string, and any extra arguments discarded. (Contributed by Serhiy Storchaka in :gh:`95781`.) +* Fixed wrong sign placement in ``PyUnicode_FromFormat``. + (Contributed by Philip Georgi in :gh:`95504`.) Deprecated ---------- diff --git a/Misc/ACKS b/Misc/ACKS index 7065267379deb1..28b4ce42e907c6 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -608,8 +608,8 @@ Marius Gedminas Jan-Philip Gehrcke Thomas Gellekum Gabriel Genellina -Christos Georgiou Philip Georgi +Christos Georgiou Elazar (אלעזר) Gershuni Ben Gertzfield Nadim Ghaznavi diff --git a/Misc/NEWS.d/next/C API/2022-07-31-21-58-27.gh-issue-95504.wy7B1F.rst b/Misc/NEWS.d/next/C API/2022-07-31-21-58-27.gh-issue-95504.wy7B1F.rst new file mode 100644 index 00000000000000..dfe7e226c5e8af --- /dev/null +++ b/Misc/NEWS.d/next/C API/2022-07-31-21-58-27.gh-issue-95504.wy7B1F.rst @@ -0,0 +1,2 @@ +Fix sign placement when specifying width or precision in +``PyUnicode_FromFormat``. Patch by Philip Georgi. diff --git a/Modules/_testcapi/unicode.c b/Modules/_testcapi/unicode.c index 58214249e22527..d0f1e2abdc8259 100644 --- a/Modules/_testcapi/unicode.c +++ b/Modules/_testcapi/unicode.c @@ -433,6 +433,16 @@ test_string_from_format(PyObject *self, PyObject *Py_UNUSED(ignored)) CHECK_FORMAT_1("%05zu", "00123", (size_t)123); CHECK_FORMAT_1("%05x", "0007b", (int)123); + CHECK_FORMAT_1("%05d", "-0123", (int)-123); + CHECK_FORMAT_1("%05i", "-0123", (int)-123); + CHECK_FORMAT_1("%05ld", "-0123", (long)-123); + CHECK_FORMAT_1("%05li", "-0123", (long)-123); + CHECK_FORMAT_1("%05lld", "-0123", (long long)-123); + CHECK_FORMAT_1("%05lli", "-0123", (long long)-123); + CHECK_FORMAT_1("%05zd", "-0123", (Py_ssize_t)-123); + CHECK_FORMAT_1("%05zi", "-0123", (Py_ssize_t)-123); + CHECK_FORMAT_1("%09x", "0ffffff85", (int)-123); + // Integers: precision < length CHECK_FORMAT_1("%.1d", "123", (int)123); CHECK_FORMAT_1("%.1i", "123", (int)123); @@ -473,6 +483,16 @@ test_string_from_format(PyObject *self, PyObject *Py_UNUSED(ignored)) CHECK_FORMAT_1("%.5zu", "00123", (size_t)123); CHECK_FORMAT_1("%.5x", "0007b", (int)123); + CHECK_FORMAT_1("%.5d", "-00123", (int)-123); + CHECK_FORMAT_1("%.5i", "-00123", (int)-123); + CHECK_FORMAT_1("%.5ld", "-00123", (long)-123); + CHECK_FORMAT_1("%.5li", "-00123", (long)-123); + CHECK_FORMAT_1("%.5lld", "-00123", (long long)-123); + CHECK_FORMAT_1("%.5lli", "-00123", (long long)-123); + CHECK_FORMAT_1("%.5zd", "-00123", (Py_ssize_t)-123); + CHECK_FORMAT_1("%.5zi", "-00123", (Py_ssize_t)-123); + CHECK_FORMAT_1("%.9x", "0ffffff85", (int)-123); + // Integers: width > precision > length CHECK_FORMAT_1("%7.5d", " 00123", (int)123); CHECK_FORMAT_1("%7.5i", " 00123", (int)123); @@ -488,6 +508,16 @@ test_string_from_format(PyObject *self, PyObject *Py_UNUSED(ignored)) CHECK_FORMAT_1("%7.5zu", " 00123", (size_t)123); CHECK_FORMAT_1("%7.5x", " 0007b", (int)123); + CHECK_FORMAT_1("%7.5d", " -00123", (int)-123); + CHECK_FORMAT_1("%7.5i", " -00123", (int)-123); + CHECK_FORMAT_1("%7.5ld", " -00123", (long)-123); + CHECK_FORMAT_1("%7.5li", " -00123", (long)-123); + CHECK_FORMAT_1("%7.5lld", " -00123", (long long)-123); + CHECK_FORMAT_1("%7.5lli", " -00123", (long long)-123); + CHECK_FORMAT_1("%7.5zd", " -00123", (Py_ssize_t)-123); + CHECK_FORMAT_1("%7.5zi", " -00123", (Py_ssize_t)-123); + CHECK_FORMAT_1("%10.9x", " 0ffffff85", (int)-123); + // Integers: width > precision > length, 0-flag CHECK_FORMAT_1("%07.5d", "0000123", (int)123); CHECK_FORMAT_1("%07.5i", "0000123", (int)123); @@ -503,6 +533,16 @@ test_string_from_format(PyObject *self, PyObject *Py_UNUSED(ignored)) CHECK_FORMAT_1("%07.5zu", "0000123", (size_t)123); CHECK_FORMAT_1("%07.5x", "000007b", (int)123); + CHECK_FORMAT_1("%07.5d", "-000123", (int)-123); + CHECK_FORMAT_1("%07.5i", "-000123", (int)-123); + CHECK_FORMAT_1("%07.5ld", "-000123", (long)-123); + CHECK_FORMAT_1("%07.5li", "-000123", (long)-123); + CHECK_FORMAT_1("%07.5lld", "-000123", (long long)-123); + CHECK_FORMAT_1("%07.5lli", "-000123", (long long)-123); + CHECK_FORMAT_1("%07.5zd", "-000123", (Py_ssize_t)-123); + CHECK_FORMAT_1("%07.5zi", "-000123", (Py_ssize_t)-123); + CHECK_FORMAT_1("%010.9x", "00ffffff85", (int)-123); + // Integers: precision > width > length CHECK_FORMAT_1("%5.7d", "0000123", (int)123); CHECK_FORMAT_1("%5.7i", "0000123", (int)123); @@ -518,6 +558,16 @@ test_string_from_format(PyObject *self, PyObject *Py_UNUSED(ignored)) CHECK_FORMAT_1("%5.7zu", "0000123", (size_t)123); CHECK_FORMAT_1("%5.7x", "000007b", (int)123); + CHECK_FORMAT_1("%5.7d", "-0000123", (int)-123); + CHECK_FORMAT_1("%5.7i", "-0000123", (int)-123); + CHECK_FORMAT_1("%5.7ld", "-0000123", (long)-123); + CHECK_FORMAT_1("%5.7li", "-0000123", (long)-123); + CHECK_FORMAT_1("%5.7lld", "-0000123", (long long)-123); + CHECK_FORMAT_1("%5.7lli", "-0000123", (long long)-123); + CHECK_FORMAT_1("%5.7zd", "-0000123", (Py_ssize_t)-123); + CHECK_FORMAT_1("%5.7zi", "-0000123", (Py_ssize_t)-123); + CHECK_FORMAT_1("%9.10x", "00ffffff85", (int)-123); + // Integers: precision > width > length, 0-flag CHECK_FORMAT_1("%05.7d", "0000123", (int)123); CHECK_FORMAT_1("%05.7i", "0000123", (int)123); @@ -533,6 +583,16 @@ test_string_from_format(PyObject *self, PyObject *Py_UNUSED(ignored)) CHECK_FORMAT_1("%05.7zu", "0000123", (size_t)123); CHECK_FORMAT_1("%05.7x", "000007b", (int)123); + CHECK_FORMAT_1("%05.7d", "-0000123", (int)-123); + CHECK_FORMAT_1("%05.7i", "-0000123", (int)-123); + CHECK_FORMAT_1("%05.7ld", "-0000123", (long)-123); + CHECK_FORMAT_1("%05.7li", "-0000123", (long)-123); + CHECK_FORMAT_1("%05.7lld", "-0000123", (long long)-123); + CHECK_FORMAT_1("%05.7lli", "-0000123", (long long)-123); + CHECK_FORMAT_1("%05.7zd", "-0000123", (Py_ssize_t)-123); + CHECK_FORMAT_1("%05.7zi", "-0000123", (Py_ssize_t)-123); + CHECK_FORMAT_1("%09.10x", "00ffffff85", (int)-123); + // Integers: precision = 0, arg = 0 (empty string in C) CHECK_FORMAT_1("%.0d", "0", (int)0); CHECK_FORMAT_1("%.0i", "0", (int)0); diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 184a2bfd5dd869..b1d14a32f70f3e 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -2481,21 +2481,34 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer, } assert(len >= 0); - if (precision < len) - precision = len; + int negative = (buffer[0] == '-'); + len -= negative; + + precision = Py_MAX(precision, len); + width = Py_MAX(width, precision + negative); arglen = Py_MAX(precision, width); if (_PyUnicodeWriter_Prepare(writer, arglen, 127) == -1) return NULL; if (width > precision) { - Py_UCS4 fillchar; - fill = width - precision; - fillchar = zeropad?'0':' '; + if (negative && zeropad) { + if (_PyUnicodeWriter_WriteChar(writer, '-') == -1) + return NULL; + } + + Py_UCS4 fillchar = zeropad?'0':' '; + fill = width - precision - negative; if (PyUnicode_Fill(writer->buffer, writer->pos, fill, fillchar) == -1) return NULL; writer->pos += fill; + + if (negative && !zeropad) { + if (_PyUnicodeWriter_WriteChar(writer, '-') == -1) + return NULL; + } } + if (precision > len) { fill = precision - len; if (PyUnicode_Fill(writer->buffer, writer->pos, fill, '0') == -1) @@ -2503,7 +2516,7 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer, writer->pos += fill; } - if (_PyUnicodeWriter_WriteASCIIString(writer, buffer, len) < 0) + if (_PyUnicodeWriter_WriteASCIIString(writer, &buffer[negative], len) < 0) return NULL; break; } From e7c617f85c336c6965d0c63e3a33785fb3ca83c0 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 10 Aug 2022 11:04:59 +0200 Subject: [PATCH 2/3] Apply What's New fixes Co-authored-by: philg314 <110174000+philg314@users.noreply.github.com> --- Doc/whatsnew/3.12.rst | 3 ++- .../next/C API/2022-07-31-21-58-27.gh-issue-95504.wy7B1F.rst | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 9a7798ca0bfbb9..d50ca480a0a427 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -475,7 +475,8 @@ Porting to Python 3.12 copied as-is to the result string, and any extra arguments discarded. (Contributed by Serhiy Storchaka in :gh:`95781`.) -* Fixed wrong sign placement in ``PyUnicode_FromFormat``. +* Fixed wrong sign placement in :c:func:`PyUnicode_FromFormat` and + :c:func:`PyUnicode_FromFormatV`. (Contributed by Philip Georgi in :gh:`95504`.) Deprecated diff --git a/Misc/NEWS.d/next/C API/2022-07-31-21-58-27.gh-issue-95504.wy7B1F.rst b/Misc/NEWS.d/next/C API/2022-07-31-21-58-27.gh-issue-95504.wy7B1F.rst index dfe7e226c5e8af..955bdd4c7494f1 100644 --- a/Misc/NEWS.d/next/C API/2022-07-31-21-58-27.gh-issue-95504.wy7B1F.rst +++ b/Misc/NEWS.d/next/C API/2022-07-31-21-58-27.gh-issue-95504.wy7B1F.rst @@ -1,2 +1,3 @@ Fix sign placement when specifying width or precision in -``PyUnicode_FromFormat``. Patch by Philip Georgi. +:c:func:`PyUnicode_FromFormat` and :c:func:`PyUnicode_FromFormatV`. +Patch by Philip Georgi. From bb48dfbd26d2efd30c945637d9c802ef74619ed2 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 10 Aug 2022 11:41:57 +0200 Subject: [PATCH 3/3] Remove trailing namespace in docs :/ --- Doc/whatsnew/3.12.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index d50ca480a0a427..acf59616b2fe17 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -475,7 +475,7 @@ Porting to Python 3.12 copied as-is to the result string, and any extra arguments discarded. (Contributed by Serhiy Storchaka in :gh:`95781`.) -* Fixed wrong sign placement in :c:func:`PyUnicode_FromFormat` and +* Fixed wrong sign placement in :c:func:`PyUnicode_FromFormat` and :c:func:`PyUnicode_FromFormatV`. (Contributed by Philip Georgi in :gh:`95504`.)