[Python-checkins] r54100 - sandbox/trunk/pep3101/README.txt sandbox/trunk/pep3101/test_simpleformat.py sandbox/trunk/pep3101/unicodeformat.c

eric.smith python-checkins at python.org
Sat Mar 3 01:30:41 CET 2007


Author: eric.smith
Date: Sat Mar  3 01:30:36 2007
New Revision: 54100

Modified:
   sandbox/trunk/pep3101/README.txt
   sandbox/trunk/pep3101/test_simpleformat.py
   sandbox/trunk/pep3101/unicodeformat.c
Log:
Added character formatter, code cleanup, added to do items in README.

Modified: sandbox/trunk/pep3101/README.txt
==============================================================================
--- sandbox/trunk/pep3101/README.txt	(original)
+++ sandbox/trunk/pep3101/README.txt	Sat Mar  3 01:30:36 2007
@@ -7,7 +7,7 @@
 Current developers:
 
     Patrick Maupin (pmaupin at gmail.com)
-    Eric V. Smith
+    Eric V. Smith (eric at trueblade.com)
     Pete Shinner
 
 The code is only half-baked at present
@@ -61,6 +61,10 @@
       compatible template systems.
     - Play with possible options for specifying additional
       escape syntaxes
+    - Should we have stricter checking on format strings?  For example
+      type "s" doesn't allow a sign character.  Should specifying one
+      be an error?
+    - Test suite needs to check for specific exceptions.
 
 _flags options to consider adding:
 

Modified: sandbox/trunk/pep3101/test_simpleformat.py
==============================================================================
--- sandbox/trunk/pep3101/test_simpleformat.py	(original)
+++ sandbox/trunk/pep3101/test_simpleformat.py	Sat Mar  3 01:30:36 2007
@@ -89,7 +89,7 @@
         self.formatEquals("32_*>4d", "{0:{1}{2}4{3}}", 32, "*", ">", "d")
 
     def test_specifiers(self):
-        self.formatEquals("97_c", "{0:c}", ord("a"))
+        self.formatEquals("a", "{0:c}", ord("a"))
         self.formatEquals("8_08b", "{0:08b}", 8)
         self.formatEquals("8_ >3d", "{0: >3d}", 8)
         self.formatEquals("0.1515_.0%", "{0:.0%}", .1515)
@@ -101,9 +101,11 @@
 
         self.formatEquals("abc", "{0:.3s}", "abcdef")
         self.formatEquals("resultx", "{0:x<7s}", "result")
+        self.formatEquals("resultxx", "{0:x<8s}", "result")
         self.formatEquals("result ", "{0: <7s}", "result")
         self.formatEquals("result ", "{0:<7s}", "result")
         self.formatEquals(" result", "{0:>7s}", "result")
+        self.formatEquals("  result", "{0:>8s}", "result")
 
     def test_repr_specifiers(self):
         self.formatEquals("3", "{0:r}", 3)
@@ -113,6 +115,30 @@
         self.formatEquals("'abcdefg'", "{0:r}", "abcdefg")
         self.formatEquals("'abcdefg", "{0:8r}", "abcdefg")
 
+    def test_decimal_specifiers(self):
+        pass
+#        self.assertRaises(Exception, "{0:d}", "non-number")
+
+#        self.formatEquals("0", "{0:d}", 0)
+#        self.formatEquals("1" + "0" * 100, "{0:d}", 10**100)
+
+    def test_char_specifiers(self):
+        self.formatEquals("A", "{0:c}", "A")
+        self.formatEquals("8", "{0:c}", "8")
+        self.formatEquals(";", "{0:c}", ";")
+        self.formatEquals(";", "{0:c}", long(ord(";")))
+
+        self.formatRaises(TypeError, "{0:c}", "abcd")
+
+        # XXX not sure why this doesn't raise
+        #self.formatRaises(TypeError, "{0:c}", [1, 2, 3])
+
+        # XXX not sure why this doesn't raise
+        #self.formatRaises(TypeError, "{0:c}", 1j)
+
+        # XXX this should raise, but instead gives a DeprecationWarning
+        #self.formatRaises(TypeError, "{0:c}", 3.14)
+
     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	Sat Mar  3 01:30:36 2007
