[Python-checkins] r54137 - sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c

eric.smith python-checkins at python.org
Mon Mar 5 13:05:30 CET 2007


Author: eric.smith
Date: Mon Mar  5 13:05:29 2007
New Revision: 54137

Modified:
   sandbox/trunk/pep3101/test_simpleformat.py
   sandbox/trunk/pep3101/unicodeformat.c
Log:
Completed all floating point formats, with tests.

Modified: sandbox/trunk/pep3101/test_simpleformat.py
==============================================================================
--- sandbox/trunk/pep3101/test_simpleformat.py	(original)
+++ sandbox/trunk/pep3101/test_simpleformat.py	Mon Mar  5 13:05:29 2007
@@ -125,7 +125,7 @@
         self.formatEquals("a", "{0:c}", ord("a"))
         self.formatEquals("8_08b", "{0:08b}", 8)
         self.formatEquals("  8", "{0: >3d}", 8)
-        self.formatEquals("0.1515_.0%", "{0:.0%}", .1515)
+        self.formatEquals("15%", "{0:.0%}", .1515)
 
     def test_string_specifiers(self):
         self.formatEqualsWithUnicode("abc", "{0:.3s}", "abc")
@@ -232,6 +232,22 @@
         # XXX this should raise, but instead gives a DeprecationWarning
         #self.formatRaises(TypeError, "{0:c}", 3.14)
 
+    def test_exponent_specifiers(self):
+        self.formatEqualsWithUnicodeUC("3.141500e+00", "{0:e}", 3.1415)
+        self.formatEqualsWithUnicodeUC("3.1415000000e+00", "{0:.10e}", 3.1415)
+
+    def test_fixed_specifiers(self):
+        self.formatEqualsWithUnicode("3.141500", "{0:f}", 3.1415)
+        self.formatEqualsWithUnicode("3.1415000000", "{0:.10f}", 3.1415)
+        self.formatEqualsWithUnicode("3.1415e+200", "{0:f}", 3.1415e200)
+        self.formatEqualsWithUnicode("3.1415e+200", "{0:F}", 3.1415e200)
+
+    def test_general_specifiers(self):
+        self.formatEqualsWithUnicodeUC("3.1415", "{0:g}", 3.1415)
+        self.formatEqualsWithUnicodeUC("3.1415", "{0:.10g}", 3.1415)
+        self.formatEqualsWithUnicodeUC("3.1415e+200", "{0:g}", 3.1415e200)
+        self.formatEqualsWithUnicodeUC("3.1415e+200", "{0:g}", 3.1415e200)
+
     def test_missing_type_specifier(self):
         # make sure floats use 'g', ints and longs 'd', and everything else 's'
         pass

Modified: sandbox/trunk/pep3101/unicodeformat.c
==============================================================================
--- sandbox/trunk/pep3101/unicodeformat.c	(original)
+++ sandbox/trunk/pep3101/unicodeformat.c	Mon Mar  5 13:05:29 2007
@@ -60,9 +60,10 @@
 #define MAX_SIZE_INCREMENT  3200
 
 #if PYTHON_API_VERSION < 1013
-#define PySet_Discard    PyDict_DelItem
-#define PySet_New        PyDict_Copy
-#define PySet_GET_SIZE   PyDict_Size
+#define PySet_Discard        PyDict_DelItem
+#define PySet_New            PyDict_Copy
+#define PySet_GET_SIZE       PyDict_Size
+#define PyOS_ascii_formatd   PyOS_snprintf
 #endif
 
 
@@ -74,7 +75,6 @@
                        + 1 + 1 = 24 */
 #define MAXLEN_INT_STRING 64
 
-
 /************************************************************************/
 /***********   Global data structures and forward declarations  *********/
 /************************************************************************/
@@ -1142,8 +1142,8 @@
 #if C_UNICODE
     len = strtounicode(*pbuf, start, -1);
 #else
-    /* compute the length.  I believe this is done because the return value from
-       snprintf above is unreliable */
+    /* compute the length.  I believe this is done because the return
+       value from snprintf above is unreliable */
     len = strlen(start);
 #endif
 
@@ -1207,7 +1207,7 @@
 }
 
 static int
