summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_types.py38
-rw-r--r--Lib/test/test_unicode.py4
-rw-r--r--Objects/stringlib/formatter.h56
3 files changed, 85 insertions, 13 deletions
diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py
index d7deea0..360cfe4 100644
--- a/Lib/test/test_types.py
+++ b/Lib/test/test_types.py
@@ -293,6 +293,40 @@ class TypesTests(unittest.TestCase):
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
# 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)
+ # Alternate formatting is not supported
+ self.assertRaises(ValueError, format, 0.0, '#')
+ self.assertRaises(ValueError, format, 0.0, '#20f')
+
def test_main():
run_unittest(TypesTests)
diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py
index fb904bf..e6b3cb0 100644
--- a/Lib/test/test_unicode.py
+++ b/Lib/test/test_unicode.py
@@ -700,6 +700,10 @@ class UnicodeTest(
self.assertRaises(ValueError, 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):
string_tests.MixinStrUnicodeUserStringTest.test_formatting(self)
# Testing Unicode formatting strings...
diff --git a/Objects/stringlib/formatter.h b/Objects/stringlib/formatter.h
index 018121a..9b7d607 100644
--- a/Objects/stringlib/formatter.h
+++ b/Objects/stringlib/formatter.h
@@ -89,6 +89,7 @@ is_sign_element(STRINGLIB_CHAR c)
typedef struct {
STRINGLIB_CHAR fill_char;
STRINGLIB_CHAR align;
+ int alternate;
STRINGLIB_CHAR sign;
Py_ssize_t width;
Py_ssize_t precision;
@@ -117,6 +118,7 @@ parse_internal_render_format_spec(STRINGLIB_CHAR *format_spec,
format->fill_char = '\0';
format->align = '\0';
+ format->alternate = 0;
format->sign = '\0';
format->width = -1;
format->precision = -1;
@@ -154,6 +156,13 @@ parse_internal_render_format_spec(STRINGLIB_CHAR *format_spec,
++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 */
specified_width = get_integer(&ptr, end, &format->width);
@@ -221,7 +230,8 @@ typedef struct {
and more efficient enough to justify a little obfuscation? */
static void
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_spadding = 0;
@@ -232,13 +242,15 @@ calc_number_widths(NumberFieldWidths *r, STRINGLIB_CHAR actual_sign,
r->n_rsign = 0;
/* 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
sign of the number
+ prefix is given (it's for the '0x' prefix)
+
digits is already known
the total width is either given, or computed from the
@@ -363,6 +375,14 @@ format_string_internal(PyObject *value, const InternalFormatSpec *format)
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 */
if (format->align == '=') {
PyErr_SetString(PyExc_ValueError,
@@ -505,31 +525,33 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format,
}
else {
int base;
- int leading_chars_to_skip; /* Number of characters added by
- PyNumber_ToBase that we want to
- skip over. */
+ int leading_chars_to_skip = 0; /* Number of characters added by
+ PyNumber_ToBase that we want to
+ skip over. */
/* Compute the base and how many characters will be added by
PyNumber_ToBase */
switch (format->type) {
case 'b':
base = 2;
- leading_chars_to_skip = 2; /* 0b */
+ if (!format->alternate)
+ leading_chars_to_skip = 2; /* 0b */
break;
case 'o':
base = 8;
- leading_chars_to_skip = 2; /* 0o */
+ if (!format->alternate)
+ leading_chars_to_skip = 2; /* 0o */
break;
case 'x':
case 'X':
base = 16;
- leading_chars_to_skip = 2; /* 0x */
+ if (!format->alternate)
+ leading_chars_to_skip = 2; /* 0x */
break;
default: /* shouldn't be needed, but stops a compiler warning */
case 'd':
case 'n':
base = 10;
- leading_chars_to_skip = 0;
break;
}
@@ -564,7 +586,7 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format,
0, &n_grouping_chars, 0);
/* 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 */
result = STRINGLIB_NEW(NULL, spec.n_total);
@@ -670,6 +692,14 @@ format_float_internal(PyObject *value,
Py_UNICODE unicodebuf[FLOAT_FORMATBUFLEN];
#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
snprintf. then, if needed, convert to unicode. */
@@ -730,7 +760,7 @@ format_float_internal(PyObject *value,
--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 */
result = STRINGLIB_NEW(NULL, spec.n_total);