Skip to content

Commit 71c3d64

Browse files
encukouphilg314
andauthored
gh-95504: Fix negative numbers in PyUnicode_FromFormat (GH-95848)
Co-authored-by: philg314 <[email protected]>
1 parent cf28540 commit 71c3d64

File tree

5 files changed

+86
-7
lines changed

5 files changed

+86
-7
lines changed

Doc/whatsnew/3.12.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,9 @@ Porting to Python 3.12
475475
copied as-is to the result string, and any extra arguments discarded.
476476
(Contributed by Serhiy Storchaka in :gh:`95781`.)
477477

478+
* Fixed wrong sign placement in :c:func:`PyUnicode_FromFormat` and
479+
:c:func:`PyUnicode_FromFormatV`.
480+
(Contributed by Philip Georgi in :gh:`95504`.)
478481

479482
Deprecated
480483
----------

Misc/ACKS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,8 +608,8 @@ Marius Gedminas
608608
Jan-Philip Gehrcke
609609
Thomas Gellekum
610610
Gabriel Genellina
611-
Christos Georgiou
612611
Philip Georgi
612+
Christos Georgiou
613613
Elazar (אלעזר) Gershuni
614614
Ben Gertzfield
615615
Nadim Ghaznavi
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix sign placement when specifying width or precision in
2+
:c:func:`PyUnicode_FromFormat` and :c:func:`PyUnicode_FromFormatV`.
3+
Patch by Philip Georgi.

Modules/_testcapi/unicode.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,16 @@ test_string_from_format(PyObject *self, PyObject *Py_UNUSED(ignored))
433433
CHECK_FORMAT_1("%05zu", "00123", (size_t)123);
434434
CHECK_FORMAT_1("%05x", "0007b", (int)123);
435435

436+
CHECK_FORMAT_1("%05d", "-0123", (int)-123);
437+
CHECK_FORMAT_1("%05i", "-0123", (int)-123);
438+
CHECK_FORMAT_1("%05ld", "-0123", (long)-123);
439+
CHECK_FORMAT_1("%05li", "-0123", (long)-123);
440+
CHECK_FORMAT_1("%05lld", "-0123", (long long)-123);
441+
CHECK_FORMAT_1("%05lli", "-0123", (long long)-123);
442+
CHECK_FORMAT_1("%05zd", "-0123", (Py_ssize_t)-123);
443+
CHECK_FORMAT_1("%05zi", "-0123", (Py_ssize_t)-123);
444+
CHECK_FORMAT_1("%09x", "0ffffff85", (int)-123);
445+
436446
// Integers: precision < length
437447
CHECK_FORMAT_1("%.1d", "123", (int)123);
438448
CHECK_FORMAT_1("%.1i", "123", (int)123);
@@ -473,6 +483,16 @@ test_string_from_format(PyObject *self, PyObject *Py_UNUSED(ignored))
473483
CHECK_FORMAT_1("%.5zu", "00123", (size_t)123);
474484
CHECK_FORMAT_1("%.5x", "0007b", (int)123);
475485

486+
CHECK_FORMAT_1("%.5d", "-00123", (int)-123);
487+
CHECK_FORMAT_1("%.5i", "-00123", (int)-123);
488+
CHECK_FORMAT_1("%.5ld", "-00123", (long)-123);
489+
CHECK_FORMAT_1("%.5li", "-00123", (long)-123);
490+
CHECK_FORMAT_1("%.5lld", "-00123", (long long)-123);
491+
CHECK_FORMAT_1("%.5lli", "-00123", (long long)-123);
492+
CHECK_FORMAT_1("%.5zd", "-00123", (Py_ssize_t)-123);
493+
CHECK_FORMAT_1("%.5zi", "-00123", (Py_ssize_t)-123);
494+
CHECK_FORMAT_1("%.9x", "0ffffff85", (int)-123);
495+
476496
// Integers: width > precision > length
477497
CHECK_FORMAT_1("%7.5d", " 00123", (int)123);
478498
CHECK_FORMAT_1("%7.5i", " 00123", (int)123);
@@ -488,6 +508,16 @@ test_string_from_format(PyObject *self, PyObject *Py_UNUSED(ignored))
488508
CHECK_FORMAT_1("%7.5zu", " 00123", (size_t)123);
489509
CHECK_FORMAT_1("%7.5x", " 0007b", (int)123);
490510

511+
CHECK_FORMAT_1("%7.5d", " -00123", (int)-123);
512+
CHECK_FORMAT_1("%7.5i", " -00123", (int)-123);
513+
CHECK_FORMAT_1("%7.5ld", " -00123", (long)-123);
514+
CHECK_FORMAT_1("%7.5li", " -00123", (long)-123);
515+
CHECK_FORMAT_1("%7.5lld", " -00123", (long long)-123);
516+
CHECK_FORMAT_1("%7.5lli", " -00123", (long long)-123);
517+
CHECK_FORMAT_1("%7.5zd", " -00123", (Py_ssize_t)-123);
518+
CHECK_FORMAT_1("%7.5zi", " -00123", (Py_ssize_t)-123);
519+
CHECK_FORMAT_1("%10.9x", " 0ffffff85", (int)-123);
520+
491521
// Integers: width > precision > length, 0-flag
492522
CHECK_FORMAT_1("%07.5d", "0000123", (int)123);
493523
CHECK_FORMAT_1("%07.5i", "0000123", (int)123);
@@ -503,6 +533,16 @@ test_string_from_format(PyObject *self, PyObject *Py_UNUSED(ignored))
503533
CHECK_FORMAT_1("%07.5zu", "0000123", (size_t)123);
504534
CHECK_FORMAT_1("%07.5x", "000007b", (int)123);
505535