-format_decimal(PyObject *fieldobj, FmtState *fs,
+format_integer(PyObject *fieldobj, FmtState *fs,
                const InternalFormatSpec *format)
 {
     CH_TYPE *p_buf;
@@ -1322,7 +1322,8 @@
     }
 
     /* set the total length of the string */
-    n_total = n_lpadding + n_lsign + n_spadding + n_digits + n_rsign + n_rpadding;
+    n_total = n_lpadding + n_lsign + n_spadding + n_digits
+        + n_rsign + n_rpadding;
     assert(n_total >= n_allocated);
 
     /* because we're going to reallocate, our pointers might be
@@ -1355,17 +1356,20 @@
        overwrite some of that space */
     /* short circuit test, in case we don't have to move anything */
     if (p_buf + (n_lpadding + n_lsign + n_spadding) != p_digits)
-        memmove(p_buf + (n_lpadding + n_lsign + n_spadding), p_digits, n_digits * sizeof(CH_TYPE));
+        memmove(p_buf + (n_lpadding + n_lsign + n_spadding), p_digits,
+                n_digits * sizeof(CH_TYPE));
 
     if (n_lpadding) {
-        CH_TYPE_FILL(p_buf, format->fill_char == '\0' ? ' ' : format->fill_char, n_lpadding);
+        CH_TYPE_FILL(p_buf, format->fill_char == '\0' ? ' ' : format->fill_char,
+                     n_lpadding);
         p_buf += n_lpadding;
     }
     if (n_lsign == 1) {
         *p_buf++ = lsign;
     }
     if (n_spadding) {
-        CH_TYPE_FILL(p_buf, format->fill_char == '\0' ? ' ' : format->fill_char, n_spadding);
+        CH_TYPE_FILL(p_buf, format->fill_char == '\0' ? ' ' : format->fill_char,
+                     n_spadding);
         p_buf += n_spadding;
     }
     p_buf += n_digits;
@@ -1373,7 +1377,8 @@
         *p_buf++ = rsign;
     }
     if (n_rpadding) {
-        CH_TYPE_FILL(p_buf, format->fill_char == '\0' ? ' ' : format->fill_char, n_rpadding);
+        CH_TYPE_FILL(p_buf, format->fill_char == '\0' ? ' ' : format->fill_char,
+                     n_rpadding);
         p_buf += n_rpadding;
     }
 
@@ -1381,24 +1386,87 @@
 }
 
 static int