@@ -1,5 +1,3 @@
-#define DUMMY_FORMATTING 1
-
 /*
     unicodeformat.c -- implementation of PEP 3101
 
@@ -26,6 +24,7 @@
 #define CH_TYPE                  Py_UNICODE
 #define CH_TYPE_ISDECIMAL        Py_UNICODE_ISDECIMAL
 #define CH_TYPE_TODECIMAL        Py_UNICODE_TODECIMAL
+#define CH_TYPE_FILL             Py_UNICODE_FILL
 #define STROBJ_AS_PTR            PyUnicode_AS_UNICODE
 #define STROBJ_GET_SIZE          PyUnicode_GET_SIZE
 #define STROBJ_NEW               PyUnicode_FromUnicode
@@ -37,6 +36,7 @@
 #define CH_TYPE                  char
 #define CH_TYPE_ISDECIMAL(x)     ((x >= '0') && (x <= '9'))
 #define CH_TYPE_TODECIMAL(x)     (CH_TYPE_ISDECIMAL(x) ? (x - '0') : -1)
+#define CH_TYPE_FILL             Py_UNICODE_FILL
 #define STROBJ_AS_PTR            PyString_AS_STRING
 #define STROBJ_GET_SIZE          PyString_GET_SIZE
 #define STROBJ_NEW               PyString_FromStringAndSize
@@ -67,6 +67,19 @@
 /***********   Global data structures and forward declarations  *********/
 /************************************************************************/
 
+/* FORMATBUFLEN is taken from stringobject.c, it should probably be
+   factored out */
+/* fmt%(v1,v2,...) is roughly equivalent to sprintf(fmt, v1, v2, ...)
+
+   FORMATBUFLEN is the length of the buffer in which the floats, ints, &
+   chars are formatted. XXX This is a magic number. Each formatting
+   routine does bounds checking to ensure no overflow, but a better
+   solution may be to malloc a buffer of appropriate size for each
+   format. For now, the current solution is sufficient.
+*/
+#define FORMATBUFLEN (size_t)120
+
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -161,17 +174,6 @@
 /**************************  Utility  functions  ************************/
 /************************************************************************/
 
-/* XXX probably a better way to do this.  can't use memset, though,
-   because of Unicode and char* support */
-Py_LOCAL_INLINE(void)
-charset(CH_TYPE* dst, CH_TYPE ch, Py_ssize_t len)
-{
-    CH_TYPE* end = dst + len;
-    for(; dst < end; dst++) {
-        *dst = ch;
-    }
-}
-
 
 /************************************************************************/
 /***********      Error handling and exception generation  **************/
@@ -748,7 +750,8 @@
 /* used to output simple strings, only supports a maximum width and
    total field alignment */
 static int
