]> granicus.if.org Git - python/commitdiff
Forward port of r64958.
authorEric Smith <eric@trueblade.com>
Tue, 15 Jul 2008 13:02:41 +0000 (13:02 +0000)
committerEric Smith <eric@trueblade.com>
Tue, 15 Jul 2008 13:02:41 +0000 (13:02 +0000)
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.

Lib/test/test_types.py
Lib/test/test_unicode.py
Objects/stringlib/formatter.h

index d7deea0432ae1fc5c6b9321e313267a8b87ca056..360cfe4b15ba6bb2690b3dac811edd5e189b6486 100644 (file)
@@ -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)
index fb904bf2a462b4dec05a97dd09e13cb2ec5b67c9..e6b3cb0bed0098ffaf0c0f3a732d441872b4499c 100644 (file)
@@ -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...
index 018121a4505c12b1a4a7579f83680f25d6648049..9b7d607a5a82958277e6a38cb0f144b1962850fc 100644 (file)
@@ -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);