-format_exponent(PyObject *fieldobj, FmtState *fs,
-                const InternalFormatSpec *format)
-{
-    return format_DUMMY(fieldobj, fs);
-}
-
-static int
-format_fixed(PyObject *fieldobj, FmtState *fs,
+format_float(PyObject *fieldobj, FmtState *fs,
              const InternalFormatSpec *format)
 {
-    return format_DUMMY(fieldobj, fs);
-}
+    /* first, do the conversion as 8-bit chars, using the platform's
+       snprintf.  then, if needed, convert to unicode. */
 
-static int
-format_general(PyObject *fieldobj, FmtState *fs,
-               const InternalFormatSpec *format)
-{
-    return format_DUMMY(fieldobj, fs);
+    /* fmt = '%.' + `prec` + `type` + '%%'
+       worst case length = 2 + 10 (len of INT_MAX) + 1 + 2 = 15 (use 20)*/
+    char fmt[20];
+
+    double x;
+    CH_TYPE type = format->type;
+    Py_ssize_t precision = format->precision;
+    CH_TYPE *buf;
+    int buflen;
+    int len;
+    char* trailing = "";
+
+    /* 'F' is the same as 'f', per the PEP */
+    if (type == 'F')
+        type = 'f';
+
+    x = PyFloat_AsDouble(fieldobj);
+    if (x == -1.0 && PyErr_Occurred())
+	return 0;
+
+    if (type == '%') {
+        type = 'f';
+        x *= 100;
+        trailing = "%%";
+    }
+
+    if (precision < 0)
+	precision = 6;
+    if (type == 'f' && (fabs(x) / 1e25) >= 1e25)
+	type = 'g';
+
+    /* cast "type", because if we're in unicode we need to pass a
+       8-bit char.  this is safe, because we've restricted what "type"
+       can be */
+    PyOS_snprintf(fmt, sizeof(fmt), "%%.%d%c%s", precision, (char)type, trailing);
+
+    /* this is taken from unicodeobject.c, except we don't force a
+       limit here, we dynamically allocate instead */
+    /* Worst case length calc to ensure no buffer overrun:
+
+       'g' formats:
+	 fmt = %#.<prec>g
+	 buf = '-' + [0-9]*prec + '.' + 'e+' + (longest exp
+	    for any double rep.)
+	 len = 1 + prec + 1 + 2 + 5 = 9 + prec
+
+       'f' formats:
+	 buf = '-' + [0-9]*x + '.' + [0-9]*prec (with x < 50)
+	 len = 1 + 50 + 1 + prec = 52 + prec
+
+       If prec=0 the effective precision is 1 (the leading digit is
+       always given), therefore increase the length by one.
+
+    */
+    /* so, allocate the precision plus 54 chars (we add one additional
+       for the trailing percent).  do this allocation as the native
+       type, because we're going to convert to unicode anyway */
+    buflen = 54 + precision;
+    if (output_allocate(fs, buflen, &buf) == 0)
+        return 0;
+    PyOS_ascii_formatd((char *)buf, buflen, fmt, x);
+
+#if C_UNICODE
+    len = strtounicode(buf, (char*)buf, -1);
+#else
+    /* compute the length.  I believe this is done because the return
+       value from snprintf above is unreliable */
+    len = strlen(buf);
+#endif
+
+    /* shrink the buffer down to how many characters we actually
+       wrote.  this is cheap, just pointer arithmetic */
+    output_shrink(fs, buflen - len);
+
+    return 1;
 }
 
 static int
@@ -1448,13 +1516,6 @@
     return ok;
 }
 
-static int
-format_percentage(PyObject *fieldobj, FmtState *fs,
-                  const InternalFormatSpec *format)
-{
-    return format_DUMMY(fieldobj, fs);
-}
-
 /* returns a pointer to our conversion function, or NULL if invalid */
 Py_LOCAL_INLINE(FormatFunction)
 format_function(CH_TYPE c)
@@ -1462,23 +1523,23 @@
     switch (c) {
     case 'b': return format_binary;          /* base-2 */
     case 'c': return format_char;            /* as character */
-    case 'd': return format_decimal;         /* decimal integer */
-    case 'e': return format_exponent;        /* exponential notation */
-    case 'E': return format_exponent;        /* exponential notation
-                                                with uppercase 'E' */
-    case 'f': return format_fixed;           /* fixed-point */
-    case 'F': return format_fixed;           /* fixed-point with uppercase */
-    case 'g': return format_general;         /* general number notation */
-    case 'G': return format_general;         /* general number notation
-                                                with uppercase 'E' */
     case 'n': return format_locale_number;   /* number in locale-specific
                                                 format */
-    case 'o': return format_decimal;         /* octal */
+    case 'd':                                /* decimal integer */
+    case 'o':                                /* octal */
+    case 'x':                                /* base 16 */
+    case 'X': return format_integer;         /* base 16 uppercase */
     case 'r': return format_repr;            /* in repr() format */
     case 's': return format_string;          /* convert using str() */
-    case 'x': return format_decimal;         /* base 16 */
-    case 'X': return format_decimal;         /* base 16 uppercase */
-    case '%': return format_percentage;      /* as percentage */
+    case 'e':                                /* exponential notation */
+    case 'E':                                /* exponential notation
+                                                with uppercase 'E' */
+    case 'f':                                /* fixed-point */
+    case 'F':                                /* fixed-point with uppercase */
+    case 'g':                                /* general number notation */
+    case 'G':                                /* general number notation
+                                                with uppercase 'E' */
+    case '%': return format_float;           /* as percentage */
     default:
         return NULL;
     }
@@ -1727,7 +1788,7 @@
 static int
 do_markup(FmtState *fs)
 {
-    CH_TYPE c, *start, *ptr, *end;
+    CH_TYPE c = 0, *start, *ptr, *end;
     Py_ssize_t count;
     int syntaxmode, escape, at_end;
 


More information about the Python-checkins mailing list