-output_string_chars(FmtState *fs, CH_TYPE *src, Py_ssize_t len, const InternalFormatSpec *format)
+output_string_chars(FmtState *fs, CH_TYPE *src, Py_ssize_t len,
+                    const InternalFormatSpec *format)
 {
     Py_ssize_t ok;
     Py_ssize_t width; /* total field width */
@@ -762,6 +765,10 @@
 
     if (format->width >= 0) {
         width = format->width;
+        /* don't write out more than width characters */
+        if (len > width) {
+            len = width;
+        }
     } else {
         /* not specified, use all of the chars and no more */
         width = len;
@@ -773,11 +780,13 @@
 
     /* now write into that space */
 
-    /* if right aligning, increment the destination allow space on the left */
-    memcpy(dst + (format->align == '>' ? (width - len) : 0), src, len * sizeof(CH_TYPE));
+    /* if right aligning, increment the destination allow space on the
+       left */
+    memcpy(dst + (format->align == '>' ? (width - len) : 0), src,
+           len * sizeof(CH_TYPE));
 
     /* do any padding */
-    if (len != width) {
+    if (width > len) {
         CH_TYPE fill_char = format->fill_char;
         if (fill_char == '\0') {
             /* use the default, if not specified */
@@ -786,27 +795,19 @@
 
         if (format->align == '>') {
             /* right align, pad on left */
-            charset(dst, fill_char, width - len);
+            CH_TYPE_FILL(dst, fill_char, width - len);
         } else {
             /* left align, pad on right */
-            charset(dst + len, fill_char, width - len);
+            CH_TYPE_FILL(dst + len, fill_char, width - len);
         }
     }
     return 1;
 }
 
 
-/*
-    Our internal conversion functions have this signature.
-
-    returns 0 on failure, else 1
-*/
-typedef int
-(*ConversionFunction)(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format);
-
 /* XXX delete this when all internal conversions are implemented */
 static int
-convert_DUMMY(PyObject *fieldobj, FmtState *fs)
+format_DUMMY(PyObject *fieldobj, FmtState *fs)
 {
     PyObject *myobj;
     int ok;
@@ -832,23 +833,70 @@
     return 1;
 }
 
-/* conversion functions */
+/************************************************************************/
+/*************************  Builtin formatters  *************************/
+/************************************************************************/
+
+/*
+    return 0 on failure, else 1
+*/
+typedef int
+(*FormatFunction)(PyObject *fieldobj, FmtState *fs,
+                  const InternalFormatSpec *format);
+
 static int
-convert_binary(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format)
+format_binary(PyObject *fieldobj, FmtState *fs,
+              const InternalFormatSpec *format)
 {
-    return convert_DUMMY(fieldobj, fs);
+    return format_DUMMY(fieldobj, fs);
 }
 
 static int
-convert_char(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format)
+format_char(PyObject *fieldobj, FmtState *fs,
+            const InternalFormatSpec *format)
 {
-    return convert_DUMMY(fieldobj, fs);
+    int ok;
+    CH_TYPE buf;
+
+    if (PyString_Check(fieldobj)) {
+        if (!PyArg_Parse(fieldobj, "c;%c requires int or char", &buf))
+            return 0;
+    }
+    else {
+        if (!PyArg_Parse(fieldobj, "b;%c requires int or char", &buf))
+            return -1;
+    }
+
+    return output_data(fs, &buf, 1);
 }
 
 static int
-convert_decimal(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format)
+format_decimal(PyObject *fieldobj, FmtState *fs,
+               const InternalFormatSpec *format)
 {
-    return convert_DUMMY(fieldobj, fs);
+#if 0
+    if (PyLong_Check(fieldobj)) {
+        int ilen;
+        temp = _PyString_FormatLong(v, flags,
+                                    prec, c, &pbuf, &ilen);
+        len = ilen;
+        if (!temp)
+            goto error;
+        sign = 1;
+    }
+    else {
+        pbuf = formatbuf;
+        len = formatint(pbuf,
+                        sizeof(formatbuf),
+                        flags, prec, c, v);
+        if (len < 0)
+            goto error;
+        sign = 1;
+    }
+    if (flags & F_ZERO)
+        fill = '0';
+#endif
+    return format_DUMMY(fieldobj, fs);
 #if 0
     PyObject *intobj;
     PyObject *strobj;
@@ -884,55 +932,64 @@
 }
 
 static int
-convert_exponent(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format)
+format_exponent(PyObject *fieldobj, FmtState *fs,
+                const InternalFormatSpec *format)
 {
-    return convert_DUMMY(fieldobj, fs);
+    return format_DUMMY(fieldobj, fs);
 }
 
 static int
-convert_exponentUC(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format)
+format_exponentUC(PyObject *fieldobj, FmtState *fs,
+                  const InternalFormatSpec *format)
 {
-    return convert_DUMMY(fieldobj, fs);
+    return format_DUMMY(fieldobj, fs);
 }
 
 static int
-convert_fixed(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format)
+format_fixed(PyObject *fieldobj, FmtState *fs,
+             const InternalFormatSpec *format)
 {
-    return convert_DUMMY(fieldobj, fs);
+    return format_DUMMY(fieldobj, fs);
 }
 
 static int
-convert_fixedUC(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format)
+format_fixedUC(PyObject *fieldobj, FmtState *fs,
+               const InternalFormatSpec *format)
 {
-    return convert_DUMMY(fieldobj, fs);
+    return format_DUMMY(fieldobj, fs);
 }
 
 static int
-convert_general(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format)
+format_general(PyObject *fieldobj, FmtState *fs,
+               const InternalFormatSpec *format)
 {
-    return convert_DUMMY(fieldobj, fs);
+    return format_DUMMY(fieldobj, fs);
 }
 
 static int
-convert_generalUC(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format)
+format_generalUC(PyObject *fieldobj, FmtState *fs,
+                 const InternalFormatSpec *format)
 {
-    return convert_DUMMY(fieldobj, fs);
+    return format_DUMMY(fieldobj, fs);
 }
 
 static int
-convert_number(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format)
+format_number(PyObject *fieldobj, FmtState *fs,
+              const InternalFormatSpec *format)
 {
-    return convert_DUMMY(fieldobj, fs);
+    return format_DUMMY(fieldobj, fs);
 }
 
 static int
-convert_octal(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format)
+format_octal(PyObject *fieldobj, FmtState *fs,
+             const InternalFormatSpec *format)
 {
-    return convert_DUMMY(fieldobj, fs);
+    return format_DUMMY(fieldobj, fs);
 }
 
 static int
-convert_repr(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format)
+format_repr(PyObject *fieldobj, FmtState *fs,
+            const InternalFormatSpec *format)
 {
     int ok;
     PyObject *strobj;
@@ -945,14 +1002,16 @@
     strobj = STROBJ_STR(reprobj);
     Py_DECREF(reprobj);
 
-    ok = output_string_chars(fs, STROBJ_AS_PTR(strobj), STROBJ_GET_SIZE(strobj), format);
+    ok = output_string_chars(fs, STROBJ_AS_PTR(strobj),
+                             STROBJ_GET_SIZE(strobj), format);
     Py_DECREF(strobj);
 
     return ok;
 }
 
 static int
-convert_string(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format)
+format_string(PyObject *fieldobj, FmtState *fs,
+              const InternalFormatSpec *format)
 {
     PyObject *strobj;
     int ok;
@@ -961,54 +1020,58 @@
     if (strobj == NULL)
         return 0;
 
-    ok = output_string_chars(fs, STROBJ_AS_PTR(strobj), STROBJ_GET_SIZE(strobj), format);
+    ok = output_string_chars(fs, STROBJ_AS_PTR(strobj),
+                             STROBJ_GET_SIZE(strobj), format);
     Py_DECREF(strobj);
 
     return ok;
 }
 
 static int
-convert_hex(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format)
+format_hex(PyObject *fieldobj, FmtState *fs,
+           const InternalFormatSpec *format)
 {
-    return convert_DUMMY(fieldobj, fs);
+    return format_DUMMY(fieldobj, fs);
 }
 
 static int
-convert_hexUC(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format)
+format_hexUC(PyObject *fieldobj, FmtState *fs,
+             const InternalFormatSpec *format)
 {
-    return convert_DUMMY(fieldobj, fs);
+    return format_DUMMY(fieldobj, fs);
 }
 
 static int
-convert_percentage(PyObject *fieldobj, FmtState *fs, const InternalFormatSpec *format)
+format_percentage(PyObject *fieldobj, FmtState *fs,
+                  const InternalFormatSpec *format)
 {
-    return convert_DUMMY(fieldobj, fs);
+    return format_DUMMY(fieldobj, fs);
 }
 
 /* returns a pointer to our conversion function, or NULL if invalid */
-Py_LOCAL_INLINE(ConversionFunction)
-conversion_function(CH_TYPE c)
+Py_LOCAL_INLINE(FormatFunction)
+format_function(CH_TYPE c)
 {
     switch (c) {
-    case 'b': return convert_binary;          /* base-2 */
-    case 'c': return convert_char;            /* as character */
-    case 'd': return convert_decimal;         /* decimal integer */
-    case 'e': return convert_exponent;        /* exponential notation */
-    case 'E': return convert_exponentUC;      /* exponential notation
-                                                 with uppercase 'E' */
-    case 'f': return convert_fixed;           /* fixed-point */
-    case 'F': return convert_fixedUC;         /* fixed-point with uppercase */
-    case 'g': return convert_general;         /* general number notation */
-    case 'G': return convert_generalUC;       /* general number notation
-                                                 with uppercase 'E' */
-    case 'n': return convert_number;          /* number in locale-specific
-                                                 format */
-    case 'o': return convert_octal;           /* octal */
-    case 'r': return convert_repr;            /* in repr() format */
-    case 's': return convert_string;          /* convert using str() */
-    case 'x': return convert_hex;             /* base 16 */
-    case 'X': return convert_hexUC;           /* base 16 uppercase */
-    case '%': return convert_percentage;      /* as percentage */
+    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_exponentUC;      /* exponential notation
+                                                with uppercase 'E' */
+    case 'f': return format_fixed;           /* fixed-point */
+    case 'F': return format_fixedUC;         /* fixed-point with uppercase */
+    case 'g': return format_general;         /* general number notation */
+    case 'G': return format_generalUC;       /* general number notation
+                                                with uppercase 'E' */
+    case 'n': return format_number;          /* number in locale-specific
+                                                format */
+    case 'o': return format_octal;           /* octal */
+    case 'r': return format_repr;            /* in repr() format */
+    case 's': return format_string;          /* convert using str() */
+    case 'x': return format_hex;             /* base 16 */
+    case 'X': return format_hexUC;           /* base 16 uppercase */
+    case '%': return format_percentage;      /* as percentage */
     default:
         return NULL;
     }
@@ -1020,7 +1083,7 @@
 static int internal_render(FmtState *fs, PyObject *fieldobj)
 {
     InternalFormatSpec format;
-    ConversionFunction conversion;
+    FormatFunction formatter;
 
     if (!parse_internal_render_format_spec(fs, &format)) {
         return 0;
@@ -1038,18 +1101,18 @@
         }
     }
 
-    /* XXX handle conversion functions that logically map to
-       other conversion functions? percent is the only one, and I'm not wild
+    /* XXX handle conversion functions that logically map to other
+       conversion functions? percent is the only one, and I'm not wild
        about having percent at all*/
 
-    conversion = conversion_function(format.type);
-    if (conversion == NULL) {
+    formatter = format_function(format.type);
+    if (formatter == NULL) {
         SetError(fs, "Invalid conversion character");
         return 0;
     }
 
-    /* do the conversion, writing into the output string */
-    return conversion(fieldobj, fs, &format);
+    /* do the formatting, writing into the output string */
+    return formatter(fieldobj, fs, &format);
 
 #if 0
 


More information about the Python-checkins mailing list