]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-95504: Fix negative numbers in PyUnicode_FromFormat (GH-95848)
authorPetr Viktorin <encukou@gmail.com>
Wed, 10 Aug 2022 11:12:40 +0000 (13:12 +0200)
committerGitHub <noreply@github.com>
Wed, 10 Aug 2022 11:12:40 +0000 (13:12 +0200)
Co-authored-by: philg314 <110174000+philg314@users.noreply.github.com>
Doc/whatsnew/3.12.rst
Misc/ACKS
Misc/NEWS.d/next/C API/2022-07-31-21-58-27.gh-issue-95504.wy7B1F.rst [new file with mode: 0644]
Modules/_testcapi/unicode.c
Objects/unicodeobject.c

index 6df122acba71d550781c1a6360672748d74f579a..acf59616b2fe170711290bb8d443d3eab067e9cf 100644 (file)
@@ -475,6 +475,9 @@ 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
+  :c:func:`PyUnicode_FromFormatV`.
+  (Contributed by Philip Georgi in :gh:`95504`.)
 
 Deprecated
 ----------
index 7065267379deb132806c77be0946d351f8b7eb6e..28b4ce42e907c6afaa6079fcff6c453bd287712b 100644 (file)
--- 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 (file)
index 0000000..955bdd4
--- /dev/null
@@ -0,0 +1,3 @@
+Fix sign placement when specifying width or precision in
+:c:func:`PyUnicode_FromFormat` and :c:func:`PyUnicode_FromFormatV`.
+Patch by Philip Georgi.
index 58214249e2252752d48099a61276943a3a67c156..d0f1e2abdc8259d09312ef80b4d92545c861aee1 100644 (file)
@@ -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);
index 184a2bfd5dd869f4ef709da75b3073412a5d5b98..b1d14a32f70f3e385a055927d76d6684835222a5 100644 (file)
@@ -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;
     }