@@ -32,14 +32,20 @@ invalid_comma_type(Py_UCS4 presentation_type)
3232{
3333 if (presentation_type > 32 && presentation_type < 128 )
3434 PyErr_Format (PyExc_ValueError ,
35- "Cannot specify ',' with '%c'." ,
35+ "Cannot specify ',' or '_' with '%c'." ,
3636 (char )presentation_type );
3737 else
3838 PyErr_Format (PyExc_ValueError ,
39- "Cannot specify ',' with '\\x%x'." ,
39+ "Cannot specify ',' or '_' with '\\x%x'." ,
4040 (unsigned int )presentation_type );
4141}
4242
43+ static void
44+ invalid_comma_and_underscore ()
45+ {
46+ PyErr_Format (PyExc_ValueError , "Cannot specify both ',' and '_'." );
47+ }
48+
4349/*
4450 get_integer consumes 0 or more decimal digit characters from an
4551 input string, updates *result with the corresponding positive
@@ -108,6 +114,12 @@ is_sign_element(Py_UCS4 c)
108114 }
109115}
110116
117+ /* Locale type codes. LT_NO_LOCALE must be zero. */
118+ #define LT_NO_LOCALE 0
119+ #define LT_DEFAULT_LOCALE 1
120+ #define LT_UNDERSCORE_LOCALE 2
121+ #define LT_UNDER_FOUR_LOCALE 3
122+ #define LT_CURRENT_LOCALE 4
111123
112124typedef struct {
113125 Py_UCS4 fill_char ;
@@ -223,9 +235,22 @@ parse_internal_render_format_spec(PyObject *format_spec,
223235
224236 /* Comma signifies add thousands separators */
225237 if (end - pos && READ_spec (pos ) == ',' ) {
226- format -> thousands_separators = 1 ;
238+ format -> thousands_separators = LT_DEFAULT_LOCALE ;
227239 ++ pos ;
228240 }
241+ /* Underscore signifies add thousands separators */
242+ if (end - pos && READ_spec (pos ) == '_' ) {
243+ if (format -> thousands_separators != 0 ) {
244+ invalid_comma_and_underscore ();
245+ return 0 ;
246+ }
247+ format -> thousands_separators = LT_UNDERSCORE_LOCALE ;
248+ ++ pos ;
249+ }
250+ if (end - pos && READ_spec (pos ) == ',' ) {
251+ invalid_comma_and_underscore ();
252+ return 0 ;
253+ }
229254
230255 /* Parse field precision */
231256 if (end - pos && READ_spec (pos ) == '.' ) {
@@ -275,6 +300,16 @@ parse_internal_render_format_spec(PyObject *format_spec,
275300 case '\0' :
276301 /* These are allowed. See PEP 378.*/
277302 break ;
303+ case 'b' :
304+ case 'o' :
305+ case 'x' :
306+ case 'X' :
307+ /* Underscores are allowed in bin/oct/hex. See PEP 515. */
308+ if (format -> thousands_separators == LT_UNDERSCORE_LOCALE ) {
309+ /* Every four digits, not every three, in bin/oct/hex. */
310+ format -> thousands_separators = LT_UNDER_FOUR_LOCALE ;
311+ break ;
312+ }
278313 default :
279314 invalid_comma_type (format -> type );
280315 return 0 ;
@@ -351,11 +386,6 @@ fill_padding(_PyUnicodeWriter *writer,
351386/*********** common routines for numeric formatting *********************/
352387/************************************************************************/
353388
354- /* Locale type codes. */
355- #define LT_CURRENT_LOCALE 0
356- #define LT_DEFAULT_LOCALE 1
357- #define LT_NO_LOCALE 2
358-
359389/* Locale info needed for formatting integers and the part of floats
360390 before and including the decimal. Note that locales only support
361391 8-bit chars, not unicode. */
@@ -667,8 +697,8 @@ static const char no_grouping[1] = {CHAR_MAX};
667697
668698/* Find the decimal point character(s?), thousands_separator(s?), and
669699 grouping description, either for the current locale if type is
670- LT_CURRENT_LOCALE, a hard-coded locale if LT_DEFAULT_LOCALE, or
671- none if LT_NO_LOCALE. */
700+ LT_CURRENT_LOCALE, a hard-coded locale if LT_DEFAULT_LOCALE or
701+ LT_UNDERSCORE_LOCALE/LT_UNDER_FOUR_LOCALE, or none if LT_NO_LOCALE. */
672702static int
673703get_locale_info (int type , LocaleInfo * locale_info )
674704{
@@ -691,16 +721,22 @@ get_locale_info(int type, LocaleInfo *locale_info)
691721 break ;
692722 }
693723 case LT_DEFAULT_LOCALE :
724+ case LT_UNDERSCORE_LOCALE :
725+ case LT_UNDER_FOUR_LOCALE :
694726 locale_info -> decimal_point = PyUnicode_FromOrdinal ('.' );
695- locale_info -> thousands_sep = PyUnicode_FromOrdinal (',' );
727+ locale_info -> thousands_sep = PyUnicode_FromOrdinal (
728+ type == LT_DEFAULT_LOCALE ? ',' : '_' );
696729 if (!locale_info -> decimal_point || !locale_info -> thousands_sep ) {
697730 Py_XDECREF (locale_info -> decimal_point );
698731 Py_XDECREF (locale_info -> thousands_sep );
699732 return -1 ;
700733 }
701- locale_info -> grouping = "\3" ; /* Group every 3 characters. The
734+ if (type != LT_UNDER_FOUR_LOCALE )
735+ locale_info -> grouping = "\3" ; /* Group every 3 characters. The
702736 (implicit) trailing 0 means repeat
703737 infinitely. */
738+ else
739+ locale_info -> grouping = "\4" ; /* Bin/oct/hex group every four. */
704740 break ;
705741 case LT_NO_LOCALE :
706742 locale_info -> decimal_point = PyUnicode_FromOrdinal ('.' );
@@ -952,9 +988,7 @@ format_long_internal(PyObject *value, const InternalFormatSpec *format,
952988
953989 /* Determine the grouping, separator, and decimal point, if any. */
954990 if (get_locale_info (format -> type == 'n' ? LT_CURRENT_LOCALE :
955- (format -> thousands_separators ?
956- LT_DEFAULT_LOCALE :
957- LT_NO_LOCALE ),
991+ format -> thousands_separators ,
958992 & locale ) == -1 )
959993 goto done ;
960994
@@ -1099,9 +1133,7 @@ format_float_internal(PyObject *value,
10991133
11001134 /* Determine the grouping, separator, and decimal point, if any. */
11011135 if (get_locale_info (format -> type == 'n' ? LT_CURRENT_LOCALE :
1102- (format -> thousands_separators ?
1103- LT_DEFAULT_LOCALE :
1104- LT_NO_LOCALE ),
1136+ format -> thousands_separators ,
11051137 & locale ) == -1 )
11061138 goto done ;
11071139
@@ -1277,9 +1309,7 @@ format_complex_internal(PyObject *value,
12771309
12781310 /* Determine the grouping, separator, and decimal point, if any. */
12791311 if (get_locale_info (format -> type == 'n' ? LT_CURRENT_LOCALE :
1280- (format -> thousands_separators ?
1281- LT_DEFAULT_LOCALE :
1282- LT_NO_LOCALE ),
1312+ format -> thousands_separators ,
12831313 & locale ) == -1 )
12841314 goto done ;
12851315
0 commit comments