@@ -135,6 +135,7 @@ typedef struct {
135
135
Py_ssize_t width ;
136
136
enum LocaleType thousands_separators ;
137
137
Py_ssize_t precision ;
138
+ enum LocaleType frac_thousands_separator ;
138
139
Py_UCS4 type ;
139
140
} InternalFormatSpec ;
140
141
@@ -171,6 +172,7 @@ parse_internal_render_format_spec(PyObject *obj,
171
172
format -> sign = '\0' ;
172
173
format -> width = -1 ;
173
174
format -> thousands_separators = LT_NO_LOCALE ;
175
+ format -> frac_thousands_separator = LT_NO_LOCALE ;
174
176
format -> precision = -1 ;
175
177
format -> type = default_type ;
176
178
@@ -260,7 +262,16 @@ parse_internal_render_format_spec(PyObject *obj,
260
262
/* Overflow error. Exception already set. */
261
263
return 0 ;
262
264
263
- /* Not having a precision after a dot is an error. */
265
+ if (end - pos && READ_spec (pos ) == '_' ) {
266
+ if (consumed == 0 ) {
267
+ format -> precision = -1 ;
268
+ }
269
+ format -> frac_thousands_separator = LT_UNDERSCORE_LOCALE ;
270
+ ++ pos ;
271
+ ++ consumed ;
272
+ }
273
+
274
+ /* Not having a precision or underscore after a dot is an error. */
264
275
if (consumed == 0 ) {
265
276
PyErr_Format (PyExc_ValueError ,
266
277
"Format specifier missing precision" );
@@ -402,6 +413,7 @@ fill_padding(_PyUnicodeWriter *writer,
402
413
typedef struct {
403
414
PyObject * decimal_point ;
404
415
PyObject * thousands_sep ;
416
+ PyObject * frac_thousands_sep ;
405
417
const char * grouping ;
406
418
char * grouping_buffer ;
407
419
} LocaleInfo ;
@@ -423,6 +435,8 @@ typedef struct {
423
435
Py_ssize_t n_remainder ; /* Digits in decimal and/or exponent part,
424
436
excluding the decimal itself, if
425
437
present. */
438
+ Py_ssize_t n_frac ;
439
+ Py_ssize_t n_grouped_frac_digits ;
426
440
427
441
/* These 2 are not the widths of fields, but are needed by
428
442
STRINGLIB_GROUPING. */
@@ -445,24 +459,32 @@ typedef struct {
445
459
*/
446
460
static void
447
461
parse_number (PyObject * s , Py_ssize_t pos , Py_ssize_t end ,
448
- Py_ssize_t * n_remainder , int * has_decimal )
462
+ Py_ssize_t * n_remainder , Py_ssize_t * n_frac , int * has_decimal )
449
463
{
450
- Py_ssize_t remainder ;
464
+ Py_ssize_t frac ;
451
465
int kind = PyUnicode_KIND (s );
452
466
const void * data = PyUnicode_DATA (s );
453
467
454
- while (pos < end && Py_ISDIGIT (PyUnicode_READ (kind , data , pos )))
468
+ while (pos < end && Py_ISDIGIT (PyUnicode_READ (kind , data , pos ))) {
455
469
++ pos ;
456
- remainder = pos ;
470
+ }
471
+ frac = pos ;
457
472
458
473
/* Does remainder start with a decimal point? */
459
- * has_decimal = pos < end && PyUnicode_READ (kind , data , remainder ) == '.' ;
474
+ * has_decimal = pos < end && PyUnicode_READ (kind , data , frac ) == '.' ;
460
475
461
476
/* Skip the decimal point. */
462
- if (* has_decimal )
463
- remainder ++ ;
477
+ if (* has_decimal ) {
478
+ frac ++ ;
479
+ pos ++ ;
480
+ }
481
+
482
+ while (pos < end && Py_ISDIGIT (PyUnicode_READ (kind , data , pos ))) {
483
+ ++ pos ;
484
+ }
464
485
465
- * n_remainder = end - remainder ;
486
+ * n_frac = pos - frac ;
487
+ * n_remainder = end - pos ;
466
488
}
467
489
468
490
/* not all fields of format are used. for example, precision is
@@ -473,18 +495,19 @@ parse_number(PyObject *s, Py_ssize_t pos, Py_ssize_t end,
473
495
static Py_ssize_t
474
496
calc_number_widths (NumberFieldWidths * spec , Py_ssize_t n_prefix ,
475
497
Py_UCS4 sign_char , Py_ssize_t n_start ,
476
- Py_ssize_t n_end , Py_ssize_t n_remainder ,
498
+ Py_ssize_t n_end , Py_ssize_t n_remainder , Py_ssize_t n_frac ,
477
499
int has_decimal , const LocaleInfo * locale ,
478
500
const InternalFormatSpec * format , Py_UCS4 * maxchar )
479
501
{
480
502
Py_ssize_t n_non_digit_non_padding ;
481
503
Py_ssize_t n_padding ;
482
504
483
- spec -> n_digits = n_end - n_start - n_remainder - (has_decimal ?1 :0 );
505
+ spec -> n_digits = n_end - n_start - n_frac - n_remainder - (has_decimal ?1 :0 );
484
506
spec -> n_lpadding = 0 ;
485
507
spec -> n_prefix = n_prefix ;
486
508
spec -> n_decimal = has_decimal ? PyUnicode_GET_LENGTH (locale -> decimal_point ) : 0 ;
487
509
spec -> n_remainder = n_remainder ;
510
+ spec -> n_frac = n_frac ;
488
511
spec -> n_spadding = 0 ;
489
512
spec -> n_rpadding = 0 ;
490
513
spec -> sign = '\0' ;
@@ -530,7 +553,7 @@ calc_number_widths(NumberFieldWidths *spec, Py_ssize_t n_prefix,
530
553
531
554
/* The number of chars used for non-digits and non-padding. */
532
555
n_non_digit_non_padding = spec -> n_sign + spec -> n_prefix + spec -> n_decimal +
533
- spec -> n_remainder ;
556
+ + spec -> n_frac + spec -> n_remainder ;
534
557
535
558
/* min_width can go negative, that's okay. format->width == -1 means
536
559
we don't care. */
@@ -557,12 +580,29 @@ calc_number_widths(NumberFieldWidths *spec, Py_ssize_t n_prefix,
557
580
* maxchar = Py_MAX (* maxchar , grouping_maxchar );
558
581
}
559
582
583
+ if (spec -> n_frac == 0 ) {
584
+ spec -> n_grouped_frac_digits = 0 ;
585
+ }
586
+ else {
587
+ Py_UCS4 grouping_maxchar ;
588
+ spec -> n_grouped_frac_digits = _PyUnicode_InsertThousandsGrouping (
589
+ NULL , 0 ,
590
+ NULL , 0 , spec -> n_frac ,
591
+ spec -> n_frac ,
592
+ locale -> grouping , locale -> frac_thousands_sep , & grouping_maxchar );
593
+ if (spec -> n_grouped_frac_digits == -1 ) {
594
+ return -1 ;
595
+ }
596
+ * maxchar = Py_MAX (* maxchar , grouping_maxchar );
597
+ }
598
+
560
599
/* Given the desired width and the total of digit and non-digit
561
600
space we consume, see if we need any padding. format->width can
562
601
be negative (meaning no padding), but this code still works in
563
602
that case. */
564
603
n_padding = format -> width -
565
- (n_non_digit_non_padding + spec -> n_grouped_digits );
604
+ (n_non_digit_non_padding + spec -> n_grouped_digits
605
+ + spec -> n_grouped_frac_digits - spec -> n_frac );
566
606
if (n_padding > 0 ) {
567
607
/* Some padding is needed. Determine if it's left, space, or right. */
568
608
switch (format -> align ) {
@@ -593,7 +633,7 @@ calc_number_widths(NumberFieldWidths *spec, Py_ssize_t n_prefix,
593
633
594
634
return spec -> n_lpadding + spec -> n_sign + spec -> n_prefix +
595
635
spec -> n_spadding + spec -> n_grouped_digits + spec -> n_decimal +
596
- spec -> n_remainder + spec -> n_rpadding ;
636
+ spec -> n_grouped_frac_digits + spec -> n_remainder + spec -> n_rpadding ;
597
637
}
598
638
599
639
/* Fill in the digit parts of a number's string representation,
@@ -677,6 +717,19 @@ fill_number(_PyUnicodeWriter *writer, const NumberFieldWidths *spec,
677
717
d_pos += 1 ;
678
718
}
679
719
720
+ if (spec -> n_frac ) {
721
+ r = _PyUnicode_InsertThousandsGrouping (
722
+ writer , spec -> n_grouped_frac_digits ,
723
+ digits , d_pos , spec -> n_frac , spec -> n_frac ,
724
+ locale -> grouping , locale -> frac_thousands_sep , NULL );
725
+ if (r == -1 ) {
726
+ return -1 ;
727
+ }
728
+ assert (r == spec -> n_grouped_frac_digits );
729
+ d_pos += spec -> n_frac ;
730
+ writer -> pos += spec -> n_grouped_frac_digits ;
731
+ }
732
+
680
733
if (spec -> n_remainder ) {
681
734
_PyUnicode_FastCopyCharacters (
682
735
writer -> buffer , writer -> pos ,
@@ -701,7 +754,8 @@ static const char no_grouping[1] = {CHAR_MAX};
701
754
LT_CURRENT_LOCALE, a hard-coded locale if LT_DEFAULT_LOCALE or
702
755
LT_UNDERSCORE_LOCALE/LT_UNDER_FOUR_LOCALE, or none if LT_NO_LOCALE. */
703
756
static int
704
- get_locale_info (enum LocaleType type , LocaleInfo * locale_info )
757
+ get_locale_info (enum LocaleType type , enum LocaleType frac_type ,
758
+ LocaleInfo * locale_info )
705
759
{
706
760
switch (type ) {
707
761
case LT_CURRENT_LOCALE : {
@@ -746,6 +800,15 @@ get_locale_info(enum LocaleType type, LocaleInfo *locale_info)
746
800
locale_info -> grouping = no_grouping ;
747
801
break ;
748
802
}
803
+ if (frac_type == LT_UNDERSCORE_LOCALE ) {
804
+ locale_info -> frac_thousands_sep = PyUnicode_FromOrdinal ('_' );
805
+ if (locale_info -> grouping == no_grouping ) {
806
+ locale_info -> grouping = "\3" ;
807
+ }
808
+ }
809
+ else {
810
+ locale_info -> frac_thousands_sep = Py_GetConstant (Py_CONSTANT_EMPTY_STR );
811
+ }
749
812
return 0 ;
750
813
}
751
814
@@ -754,6 +817,7 @@ free_locale_info(LocaleInfo *locale_info)
754
817
{
755
818
Py_XDECREF (locale_info -> decimal_point );
756
819
Py_XDECREF (locale_info -> thousands_sep );
820
+ Py_XDECREF (locale_info -> frac_thousands_sep );
757
821
PyMem_Free (locale_info -> grouping_buffer );
758
822
}
759
823
@@ -1005,13 +1069,13 @@ format_long_internal(PyObject *value, const InternalFormatSpec *format,
1005
1069
1006
1070
/* Determine the grouping, separator, and decimal point, if any. */
1007
1071
if (get_locale_info (format -> type == 'n' ? LT_CURRENT_LOCALE :
1008
- format -> thousands_separators ,
1072
+ format -> thousands_separators , 0 ,
1009
1073
& locale ) == -1 )
1010
1074
goto done ;
1011
1075
1012
1076
/* Calculate how much memory we'll need. */
1013
1077
n_total = calc_number_widths (& spec , n_prefix , sign_char , inumeric_chars ,
1014
- inumeric_chars + n_digits , n_remainder , 0 ,
1078
+ inumeric_chars + n_digits , n_remainder , 0 , 0 ,
1015
1079
& locale , format , & maxchar );
1016
1080
if (n_total == -1 ) {
1017
1081
goto done ;
@@ -1046,6 +1110,7 @@ format_float_internal(PyObject *value,
1046
1110
char * buf = NULL ; /* buffer returned from PyOS_double_to_string */
1047
1111
Py_ssize_t n_digits ;
1048
1112
Py_ssize_t n_remainder ;
1113
+ Py_ssize_t n_frac ;
1049
1114
Py_ssize_t n_total ;
1050
1115
int has_decimal ;
1051
1116
double val ;
@@ -1125,7 +1190,8 @@ format_float_internal(PyObject *value,
1125
1190
if (format -> sign != '+' && format -> sign != ' '
1126
1191
&& format -> width == -1
1127
1192
&& format -> type != 'n'
1128
- && !format -> thousands_separators )
1193
+ && !format -> thousands_separators
1194
+ && !format -> frac_thousands_separator )
1129
1195
{
1130
1196
/* Fast path */
1131
1197
result = _PyUnicodeWriter_WriteASCIIString (writer , buf , n_digits );
@@ -1151,18 +1217,20 @@ format_float_internal(PyObject *value,
1151
1217
1152
1218
/* Determine if we have any "remainder" (after the digits, might include
1153
1219
decimal or exponent or both (or neither)) */
1154
- parse_number (unicode_tmp , index , index + n_digits , & n_remainder , & has_decimal );
1220
+ parse_number (unicode_tmp , index , index + n_digits ,
1221
+ & n_remainder , & n_frac , & has_decimal );
1155
1222
1156
1223
/* Determine the grouping, separator, and decimal point, if any. */
1157
1224
if (get_locale_info (format -> type == 'n' ? LT_CURRENT_LOCALE :
1158
1225
format -> thousands_separators ,
1226
+ format -> frac_thousands_separator ,
1159
1227
& locale ) == -1 )
1160
1228
goto done ;
1161
1229
1162
1230
/* Calculate how much memory we'll need. */
1163
1231
n_total = calc_number_widths (& spec , 0 , sign_char , index ,
1164
- index + n_digits , n_remainder , has_decimal ,
1165
- & locale , format , & maxchar );
1232
+ index + n_digits , n_remainder , n_frac ,
1233
+ has_decimal , & locale , format , & maxchar );
1166
1234
if (n_total == -1 ) {
1167
1235
goto done ;
1168
1236
}
@@ -1202,6 +1270,8 @@ format_complex_internal(PyObject *value,
1202
1270
Py_ssize_t n_im_digits ;
1203
1271
Py_ssize_t n_re_remainder ;
1204
1272
Py_ssize_t n_im_remainder ;
1273
+ Py_ssize_t n_re_frac ;
1274
+ Py_ssize_t n_im_frac ;
1205
1275
Py_ssize_t n_re_total ;
1206
1276
Py_ssize_t n_im_total ;
1207
1277
int re_has_decimal ;
@@ -1330,13 +1400,14 @@ format_complex_internal(PyObject *value,
1330
1400
/* Determine if we have any "remainder" (after the digits, might include
1331
1401
decimal or exponent or both (or neither)) */
1332
1402
parse_number (re_unicode_tmp , i_re , i_re + n_re_digits ,
1333
- & n_re_remainder , & re_has_decimal );
1403
+ & n_re_remainder , & n_re_frac , & re_has_decimal );
1334
1404
parse_number (im_unicode_tmp , i_im , i_im + n_im_digits ,
1335
- & n_im_remainder , & im_has_decimal );
1405
+ & n_im_remainder , & n_im_frac , & im_has_decimal );
1336
1406
1337
1407
/* Determine the grouping, separator, and decimal point, if any. */
1338
1408
if (get_locale_info (format -> type == 'n' ? LT_CURRENT_LOCALE :
1339
1409
format -> thousands_separators ,
1410
+ format -> frac_thousands_separator ,
1340
1411
& locale ) == -1 )
1341
1412
goto done ;
1342
1413
@@ -1349,8 +1420,8 @@ format_complex_internal(PyObject *value,
1349
1420
/* Calculate how much memory we'll need. */
1350
1421
n_re_total = calc_number_widths (& re_spec , 0 , re_sign_char ,
1351
1422
i_re , i_re + n_re_digits , n_re_remainder ,
1352
- re_has_decimal , & locale , & tmp_format ,
1353
- & maxchar );
1423
+ n_re_frac , re_has_decimal , & locale ,
1424
+ & tmp_format , & maxchar );
1354
1425
if (n_re_total == -1 ) {
1355
1426
goto done ;
1356
1427
}
@@ -1362,8 +1433,8 @@ format_complex_internal(PyObject *value,
1362
1433
tmp_format .sign = '+' ;
1363
1434
n_im_total = calc_number_widths (& im_spec , 0 , im_sign_char ,
1364
1435
i_im , i_im + n_im_digits , n_im_remainder ,
1365
- im_has_decimal , & locale , & tmp_format ,
1366
- & maxchar );
1436
+ n_im_frac , im_has_decimal , & locale ,
1437
+ & tmp_format , & maxchar );
1367
1438
if (n_im_total == -1 ) {
1368
1439
goto done ;
1369
1440
}
0 commit comments