Forward port of r64958.
Added '#' formatting to integers. This adds the 0b, 0o, or 0x prefix for bin, oct, hex. There's still one failing case, and I need to finish the docs. I hope to finish those today.
This commit is contained in:
parent
e840b9ad51
commit
b1ebcc6b0b
|
@ -293,6 +293,40 @@ class TypesTests(unittest.TestCase):
|
||||||
test(1234, "+b", "+10011010010")
|
test(1234, "+b", "+10011010010")
|
||||||
test(-1234, "+b", "-10011010010")
|
test(-1234, "+b", "-10011010010")
|
||||||
|
|
||||||
|
# alternate (#) formatting
|
||||||
|
test(0, "#b", '0b0')
|
||||||
|
test(0, "-#b", '0b0')
|
||||||
|
test(1, "-#b", '0b1')
|
||||||
|
test(-1, "-#b", '-0b1')
|
||||||
|
test(-1, "-#5b", ' -0b1')
|
||||||
|
test(1, "+#5b", ' +0b1')
|
||||||
|
test(100, "+#b", '+0b1100100')
|
||||||
|
# test(100, "#012b", '0b001100100')
|
||||||
|
|
||||||
|
test(0, "#o", '0o0')
|
||||||
|
test(0, "-#o", '0o0')
|
||||||
|
test(1, "-#o", '0o1')
|
||||||
|
test(-1, "-#o", '-0o1')
|
||||||
|
test(-1, "-#5o", ' -0o1')
|
||||||
|
test(1, "+#5o", ' +0o1')
|
||||||
|
test(100, "+#o", '+0o144')
|
||||||
|
|
||||||
|
test(0, "#x", '0x0')
|
||||||
|
test(0, "-#x", '0x0')
|
||||||
|
test(1, "-#x", '0x1')
|
||||||
|
test(-1, "-#x", '-0x1')
|
||||||
|
test(-1, "-#5x", ' -0x1')
|
||||||
|
test(1, "+#5x", ' +0x1')
|
||||||
|
test(100, "+#x", '+0x64')
|
||||||
|
|
||||||
|
test(0, "#X", '0X0')
|
||||||
|
test(0, "-#X", '0X0')
|
||||||
|
test(1, "-#X", '0X1')
|
||||||
|
test(-1, "-#X", '-0X1')
|
||||||
|
test(-1, "-#5X", ' -0X1')
|
||||||
|
test(1, "+#5X", ' +0X1')
|
||||||
|
test(100, "+#X", '+0X64')
|
||||||
|
|
||||||
# make sure these are errors
|
# make sure these are errors
|
||||||
|
|
||||||
# precision disallowed
|
# precision disallowed
|
||||||
|
@ -509,6 +543,10 @@ class TypesTests(unittest.TestCase):
|
||||||
self.assertRaises(ValueError, format, 1e-100, format_spec)
|
self.assertRaises(ValueError, format, 1e-100, format_spec)
|
||||||
self.assertRaises(ValueError, format, -1e-100, format_spec)
|
self.assertRaises(ValueError, format, -1e-100, format_spec)
|
||||||
|
|
||||||
|
# Alternate formatting is not supported
|
||||||
|
self.assertRaises(ValueError, format, 0.0, '#')
|
||||||
|
self.assertRaises(ValueError, format, 0.0, '#20f')
|
||||||
|
|
||||||
|
|
||||||
def test_main():
|
def test_main():
|
||||||
run_unittest(TypesTests)
|
run_unittest(TypesTests)
|
||||||
|
|
|
@ -700,6 +700,10 @@ class UnicodeTest(
|
||||||
self.assertRaises(ValueError, format, "", "-")
|
self.assertRaises(ValueError, format, "", "-")
|
||||||
self.assertRaises(ValueError, "{0:=s}".format, '')
|
self.assertRaises(ValueError, "{0:=s}".format, '')
|
||||||
|
|
||||||
|
# Alternate formatting is not supported
|
||||||
|
self.assertRaises(ValueError, format, '', '#')
|
||||||
|
self.assertRaises(ValueError, format, '', '#20')
|
||||||
|
|
||||||
def test_formatting(self):
|
def test_formatting(self):
|
||||||
string_tests.MixinStrUnicodeUserStringTest.test_formatting(self)
|
string_tests.MixinStrUnicodeUserStringTest.test_formatting(self)
|
||||||
# Testing Unicode formatting strings...
|
# Testing Unicode formatting strings...
|
||||||
|
|
|
@ -89,6 +89,7 @@ is_sign_element(STRINGLIB_CHAR c)
|
||||||
typedef struct {
|
typedef struct {
|
||||||
STRINGLIB_CHAR fill_char;
|
STRINGLIB_CHAR fill_char;
|
||||||
STRINGLIB_CHAR align;
|
STRINGLIB_CHAR align;
|
||||||
|
int alternate;
|
||||||
STRINGLIB_CHAR sign;
|
STRINGLIB_CHAR sign;
|
||||||
Py_ssize_t width;
|
Py_ssize_t width;
|
||||||
Py_ssize_t precision;
|
Py_ssize_t precision;
|
||||||
|
@ -117,6 +118,7 @@ parse_internal_render_format_spec(STRINGLIB_CHAR *format_spec,
|
||||||
|
|
||||||
format->fill_char = '\0';
|
format->fill_char = '\0';
|
||||||
format->align = '\0';
|
format->align = '\0';
|
||||||
|
format->alternate = 0;
|
||||||
format->sign = '\0';
|
format->sign = '\0';
|
||||||
format->width = -1;
|
format->width = -1;
|
||||||
format->precision = -1;
|
format->precision = -1;
|
||||||
|
@ -154,6 +156,13 @@ parse_internal_render_format_spec(STRINGLIB_CHAR *format_spec,
|
||||||
++ptr;
|
++ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If the next character is #, we're in alternate mode. This only
|
||||||
|
applies to integers. */
|
||||||
|
if (end-ptr >= 1 && ptr[0] == '#') {
|
||||||
|
format->alternate = 1;
|
||||||
|
++ptr;
|
||||||
|
}
|
||||||
|
|
||||||
/* XXX add error checking */
|
/* XXX add error checking */
|
||||||
specified_width = get_integer(&ptr, end, &format->width);
|
specified_width = get_integer(&ptr, end, &format->width);
|
||||||
|
|
||||||
|
@ -221,7 +230,8 @@ typedef struct {
|
||||||
and more efficient enough to justify a little obfuscation? */
|
and more efficient enough to justify a little obfuscation? */
|
||||||
static void
|
static void
|
||||||
calc_number_widths(NumberFieldWidths *r, STRINGLIB_CHAR actual_sign,
|
calc_number_widths(NumberFieldWidths *r, STRINGLIB_CHAR actual_sign,
|
||||||
Py_ssize_t n_digits, const InternalFormatSpec *format)
|
Py_ssize_t n_prefix, Py_ssize_t n_digits,
|
||||||
|
const InternalFormatSpec *format)
|
||||||
{
|
{
|
||||||
r->n_lpadding = 0;
|
r->n_lpadding = 0;
|
||||||
r->n_spadding = 0;
|
r->n_spadding = 0;
|
||||||
|
@ -233,12 +243,14 @@ calc_number_widths(NumberFieldWidths *r, STRINGLIB_CHAR actual_sign,
|
||||||
|
|
||||||
/* the output will look like:
|
/* the output will look like:
|
||||||
| |
|
| |
|
||||||
| <lpadding> <lsign> <spadding> <digits> <rsign> <rpadding> |
|
| <lpadding> <lsign> <prefix> <spadding> <digits> <rsign> <rpadding> |
|
||||||
| |
|
| |
|
||||||
|
|
||||||
lsign and rsign are computed from format->sign and the actual
|
lsign and rsign are computed from format->sign and the actual
|
||||||
sign of the number
|
sign of the number
|
||||||
|
|
||||||
|
prefix is given (it's for the '0x' prefix)
|
||||||
|
|
||||||
digits is already known
|
digits is already known
|
||||||
|
|
||||||
the total width is either given, or computed from the
|
the total width is either given, or computed from the
|
||||||
|
@ -363,6 +375,14 @@ format_string_internal(PyObject *value, const InternalFormatSpec *format)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* alternate is not allowed on strings */
|
||||||
|
if (format->alternate) {
|
||||||
|
PyErr_SetString(PyExc_ValueError,
|
||||||
|
"Alternate form (#) not allowed in string format "
|
||||||
|
"specifier");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
/* '=' alignment not allowed on strings */
|
/* '=' alignment not allowed on strings */
|
||||||
if (format->align == '=') {
|
if (format->align == '=') {
|
||||||
PyErr_SetString(PyExc_ValueError,
|
PyErr_SetString(PyExc_ValueError,
|
||||||
|
@ -505,7 +525,7 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format,
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int base;
|
int base;
|
||||||
int leading_chars_to_skip; /* Number of characters added by
|
int leading_chars_to_skip = 0; /* Number of characters added by
|
||||||
PyNumber_ToBase that we want to
|
PyNumber_ToBase that we want to
|
||||||
skip over. */
|
skip over. */
|
||||||
|
|
||||||
|
@ -514,22 +534,24 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format,
|
||||||
switch (format->type) {
|
switch (format->type) {
|
||||||
case 'b':
|
case 'b':
|
||||||
base = 2;
|
base = 2;
|
||||||
|
if (!format->alternate)
|
||||||
leading_chars_to_skip = 2; /* 0b */
|
leading_chars_to_skip = 2; /* 0b */
|
||||||
break;
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
base = 8;
|
base = 8;
|
||||||
|
if (!format->alternate)
|
||||||
leading_chars_to_skip = 2; /* 0o */
|
leading_chars_to_skip = 2; /* 0o */
|
||||||
break;
|
break;
|
||||||
case 'x':
|
case 'x':
|
||||||
case 'X':
|
case 'X':
|
||||||
base = 16;
|
base = 16;
|
||||||
|
if (!format->alternate)
|
||||||
leading_chars_to_skip = 2; /* 0x */
|
leading_chars_to_skip = 2; /* 0x */
|
||||||
break;
|
break;
|
||||||
default: /* shouldn't be needed, but stops a compiler warning */
|
default: /* shouldn't be needed, but stops a compiler warning */
|
||||||
case 'd':
|
case 'd':
|
||||||
case 'n':
|
case 'n':
|
||||||
base = 10;
|
base = 10;
|
||||||
leading_chars_to_skip = 0;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -564,7 +586,7 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format,
|
||||||
0, &n_grouping_chars, 0);
|
0, &n_grouping_chars, 0);
|
||||||
|
|
||||||
/* 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_digits + n_grouping_chars, format);
|
calc_number_widths(&spec, sign, 0, n_digits + n_grouping_chars, format);
|
||||||
|
|
||||||
/* Allocate a new string to hold the result */
|
/* Allocate a new string to hold the result */
|
||||||
result = STRINGLIB_NEW(NULL, spec.n_total);
|
result = STRINGLIB_NEW(NULL, spec.n_total);
|
||||||
|
@ -670,6 +692,14 @@ format_float_internal(PyObject *value,
|
||||||
Py_UNICODE unicodebuf[FLOAT_FORMATBUFLEN];
|
Py_UNICODE unicodebuf[FLOAT_FORMATBUFLEN];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* alternate is not allowed on floats. */
|
||||||
|
if (format->alternate) {
|
||||||
|
PyErr_SetString(PyExc_ValueError,
|
||||||
|
"Alternate form (#) not allowed in float format "
|
||||||
|
"specifier");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
/* first, do the conversion as 8-bit chars, using the platform's
|
/* first, do the conversion as 8-bit chars, using the platform's
|
||||||
snprintf. then, if needed, convert to unicode. */
|
snprintf. then, if needed, convert to unicode. */
|
||||||
|
|
||||||
|
@ -730,7 +760,7 @@ format_float_internal(PyObject *value,
|
||||||
--n_digits;
|
--n_digits;
|
||||||
}
|
}
|
||||||
|
|
||||||
calc_number_widths(&spec, sign, n_digits, format);
|
calc_number_widths(&spec, sign, 0, n_digits, format);
|
||||||
|
|
||||||
/* allocate a string with enough space */
|
/* allocate a string with enough space */
|
||||||
result = STRINGLIB_NEW(NULL, spec.n_total);
|
result = STRINGLIB_NEW(NULL, spec.n_total);
|
||||||
|
|
Loading…
Reference in New Issue