536+
CHECK_FORMAT_1("%07.5d", "-000123", (int)-123);
537+
CHECK_FORMAT_1("%07.5i", "-000123", (int)-123);
538+
CHECK_FORMAT_1("%07.5ld", "-000123", (long)-123);
539+
CHECK_FORMAT_1("%07.5li", "-000123", (long)-123);
540+
CHECK_FORMAT_1("%07.5lld", "-000123", (long long)-123);
541+
CHECK_FORMAT_1("%07.5lli", "-000123", (long long)-123);
542+
CHECK_FORMAT_1("%07.5zd", "-000123", (Py_ssize_t)-123);
543+
CHECK_FORMAT_1("%07.5zi", "-000123", (Py_ssize_t)-123);
544+
CHECK_FORMAT_1("%010.9x", "00ffffff85", (int)-123);
545+
506546
// Integers: precision > width > length
507547
CHECK_FORMAT_1("%5.7d", "0000123", (int)123);
508548
CHECK_FORMAT_1("%5.7i", "0000123", (int)123);
@@ -518,6 +558,16 @@ test_string_from_format(PyObject *self, PyObject *Py_UNUSED(ignored))
518558
CHECK_FORMAT_1("%5.7zu", "0000123", (size_t)123);
519559
CHECK_FORMAT_1("%5.7x", "000007b", (int)123);
520560

561+
CHECK_FORMAT_1("%5.7d", "-0000123", (int)-123);
562+
CHECK_FORMAT_1("%5.7i", "-0000123", (int)-123);
563+
CHECK_FORMAT_1("%5.7ld", "-0000123", (long)-123);
564+
CHECK_FORMAT_1("%5.7li", "-0000123", (long)-123);
565+
CHECK_FORMAT_1("%5.7lld", "-0000123", (long long)-123);
566+
CHECK_FORMAT_1("%5.7lli", "-0000123", (long long)-123);
567+
CHECK_FORMAT_1("%5.7zd", "-0000123", (Py_ssize_t)-123);
568+
CHECK_FORMAT_1("%5.7zi", "-0000123", (Py_ssize_t)-123);
569+
CHECK_FORMAT_1("%9.10x", "00ffffff85", (int)-123);
570+
521571
// Integers: precision > width > length, 0-flag
522572
CHECK_FORMAT_1("%05.7d", "0000123", (int)123);
523573
CHECK_FORMAT_1("%05.7i", "0000123", (int)123);
@@ -533,6 +583,16 @@ test_string_from_format(PyObject *self, PyObject *Py_UNUSED(ignored))
533583
CHECK_FORMAT_1("%05.7zu", "0000123", (size_t)123);
534584
CHECK_FORMAT_1("%05.7x", "000007b", (int)123);
535585

586+
CHECK_FORMAT_1("%05.7d", "-0000123", (int)-123);
587+
CHECK_FORMAT_1("%05.7i", "-0000123", (int)-123);
588+
CHECK_FORMAT_1("%05.7ld", "-0000123", (long)-123);
589+
CHECK_FORMAT_1("%05.7li", "-0000123", (long)-123);
590+
CHECK_FORMAT_1("%05.7lld", "-0000123", (long long)-123);
591+
CHECK_FORMAT_1("%05.7lli", "-0000123", (long long)-123);
592+
CHECK_FORMAT_1("%05.7zd", "-0000123", (Py_ssize_t)-123);
593+
CHECK_FORMAT_1("%05.7zi", "-0000123", (Py_ssize_t)-123);
594+
CHECK_FORMAT_1("%09.10x", "00ffffff85", (int)-123);
595+
536596
// Integers: precision = 0, arg = 0 (empty string in C)
537597
CHECK_FORMAT_1("%.0d", "0", (int)0);
538598
CHECK_FORMAT_1("%.0i", "0", (int)0);

Objects/unicodeobject.c

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2481,29 +2481,42 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer,
24812481
}
24822482
assert(len >= 0);
24832483

2484-
if (precision < len)
2485-
precision = len;
2484+
int negative = (buffer[0] == '-');
2485+
len -= negative;
2486+
2487+
precision = Py_MAX(precision, len);
2488+
width = Py_MAX(width, precision + negative);
24862489

24872490
arglen = Py_MAX(precision, width);
24882491
if (_PyUnicodeWriter_Prepare(writer, arglen, 127) == -1)
24892492
return NULL;
24902493

24912494
if (width > precision) {
2492-
Py_UCS4 fillchar;
2493-
fill = width - precision;
2494-
fillchar = zeropad?'0':' ';
2495+
if (negative && zeropad) {
2496+
if (_PyUnicodeWriter_WriteChar(writer, '-') == -1)
2497+
return NULL;
2498+
}
2499+
2500+
Py_UCS4 fillchar = zeropad?'0':' ';
2501+
fill = width - precision - negative;
24952502
if (PyUnicode_Fill(writer->buffer, writer->pos, fill, fillchar) == -1)
24962503
return NULL;
24972504
writer->pos += fill;
2505+
2506+
if (negative && !zeropad) {
2507+
if (_PyUnicodeWriter_WriteChar(writer, '-') == -1)
2508+
return NULL;
2509+
}
24982510
}
2511+
24992512
if (precision > len) {
25002513
fill = precision - len;
25012514
if (PyUnicode_Fill(writer->buffer, writer->pos, fill, '0') == -1)
25022515
return NULL;
25032516
writer->pos += fill;
25042517
}
25052518

2506-
if (_PyUnicodeWriter_WriteASCIIString(writer, buffer, len) < 0)
2519+
if (_PyUnicodeWriter_WriteASCIIString(writer, &buffer[negative], len) < 0)
25072520
return NULL;
25082521
break;
25092522
}

0 commit comments

Comments
 (0)