Added ',' thousands grouping to int.__format__. See PEP 378.

This is incomplete, but I want to get some version into the next alpha. I am still working on:
Documentation.
More tests.
Implement for floats.

In addition, there's an existing bug with 'n' formatting that carries forward to thousands grouping (issue 5515).
This commit is contained in:
Eric Smith 2009-04-03 14:45:06 +00:00
parent f8c8b6d3ea
commit a3b1ac8dca
10 changed files with 190 additions and 88 deletions

View File

@ -91,13 +91,25 @@ PyAPI_FUNC(int) PyBytes_AsStringAndSize(
into the string pointed to by buffer. For the argument descriptions, into the string pointed to by buffer. For the argument descriptions,
see Objects/stringlib/localeutil.h */ see Objects/stringlib/localeutil.h */
PyAPI_FUNC(int) _PyBytes_InsertThousandsGrouping(char *buffer, PyAPI_FUNC(int) _PyBytes_InsertThousandsGroupingLocale(char *buffer,
Py_ssize_t n_buffer, Py_ssize_t n_buffer,
Py_ssize_t n_digits, Py_ssize_t n_digits,
Py_ssize_t buf_size, Py_ssize_t buf_size,
Py_ssize_t *count, Py_ssize_t *count,
int append_zero_char); int append_zero_char);
/* Using explicit passed-in values, insert the thousands grouping
into the string pointed to by buffer. For the argument descriptions,
see Objects/stringlib/localeutil.h */
PyAPI_FUNC(int) _PyBytes_InsertThousandsGrouping(char *buffer,
Py_ssize_t n_buffer,
Py_ssize_t n_digits,
Py_ssize_t buf_size,
Py_ssize_t *count,
int append_zero_char,
const char *grouping,
const char *thousands_sep);
/* Flags used by string formatting */ /* Flags used by string formatting */
#define F_LJUST (1<<0) #define F_LJUST (1<<0)
#define F_SIGN (1<<1) #define F_SIGN (1<<1)

View File

@ -1482,13 +1482,24 @@ PyAPI_FUNC(PyObject *) _PyUnicode_XStrip(
into the string pointed to by buffer. For the argument descriptions, into the string pointed to by buffer. For the argument descriptions,
see Objects/stringlib/localeutil.h */ see Objects/stringlib/localeutil.h */
PyAPI_FUNC(int) _PyUnicode_InsertThousandsGrouping(Py_UNICODE *buffer, PyAPI_FUNC(int) _PyUnicode_InsertThousandsGroupingLocale(Py_UNICODE *buffer,
Py_ssize_t n_buffer, Py_ssize_t n_buffer,
Py_ssize_t n_digits, Py_ssize_t n_digits,
Py_ssize_t buf_size, Py_ssize_t buf_size,
Py_ssize_t *count, Py_ssize_t *count,
int append_zero_char); int append_zero_char);
/* Using explicit passed-in values, insert the thousands grouping
into the string pointed to by buffer. For the argument descriptions,
see Objects/stringlib/localeutil.h */
PyAPI_FUNC(int) _PyUnicode_InsertThousandsGrouping(Py_UNICODE *buffer,
Py_ssize_t n_buffer,
Py_ssize_t n_digits,
Py_ssize_t buf_size,
Py_ssize_t *count,
int append_zero_char,
const char *grouping,
const char *thousands_sep);
/* === Characters Type APIs =============================================== */ /* === Characters Type APIs =============================================== */
/* Helper array used by Py_UNICODE_ISSPACE(). */ /* Helper array used by Py_UNICODE_ISSPACE(). */

View File

@ -338,6 +338,15 @@ class TypesTests(unittest.TestCase):
test(123456, "#012X", '0X000001E240') test(123456, "#012X", '0X000001E240')
test(-123456, "#012X", '-0X00001E240') test(-123456, "#012X", '-0X00001E240')
test(123, ',', '123')
test(-123, ',', '-123')
test(1234, ',', '1,234')
test(-1234, ',', '-1,234')
test(123456, ',', '123,456')
test(-123456, ',', '-123,456')
test(1234567, ',', '1,234,567')
test(-1234567, ',', '-1,234,567')
# make sure these are errors # make sure these are errors
# precision disallowed # precision disallowed
@ -347,6 +356,8 @@ class TypesTests(unittest.TestCase):
# format spec must be string # format spec must be string
self.assertRaises(TypeError, 3 .__format__, None) self.assertRaises(TypeError, 3 .__format__, None)
self.assertRaises(TypeError, 3 .__format__, 0) self.assertRaises(TypeError, 3 .__format__, 0)
# can't have ',' with 'n'
self.assertRaises(ValueError, 3 .__format__, ",n")
# ensure that only int and float type specifiers work # ensure that only int and float type specifiers work
for format_spec in ([chr(x) for x in range(ord('a'), ord('z')+1)] + for format_spec in ([chr(x) for x in range(ord('a'), ord('z')+1)] +

View File

@ -583,6 +583,7 @@ PyBytes_AsStringAndSize(register PyObject *obj,
#include "stringlib/transmogrify.h" #include "stringlib/transmogrify.h"
#define _Py_InsertThousandsGrouping _PyBytes_InsertThousandsGrouping #define _Py_InsertThousandsGrouping _PyBytes_InsertThousandsGrouping
#define _Py_InsertThousandsGroupingLocale _PyBytes_InsertThousandsGroupingLocale
#include "stringlib/localeutil.h" #include "stringlib/localeutil.h"
PyObject * PyObject *

View File

@ -120,6 +120,7 @@ typedef struct {
int alternate; int alternate;
STRINGLIB_CHAR sign; STRINGLIB_CHAR sign;
Py_ssize_t width; Py_ssize_t width;
int thousands_separators;
Py_ssize_t precision; Py_ssize_t precision;
STRINGLIB_CHAR type; STRINGLIB_CHAR type;
} InternalFormatSpec; } InternalFormatSpec;
@ -149,6 +150,7 @@ parse_internal_render_format_spec(STRINGLIB_CHAR *format_spec,
format->alternate = 0; format->alternate = 0;
format->sign = '\0'; format->sign = '\0';
format->width = -1; format->width = -1;
format->thousands_separators = 0;
format->precision = -1; format->precision = -1;
format->type = default_type; format->type = default_type;
@ -201,6 +203,12 @@ parse_internal_render_format_spec(STRINGLIB_CHAR *format_spec,
format->width = -1; format->width = -1;
} }
/* Comma signifies add thousands separators */
if (end-ptr && ptr[0] == ',') {
format->thousands_separators = 1;
++ptr;
}
/* Parse field precision */ /* Parse field precision */
if (end-ptr && ptr[0] == '.') { if (end-ptr && ptr[0] == '.') {
++ptr; ++ptr;
@ -230,6 +238,11 @@ parse_internal_render_format_spec(STRINGLIB_CHAR *format_spec,
++ptr; ++ptr;
} }
if (format->type == 'n' && format->thousands_separators) {
PyErr_Format(PyExc_ValueError, "Cannot specify ',' with 'n'.");
return 0;
}
return 1; return 1;
} }
@ -630,8 +643,13 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format,
if (format->type == 'n') if (format->type == 'n')
/* Compute how many additional chars we need to allocate /* Compute how many additional chars we need to allocate
to hold the thousands grouping. */ to hold the thousands grouping. */
STRINGLIB_GROUPING(NULL, n_digits, n_digits, STRINGLIB_GROUPING_LOCALE(NULL, n_digits, n_digits,
0, &n_grouping_chars, 0); 0, &n_grouping_chars, 0);
if (format->thousands_separators)
/* Compute how many additional chars we need to allocate
to hold the thousands grouping. */
STRINGLIB_GROUPING(NULL, n_digits, n_digits,
0, &n_grouping_chars, 0, "\3", ",");
/* Calculate the widths of the various leading and trailing parts */ /* Calculate the widths of the various leading and trailing parts */
calc_number_widths(&spec, sign, n_prefix, n_digits + n_grouping_chars, calc_number_widths(&spec, sign, n_prefix, n_digits + n_grouping_chars,
@ -670,11 +688,22 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format,
reserved enough space. */ reserved enough space. */
STRINGLIB_CHAR *pstart = p + n_leading_chars; STRINGLIB_CHAR *pstart = p + n_leading_chars;
#ifndef NDEBUG #ifndef NDEBUG
int r = int r;
#endif #endif
STRINGLIB_GROUPING(pstart, n_digits, n_digits, if (format->type == 'n')
#ifndef NDEBUG
r =
#endif
STRINGLIB_GROUPING_LOCALE(pstart, n_digits, n_digits,
spec.n_total+n_grouping_chars-n_leading_chars, spec.n_total+n_grouping_chars-n_leading_chars,
NULL, 0); NULL, 0);
else
#ifndef NDEBUG
r =
STRINGLIB_GROUPING(pstart, n_digits, n_digits,
spec.n_total+n_grouping_chars-n_leading_chars,
NULL, 0, "\3", ",");
#endif
assert(r); assert(r);
} }

View File

@ -18,11 +18,13 @@
* @append_zero_char: If non-zero, put a trailing zero at the end of * @append_zero_char: If non-zero, put a trailing zero at the end of
* of the resulting string, if and only if we modified the * of the resulting string, if and only if we modified the
* string. * string.
* @grouping: see definition in localeconv().
* @thousands_sep: see definition in localeconv().
* *
* Inserts thousand grouping characters (as defined in the current * Inserts thousand grouping characters (as defined by grouping and
* locale) into the string between buffer and buffer+n_digits. If * thousands_sep) into the string between buffer and buffer+n_digits.
* count is non-NULL, don't do any formatting, just count the number * If count is non-NULL, don't do any formatting, just count the
* of characters to insert. This is used by the caller to * number of characters to insert. This is used by the caller to
* appropriately resize the buffer, if needed. If count is non-NULL, * appropriately resize the buffer, if needed. If count is non-NULL,
* buffer can be NULL (it is not dereferenced at all in that case). * buffer can be NULL (it is not dereferenced at all in that case).
* *
@ -38,11 +40,10 @@ _Py_InsertThousandsGrouping(STRINGLIB_CHAR *buffer,
Py_ssize_t n_digits, Py_ssize_t n_digits,
Py_ssize_t buf_size, Py_ssize_t buf_size,
Py_ssize_t *count, Py_ssize_t *count,
int append_zero_char) int append_zero_char,
const char *grouping,
const char *thousands_sep)
{ {
struct lconv *locale_data = localeconv();
const char *grouping = locale_data->grouping;
const char *thousands_sep = locale_data->thousands_sep;
Py_ssize_t thousands_sep_len = strlen(thousands_sep); Py_ssize_t thousands_sep_len = strlen(thousands_sep);
STRINGLIB_CHAR *pend = NULL; /* current end of buffer */ STRINGLIB_CHAR *pend = NULL; /* current end of buffer */
STRINGLIB_CHAR *pmax = NULL; /* max of buffer */ STRINGLIB_CHAR *pmax = NULL; /* max of buffer */
@ -127,4 +128,38 @@ _Py_InsertThousandsGrouping(STRINGLIB_CHAR *buffer,
} }
return 1; return 1;
} }
/**
* _Py_InsertThousandsGroupingLocale:
* @buffer: A pointer to the start of a string.
* @n_buffer: The length of the string.
* @n_digits: The number of digits in the string, in which we want
* to put the grouping chars.
* @buf_size: The maximum size of the buffer pointed to by buffer.
* @count: If non-NULL, points to a variable that will receive the
* number of characters we need to insert (and no formatting
* will actually occur).
* @append_zero_char: If non-zero, put a trailing zero at the end of
* of the resulting string, if and only if we modified the
* string.
*
* Reads thee current locale and calls _Py_InsertThousandsGrouping().
**/
int
_Py_InsertThousandsGroupingLocale(STRINGLIB_CHAR *buffer,
Py_ssize_t n_buffer,
Py_ssize_t n_digits,
Py_ssize_t buf_size,
Py_ssize_t *count,
int append_zero_char)
{
struct lconv *locale_data = localeconv();
const char *grouping = locale_data->grouping;
const char *thousands_sep = locale_data->thousands_sep;
return _Py_InsertThousandsGrouping(buffer, n_buffer, n_digits,
buf_size, count,
append_zero_char, grouping,
thousands_sep);
}
#endif /* STRINGLIB_LOCALEUTIL_H */ #endif /* STRINGLIB_LOCALEUTIL_H */

View File

@ -24,5 +24,6 @@
#define STRINGLIB_CMP memcmp #define STRINGLIB_CMP memcmp
#define STRINGLIB_TOSTR PyObject_Str #define STRINGLIB_TOSTR PyObject_Str
#define STRINGLIB_GROUPING _PyBytes_InsertThousandsGrouping #define STRINGLIB_GROUPING _PyBytes_InsertThousandsGrouping
#define STRINGLIB_GROUPING_LOCALE _PyBytes_InsertThousandsGroupingLocale
#define STRINGLIB_TOASCII PyObject_Repr #define STRINGLIB_TOASCII PyObject_Repr
#endif /* !STRINGLIB_STRINGDEFS_H */ #endif /* !STRINGLIB_STRINGDEFS_H */

View File

@ -22,6 +22,7 @@
#define STRINGLIB_RESIZE PyUnicode_Resize #define STRINGLIB_RESIZE PyUnicode_Resize
#define STRINGLIB_CHECK PyUnicode_Check #define STRINGLIB_CHECK PyUnicode_Check
#define STRINGLIB_GROUPING _PyUnicode_InsertThousandsGrouping #define STRINGLIB_GROUPING _PyUnicode_InsertThousandsGrouping
#define STRINGLIB_GROUPING_LOCALE _PyUnicode_InsertThousandsGroupingLocale
#if PY_VERSION_HEX < 0x03000000 #if PY_VERSION_HEX < 0x03000000
#define STRINGLIB_TOSTR PyObject_Unicode #define STRINGLIB_TOSTR PyObject_Unicode

View File

@ -5635,6 +5635,7 @@ int PyUnicode_EncodeDecimal(Py_UNICODE *s,
#include "stringlib/partition.h" #include "stringlib/partition.h"
#define _Py_InsertThousandsGrouping _PyUnicode_InsertThousandsGrouping #define _Py_InsertThousandsGrouping _PyUnicode_InsertThousandsGrouping
#define _Py_InsertThousandsGroupingLocale _PyUnicode_InsertThousandsGroupingLocale
#include "stringlib/localeutil.h" #include "stringlib/localeutil.h"
/* helper macro to fixup start/end slice values */ /* helper macro to fixup start/end slice values */

View File

@ -368,7 +368,7 @@ add_thousands_grouping(char* buffer, size_t buf_size)
/* At this point, p points just past the right-most character we /* At this point, p points just past the right-most character we
want to format. We need to add the grouping string for the want to format. We need to add the grouping string for the
characters between buffer and p. */ characters between buffer and p. */
return _PyBytes_InsertThousandsGrouping(buffer, len, p-buffer, return _PyBytes_InsertThousandsGroupingLocale(buffer, len, p-buffer,
buf_size, NULL, 1); buf_size, NULL, 1);
} }