Kaydet (Commit) d0c84124 authored tarafından Eric Smith's avatar Eric Smith

Added '#' formatting to integers. This adds the 0b, 0o, or 0x prefix for bin,…

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.
üst a6864e0d
...@@ -192,6 +192,10 @@ class ModuleTest(unittest.TestCase): ...@@ -192,6 +192,10 @@ class ModuleTest(unittest.TestCase):
self.assertRaises(ValueError, fmt.format, "{0}", 10, 20, i=100) self.assertRaises(ValueError, fmt.format, "{0}", 10, 20, i=100)
self.assertRaises(ValueError, fmt.format, "{i}", 10, 20, i=100) self.assertRaises(ValueError, fmt.format, "{i}", 10, 20, i=100)
# Alternate formatting is not supported
self.assertRaises(ValueError, format, '', '#')
self.assertRaises(ValueError, format, '', '#20')
class BytesAliasTest(unittest.TestCase): class BytesAliasTest(unittest.TestCase):
def test_builtin(self): def test_builtin(self):
......
...@@ -357,6 +357,40 @@ class TypesTests(unittest.TestCase): ...@@ -357,6 +357,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
...@@ -461,6 +495,9 @@ class TypesTests(unittest.TestCase): ...@@ -461,6 +495,9 @@ class TypesTests(unittest.TestCase):
# format spec must be string # format spec must be string
self.assertRaises(TypeError, 3L .__format__, None) self.assertRaises(TypeError, 3L .__format__, None)
self.assertRaises(TypeError, 3L .__format__, 0) self.assertRaises(TypeError, 3L .__format__, 0)
# alternate specifier in wrong place
self.assertRaises(ValueError, 1L .__format__, "#+5x")
self.assertRaises(ValueError, 1L .__format__, "+5#x")
# 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)] +
...@@ -579,6 +616,10 @@ class TypesTests(unittest.TestCase): ...@@ -579,6 +616,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)
......
...@@ -89,6 +89,7 @@ is_sign_element(STRINGLIB_CHAR c) ...@@ -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, ...@@ -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, ...@@ -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 { ...@@ -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, ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment