[Python-3000-checkins] r60880 - in python/branches/py3k: Include/abstract.h Lib/test/test_builtin.py Lib/test/test_datetime.py Modules/datetimemodule.c Objects/abstract.c Objects/stringlib/formatter.h Objects/stringlib/string_format.h Objects/stringlib/stringdefs.h Objects/stringlib/unicodedefs.h Python/bltinmodule.c

eric.smith python-3000-checkins at python.org
Sun Feb 17 20:48:01 CET 2008


Author: eric.smith
Date: Sun Feb 17 20:48:00 2008
New Revision: 60880

Modified:
   python/branches/py3k/Include/abstract.h
   python/branches/py3k/Lib/test/test_builtin.py
   python/branches/py3k/Lib/test/test_datetime.py
   python/branches/py3k/Modules/datetimemodule.c
   python/branches/py3k/Objects/abstract.c
   python/branches/py3k/Objects/stringlib/formatter.h
   python/branches/py3k/Objects/stringlib/string_format.h
   python/branches/py3k/Objects/stringlib/stringdefs.h
   python/branches/py3k/Objects/stringlib/unicodedefs.h
   python/branches/py3k/Python/bltinmodule.c
Log:
Fixes for shared 2.6 code that implements PEP 3101, advanced string
formatting.

Includes:
 - Modifying tests for basic types to use __format__ methods, instead
   of builtin "format".
 - Adding PyObject_Format.
 - General str/unicode cleanup discovered when backporting to 2.6.
 - Removing datetimemodule.c's time_format, since it was identical
   to date_format.

The files in Objects/stringlib that implement PEP 3101 (stringdefs.h,
unicodedefs.h, formatter.h, string_format.h) are identical in trunk
and py3k.  Any changes from here on should be made to trunk, and
changes will propogate to py3k).



Modified: python/branches/py3k/Include/abstract.h
==============================================================================
--- python/branches/py3k/Include/abstract.h	(original)
+++ python/branches/py3k/Include/abstract.h	Sun Feb 17 20:48:00 2008
@@ -611,6 +611,13 @@
          */
 
 
+     PyAPI_FUNC(PyObject *) PyObject_Format(PyObject* obj,
+					    PyObject *format_spec);
+       /*
+	 Takes an arbitrary object and returns the result of
+	 calling obj.__format__(format_spec).
+       */
+
 /* Iterators */
 
      PyAPI_FUNC(PyObject *) PyObject_GetIter(PyObject *);

Modified: python/branches/py3k/Lib/test/test_builtin.py
==============================================================================
--- python/branches/py3k/Lib/test/test_builtin.py	(original)
+++ python/branches/py3k/Lib/test/test_builtin.py	Sun Feb 17 20:48:00 2008
@@ -541,24 +541,58 @@
         self.assertRaises(TypeError, float, Foo4(42))
 
     def test_format(self):
-        class A:
-            def __init__(self, x):
-                self.x = x
-            def __format__(self, format_spec):
-                return str(self.x) + format_spec
+        # Test the basic machinery of the format() builtin.  Don't test
+        #  the specifics of the various formatters
+        self.assertEqual(format(3, ''), '3')
 
-        # class that returns a bad type from __format__
-        class B:
-            def __format__(self, format_spec):
-                return 1.0
+        # Returns some classes to use for various tests.  There's
+        #  an old-style version, and a new-style version
+        def classes_new():
+            class A(object):
+                def __init__(self, x):
+                    self.x = x
+                def __format__(self, format_spec):
+                    return str(self.x) + format_spec
+            class DerivedFromA(A):
+                pass
 
-        # class that is derived from string, used
-        #  as a format spec
-        class C(str):
-            pass
+            class Simple(object): pass
+            class DerivedFromSimple(Simple):
+                def __init__(self, x):
+                    self.x = x
+                def __format__(self, format_spec):
+                    return str(self.x) + format_spec
+            class DerivedFromSimple2(DerivedFromSimple): pass
+            return A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2
+
+        # In 3.0, classes_classic has the same meaning as classes_new
+        def classes_classic():
+            class A:
+                def __init__(self, x):
+                    self.x = x
+                def __format__(self, format_spec):
+                    return str(self.x) + format_spec
+            class DerivedFromA(A):
+                pass
 
-        self.assertEqual(format(3, ''), '3')
-        self.assertEqual(format(A(3), 'spec'), '3spec')
+            class Simple: pass
+            class DerivedFromSimple(Simple):
+                def __init__(self, x):
+                    self.x = x
+                def __format__(self, format_spec):
+                    return str(self.x) + format_spec
+            class DerivedFromSimple2(DerivedFromSimple): pass
+            return A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2
+
+        def class_test(A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2):
+            self.assertEqual(format(A(3), 'spec'), '3spec')
+            self.assertEqual(format(DerivedFromA(4), 'spec'), '4spec')
+            self.assertEqual(format(DerivedFromSimple(5), 'abc'), '5abc')
+            self.assertEqual(format(DerivedFromSimple2(10), 'abcdef'),
+                             '10abcdef')
+
+        class_test(*classes_new())
+        class_test(*classes_classic())
 
         def empty_format_spec(value):
             # test that:
@@ -578,19 +612,28 @@
         empty_format_spec(None)
 
         # TypeError because self.__format__ returns the wrong type
-        self.assertRaises(TypeError, format, B(), "")
+        class BadFormatResult:
+            def __format__(self, format_spec):
+                return 1.0
+        self.assertRaises(TypeError, format, BadFormatResult(), "")
 
-        # TypeError because format_spec is not unicode
+        # TypeError because format_spec is not unicode or str
         self.assertRaises(TypeError, format, object(), 4)
         self.assertRaises(TypeError, format, object(), object())
 
+        # tests for object.__format__ really belong elsewhere, but
+        #  there's no good place to put them
+        x = object().__format__('')
+        self.assert_(x.startswith('<object object at'))
+
         # first argument to object.__format__ must be string
         self.assertRaises(TypeError, object().__format__, 3)
         self.assertRaises(TypeError, object().__format__, object())
         self.assertRaises(TypeError, object().__format__, None)
 
         # make sure we can take a subclass of str as a format spec
-        self.assertEqual(format(0, C('10')), '         0')
+        class DerivedFromStr(str): pass
+        self.assertEqual(format(0, DerivedFromStr('10')), '         0')
 
     def test_floatasratio(self):
         for f, ratio in [

Modified: python/branches/py3k/Lib/test/test_datetime.py
==============================================================================
--- python/branches/py3k/Lib/test/test_datetime.py	(original)
+++ python/branches/py3k/Lib/test/test_datetime.py	Sun Feb 17 20:48:00 2008
@@ -851,29 +851,29 @@
 
     def test_format(self):
         dt = self.theclass(2007, 9, 10)
-        self.assertEqual(format(dt, ''), str(dt))
+        self.assertEqual(dt.__format__(''), str(dt))
 
         # check that a derived class's __str__() gets called
         class A(self.theclass):
             def __str__(self):
                 return 'A'
         a = A(2007, 9, 10)
-        self.assertEqual(format(a, ''), 'A')
+        self.assertEqual(a.__format__(''), 'A')
 
         # check that a derived class's strftime gets called
         class B(self.theclass):
             def strftime(self, format_spec):
                 return 'B'
         b = B(2007, 9, 10)
-        self.assertEqual(format(b, ''), str(dt))
+        self.assertEqual(b.__format__(''), str(dt))
 
         for fmt in ["m:%m d:%d y:%y",
                     "m:%m d:%d y:%y H:%H M:%M S:%S",
                     "%z %Z",
                     ]:
-            self.assertEqual(format(dt, fmt), dt.strftime(fmt))
-            self.assertEqual(format(a, fmt), dt.strftime(fmt))
-            self.assertEqual(format(b, fmt), 'B')
+            self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
+            self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
+            self.assertEqual(b.__format__(fmt), 'B')
 
     def test_resolution_info(self):
         self.assert_(isinstance(self.theclass.min, self.theclass))
@@ -1178,31 +1178,29 @@
 
     def test_format(self):
         dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
-        self.assertEqual(format(dt, ''), str(dt))
+        self.assertEqual(dt.__format__(''), str(dt))
 
         # check that a derived class's __str__() gets called
         class A(self.theclass):
             def __str__(self):
                 return 'A'
         a = A(2007, 9, 10, 4, 5, 1, 123)
-        self.assertEqual(format(a, ''), 'A')
+        self.assertEqual(a.__format__(''), 'A')
 
         # check that a derived class's strftime gets called
         class B(self.theclass):
             def strftime(self, format_spec):
                 return 'B'
         b = B(2007, 9, 10, 4, 5, 1, 123)
-        self.assertEqual(format(b, ''), str(dt))
+        self.assertEqual(b.__format__(''), str(dt))
 
         for fmt in ["m:%m d:%d y:%y",
                     "m:%m d:%d y:%y H:%H M:%M S:%S",
                     "%z %Z",
                     ]:
-            self.assertEqual(format(dt, fmt), dt.strftime(fmt))
-            self.assertEqual(format(a, fmt), dt.strftime(fmt))
-            self.assertEqual(format(b, fmt), 'B')
-
-
+            self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
+            self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
+            self.assertEqual(b.__format__(fmt), 'B')
 
     def test_more_ctime(self):
         # Test fields that TestDate doesn't touch.
@@ -1837,27 +1835,27 @@
 
     def test_format(self):
         t = self.theclass(1, 2, 3, 4)
-        self.assertEqual(format(t, ''), str(t))
+        self.assertEqual(t.__format__(''), str(t))
 
         # check that a derived class's __str__() gets called
         class A(self.theclass):
             def __str__(self):
                 return 'A'
         a = A(1, 2, 3, 4)
-        self.assertEqual(format(a, ''), 'A')
+        self.assertEqual(a.__format__(''), 'A')
 
         # check that a derived class's strftime gets called
         class B(self.theclass):
             def strftime(self, format_spec):
                 return 'B'
         b = B(1, 2, 3, 4)
-        self.assertEqual(format(b, ''), str(t))
+        self.assertEqual(b.__format__(''), str(t))
 
         for fmt in ['%H %M %S',
                     ]:
-            self.assertEqual(format(t, fmt), t.strftime(fmt))
-            self.assertEqual(format(a, fmt), t.strftime(fmt))
-            self.assertEqual(format(b, fmt), 'B')
+            self.assertEqual(t.__format__(fmt), t.strftime(fmt))
+            self.assertEqual(a.__format__(fmt), t.strftime(fmt))
+            self.assertEqual(b.__format__(fmt), 'B')
 
     def test_str(self):
         self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")

Modified: python/branches/py3k/Modules/datetimemodule.c
==============================================================================
--- python/branches/py3k/Modules/datetimemodule.c	(original)
+++ python/branches/py3k/Modules/datetimemodule.c	Sun Feb 17 20:48:00 2008
@@ -3210,21 +3210,6 @@
 	return result;
 }
 
-static PyObject *
-time_format(PyDateTime_Time *self, PyObject *args)
-{
-	PyObject *format;
-
-	if (!PyArg_ParseTuple(args, "U:__format__", &format))
-		return NULL;
-
-	/* if the format is zero length, return str(self) */
-	if (PyUnicode_GetSize(format) == 0)
-                return PyObject_Str((PyObject *)self);
-
-        return PyObject_CallMethod((PyObject *)self, "strftime", "O", format);
-}
-
 /*
  * Miscellaneous methods.
  */
@@ -3412,7 +3397,7 @@
 	{"strftime",   	(PyCFunction)time_strftime,	METH_VARARGS | METH_KEYWORDS,
 	 PyDoc_STR("format -> strftime() style string.")},
 
-	{"__format__", 	(PyCFunction)time_format,	METH_VARARGS,
+	{"__format__", 	(PyCFunction)date_format,	METH_VARARGS,
 	 PyDoc_STR("Formats self with strftime.")},
 
 	{"utcoffset",	(PyCFunction)time_utcoffset,	METH_NOARGS,

Modified: python/branches/py3k/Objects/abstract.c
==============================================================================
--- python/branches/py3k/Objects/abstract.c	(original)
+++ python/branches/py3k/Objects/abstract.c	Sun Feb 17 20:48:00 2008
@@ -704,6 +704,57 @@
 	return 0;
 }
 
+PyObject *
+PyObject_Format(PyObject *obj, PyObject *format_spec)
+{
+    static PyObject * str__format__ = NULL;
+    PyObject *meth;
+    PyObject *empty = NULL;
+    PyObject *result = NULL;
+
+    /* Initialize cached value */
+    if (str__format__ == NULL) {
+        /* Initialize static variable needed by _PyType_Lookup */
+        str__format__ = PyUnicode_FromString("__format__");
+        if (str__format__ == NULL)
+            goto done;
+    }
+
+    /* If no format_spec is provided, use an empty string */
+    if (format_spec == NULL) {
+        empty = PyUnicode_FromUnicode(NULL, 0);
+        format_spec = empty;
+    }
+
+    /* Make sure the type is initialized.  float gets initialized late */
+    if (Py_TYPE(obj)->tp_dict == NULL)
+        if (PyType_Ready(Py_TYPE(obj)) < 0)
+            goto done;
+
+    /* Find the (unbound!) __format__ method (a borrowed reference) */
+    meth = _PyType_Lookup(Py_TYPE(obj), str__format__);
+    if (meth == NULL) {
+        PyErr_Format(PyExc_TypeError,
+                "Type %.100s doesn't define __format__",
+                Py_TYPE(obj)->tp_name);
+            goto done;
+    }
+
+    /* And call it, binding it to the value */
+    result = PyObject_CallFunctionObjArgs(meth, obj, format_spec, NULL);
+
+    if (result && !PyUnicode_Check(result)) {
+        PyErr_SetString(PyExc_TypeError,
+            "__format__ method did not return string");
+        Py_DECREF(result);
+        result = NULL;
+        goto done;
+    }
+
+done:
+    Py_XDECREF(empty);
+    return result;
+}
 /* Operations on numbers */
 
 int

Modified: python/branches/py3k/Objects/stringlib/formatter.h
==============================================================================
--- python/branches/py3k/Objects/stringlib/formatter.h	(original)
+++ python/branches/py3k/Objects/stringlib/formatter.h	Sun Feb 17 20:48:00 2008
@@ -195,7 +195,7 @@
     return 1;
 }
 
-
+#if defined FORMAT_FLOAT || defined FORMAT_LONG
 /************************************************************************/
 /*********** common routines for numeric formatting *********************/
 /************************************************************************/
@@ -288,7 +288,8 @@
         else {
             /* determine which of left, space, or right padding is
                needed */
-            Py_ssize_t padding = format->width - (r->n_lsign + n_digits + r->n_rsign);
+            Py_ssize_t padding = format->width -
+		                    (r->n_lsign + n_digits + r->n_rsign);
             if (format->align == '<')
                 r->n_rpadding = padding;
             else if (format->align == '>')
@@ -338,6 +339,7 @@
     }
     return p_digits;
 }
+#endif /* FORMAT_FLOAT || FORMAT_LONG */
 
 /************************************************************************/
 /*********** string formatting ******************************************/
@@ -434,18 +436,23 @@
 /*********** long formatting ********************************************/
 /************************************************************************/
 
+#if defined FORMAT_LONG || defined FORMAT_INT
+typedef PyObject*
+(*IntOrLongToString)(PyObject *value, int base);
+
 static PyObject *
-format_long_internal(PyObject *value, const InternalFormatSpec *format)
+format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format,
+			    IntOrLongToString tostring)
 {
     PyObject *result = NULL;
-    int total_leading_chars_to_skip = 0; /* also includes sign, if
-                                            present */
+    PyObject *tmp = NULL;
+    STRINGLIB_CHAR *pnumeric_chars;
+    STRINGLIB_CHAR numeric_char;
     STRINGLIB_CHAR sign = '\0';
     STRINGLIB_CHAR *p;
     Py_ssize_t n_digits;       /* count of digits need from the computed
                                   string */
-    Py_ssize_t len;
-    Py_ssize_t tmp;
+    Py_ssize_t n_leading_chars;
     NumberFieldWidths spec;
     long x;
 
@@ -469,6 +476,7 @@
 
         /* taken from unicodeobject.c formatchar() */
         /* Integer input truncated to a character */
+/* XXX: won't work for int */
         x = PyLong_AsLong(value);
         if (x == -1 && PyErr_Occurred())
             goto done;
@@ -487,115 +495,101 @@
             goto done;
         }
 #endif
-        result = STRINGLIB_NEW(NULL, 1);
-        if (result == NULL)
-            goto done;
-        p = STRINGLIB_STR(result);
-        p[0] = (Py_UNICODE) x;
-        n_digits = len = 1;
+	numeric_char = (STRINGLIB_CHAR)x;
+	pnumeric_chars = &numeric_char;
+        n_digits = 1;
     }
     else {
         int base;
-        int format_leading_chars_to_skip;  /* characters added by
-                                              PyNumber_ToBase that we
-                                              want to skip over.
-                                              instead of using them,
-                                              we'll compute our
-                                              own. */
-        /* compute the base and how many characters will be added by
+	int leading_chars_to_skip;  /* 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;
-            format_leading_chars_to_skip = 2; /* 0b */
+            leading_chars_to_skip = 2; /* 0b */
             break;
         case 'o':
             base = 8;
-            format_leading_chars_to_skip = 2; /* 0o */
+            leading_chars_to_skip = 2; /* 0o */
             break;
         case 'x':
         case 'X':
             base = 16;
-            format_leading_chars_to_skip = 2; /* 0x */
+            leading_chars_to_skip = 2; /* 0x */
             break;
         default:  /* shouldn't be needed, but stops a compiler warning */
         case 'd':
             base = 10;
-            format_leading_chars_to_skip = 0;
+            leading_chars_to_skip = 0;
             break;
         }
 
-        /* do the hard part, converting to a string in a given base */
-        result = PyNumber_ToBase(value, base);
-        if (result == NULL)
+        /* Do the hard part, converting to a string in a given base */
+	tmp = tostring(value, base);
+        if (tmp == NULL)
             goto done;
 
-        n_digits = STRINGLIB_LEN(result);
-        len = n_digits;
-        p = STRINGLIB_STR(result);
-
-        /* if X, convert to uppercase */
-        if (format->type == 'X')
-            for (tmp = 0; tmp < len; tmp++)
-                p[tmp] = STRINGLIB_TOUPPER(p[tmp]);
+	pnumeric_chars = STRINGLIB_STR(tmp);
+        n_digits = STRINGLIB_LEN(tmp);
+
+	/* Remember not to modify what pnumeric_chars points to.  it
+	   might be interned.  Only modify it after we copy it into a
+	   newly allocated output buffer. */
 
-        /* is a sign character present in the output?  if so, remember it
+        /* Is a sign character present in the output?  If so, remember it
            and skip it */
-        sign = p[0];
+        sign = pnumeric_chars[0];
         if (sign == '-') {
-            total_leading_chars_to_skip += 1;
-            n_digits--;
+	    ++leading_chars_to_skip;
         }
 
-        /* skip over the leading digits (0x, 0b, etc.) */
-        assert(n_digits >= format_leading_chars_to_skip + 1);
-        n_digits -= format_leading_chars_to_skip;
-        total_leading_chars_to_skip += format_leading_chars_to_skip;
+	/* Skip over the leading chars (0x, 0b, etc.) */
+	n_digits -= leading_chars_to_skip;
+	pnumeric_chars += leading_chars_to_skip;
     }
 
+    /* Calculate the widths of the various leading and trailing parts */
     calc_number_widths(&spec, sign, n_digits, format);
 
-    /* if the buffer is getting bigger, realloc it.  if it's getting
-       smaller, don't realloc because we need to move the results
-       around first.  realloc after we've done that */
-
-    if (spec.n_total > len) {
-        if (STRINGLIB_RESIZE(&result, spec.n_total) < 0)
-            goto done;
-        /* recalc, because string might have moved */
-        p = STRINGLIB_STR(result);
-    }
-
-    /* copy the characters into position first, since we're going to
-       overwrite some of that space */
-    /* we need to move if the number of left padding in the output is
-       different from the number of characters we need to skip */
-    if ((spec.n_lpadding + spec.n_lsign + spec.n_spadding) !=
-          total_leading_chars_to_skip) {
-        memmove(p + (spec.n_lpadding + spec.n_lsign + spec.n_spadding),
-                p + total_leading_chars_to_skip,
-                n_digits * sizeof(STRINGLIB_CHAR));
+    /* Allocate a new string to hold the result */
+    result = STRINGLIB_NEW(NULL, spec.n_total);
+    if (!result)
+	goto done;
+    p = STRINGLIB_STR(result);
+
+    /* Fill in the digit parts */
+    n_leading_chars = spec.n_lpadding + spec.n_lsign + spec.n_spadding;
+    memmove(p + n_leading_chars,
+	    pnumeric_chars,
+	    n_digits * sizeof(STRINGLIB_CHAR));
+
+    /* if X, convert to uppercase */
+    if (format->type == 'X') {
+	Py_ssize_t t;
+	for (t = 0; t < n_digits; t++)
+	    p[t + n_leading_chars] = STRINGLIB_TOUPPER(p[t + n_leading_chars]);
     }
 
-    /* now fill in the non-digit parts */
+    /* Fill in the non-digit parts */
     fill_number(p, &spec, n_digits,
                 format->fill_char == '\0' ? ' ' : format->fill_char);
 
-    /* if we're getting smaller, realloc now */
-    if (spec.n_total < len) {
-        if (STRINGLIB_RESIZE(&result, spec.n_total) < 0)
-            goto done;
-    }
-
 done:
+    Py_XDECREF(tmp);
     return result;
 }
-
+#endif /* defined FORMAT_LONG || defined FORMAT_INT */
 
 /************************************************************************/
 /*********** float formatting *******************************************/
 /************************************************************************/
 
+#ifdef FORMAT_FLOAT
+#if STRINGLIB_IS_UNICODE
 /* taken from unicodeobject.c */
 static Py_ssize_t
 strtounicode(Py_UNICODE *buffer, const char *charbuffer)
@@ -607,6 +601,7 @@
 
     return len;
 }
+#endif
 
 /* the callback function to call to do the actual float formatting.
    it matches the definition of PyOS_ascii_formatd */
@@ -694,7 +689,8 @@
     /* 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), "%%.%" PY_FORMAT_SIZE_T "d%c", precision, (char)type);
+    PyOS_snprintf(fmt, sizeof(fmt), "%%.%" PY_FORMAT_SIZE_T "d%c", precision,
+		  (char)type);
 
     /* call the passed in function to do the actual formatting */
     snprintf(charbuf, sizeof(charbuf), fmt, x);
@@ -739,7 +735,8 @@
                 format->fill_char == '\0' ? ' ' : format->fill_char);
 
     /* fill in the digit parts */
-    memmove(STRINGLIB_STR(result) + (spec.n_lpadding + spec.n_lsign + spec.n_spadding),
+    memmove(STRINGLIB_STR(result) +
+	       (spec.n_lpadding + spec.n_lsign + spec.n_spadding),
             p,
             n_digits * sizeof(STRINGLIB_CHAR));
 
@@ -755,20 +752,43 @@
     else
         return _format_float(format->type, value, format, PyOS_ascii_formatd);
 }
+#endif /* FORMAT_FLOAT */
 
 /************************************************************************/
 /*********** built in formatters ****************************************/
 /************************************************************************/
-
+#ifdef FORMAT_STRING
 PyObject *
 FORMAT_STRING(PyObject* value, PyObject* args)
 {
     PyObject *format_spec;
     PyObject *result = NULL;
+#if PY_VERSION_HEX < 0x03000000
+    PyObject *tmp = NULL;
+#endif
     InternalFormatSpec format;
 
-    if (!PyArg_ParseTuple(args, STRINGLIB_PARSE_CODE ":__format__", &format_spec))
+    /* If 2.x, we accept either str or unicode, and try to convert it
+       to the right type.  In 3.x, we insist on only unicode */
+#if PY_VERSION_HEX >= 0x03000000
+    if (!PyArg_ParseTuple(args, STRINGLIB_PARSE_CODE ":__format__",
+			  &format_spec))
         goto done;
+#else
+    /* If 2.x, convert format_spec to the same type as value */
+    /* This is to allow things like u''.format('') */
+    if (!PyArg_ParseTuple(args, "O:__format__", &format_spec))
+        goto done;
+    if (!(PyString_Check(format_spec) || PyUnicode_Check(format_spec))) {
+        PyErr_Format(PyExc_TypeError, "__format__ arg must be str "
+		     "or unicode, not %s", Py_TYPE(format_spec)->tp_name);
+	goto done;
+    }
+    tmp = STRINGLIB_TOSTR(format_spec);
+    if (tmp == NULL)
+        goto done;
+    format_spec = tmp;
+#endif
 
     /* check for the special case of zero length format spec, make
        it equivalent to str(value) */
@@ -777,6 +797,7 @@
         goto done;
     }
 
+
     /* parse the format_spec */
     if (!parse_internal_render_format_spec(format_spec, &format, 's'))
         goto done;
@@ -795,18 +816,24 @@
     }
 
 done:
+#if PY_VERSION_HEX < 0x03000000
+    Py_XDECREF(tmp);
+#endif
     return result;
 }
+#endif /* FORMAT_STRING */
 
-PyObject *
-FORMAT_LONG(PyObject* value, PyObject* args)
+#if defined FORMAT_LONG || defined FORMAT_INT
+static PyObject*
+format_int_or_long(PyObject* value, PyObject* args, IntOrLongToString tostring)
 {
     PyObject *format_spec;
     PyObject *result = NULL;
     PyObject *tmp = NULL;
     InternalFormatSpec format;
 
-    if (!PyArg_ParseTuple(args, STRINGLIB_PARSE_CODE ":__format__", &format_spec))
+    if (!PyArg_ParseTuple(args, STRINGLIB_PARSE_CODE ":__format__",
+			  &format_spec))
         goto done;
 
     /* check for the special case of zero length format spec, make
@@ -828,8 +855,9 @@
     case 'o':
     case 'x':
     case 'X':
-        /* no type conversion needed, already an int.  do the formatting */
-        result = format_long_internal(value, &format);
+        /* no type conversion needed, already an int (or long).  do
+	   the formatting */
+	    result = format_int_or_long_internal(value, &format, tostring);
         break;
 
     case 'e':
@@ -858,7 +886,52 @@
     Py_XDECREF(tmp);
     return result;
 }
+#endif /* FORMAT_LONG || defined FORMAT_INT */
+
+#ifdef FORMAT_LONG
+/* Need to define long_format as a function that will convert a long
+   to a string.  In 3.0, _PyLong_Format has the correct signature.  In
+   2.x, we need to fudge a few parameters */
+#if PY_VERSION_HEX >= 0x03000000
+#define long_format _PyLong_Format
+#else
+static PyObject*
+long_format(PyObject* value, int base)
+{
+    /* Convert to base, don't add trailing 'L', and use the new octal
+       format. We already know this is a long object */
+    assert(PyLong_Check(value));
+    /* convert to base, don't add 'L', and use the new octal format */
+    return _PyLong_Format(value, base, 0, 1);
+}
+#endif
+
+PyObject *
+FORMAT_LONG(PyObject* value, PyObject* args)
+{
+    return format_int_or_long(value, args, long_format);
+}
+#endif /* FORMAT_LONG */
+
+#ifdef FORMAT_INT
+/* this is only used for 2.x, not 3.0 */
+static PyObject*
+int_format(PyObject* value, int base)
+{
+    /* Convert to base, and use the new octal format. We already
+       know this is an int object */
+    assert(PyInt_Check(value));
+    return _PyInt_Format((PyIntObject*)value, base, 1);
+}
+
+PyObject *
+FORMAT_INT(PyObject* value, PyObject* args)
+{
+    return format_int_or_long(value, args, int_format);
+}
+#endif /* FORMAT_INT */
 
+#ifdef FORMAT_FLOAT
 PyObject *
 FORMAT_FLOAT(PyObject *value, PyObject *args)
 {
@@ -904,3 +977,4 @@
 done:
     return result;
 }
+#endif /* FORMAT_FLOAT */

Modified: python/branches/py3k/Objects/stringlib/string_format.h
==============================================================================
--- python/branches/py3k/Objects/stringlib/string_format.h	(original)
+++ python/branches/py3k/Objects/stringlib/string_format.h	Sun Feb 17 20:48:00 2008
@@ -6,6 +6,11 @@
 */
 
 
+/* Defines for Python 2.6 compatability */
+#if PY_VERSION_HEX < 0x03000000
+#define PyLong_FromSsize_t _PyLong_FromSsize_t
+#endif
+
 /* Defines for more efficiently reallocating the string buffer */
 #define INITIAL_SIZE_INCREMENT 100
 #define SIZE_MULTIPLIER 2
@@ -470,66 +475,6 @@
     field object and field specification string generated by
     get_field_and_spec, and renders the field into the output string.
 
-    format() does the actual calling of the objects __format__ method.
-*/
-
-
-/* returns fieldobj.__format__(format_spec) */
-static PyObject *
-format(PyObject *fieldobj, SubString *format_spec)
-{
-    static PyObject *format_str = NULL;
-    PyObject *meth;
-    PyObject *spec = NULL;
-    PyObject *result = NULL;
-
-    /* Initialize cached value */
-    if (format_str == NULL) {
-        /* Initialize static variable needed by _PyType_Lookup */
-        format_str = PyUnicode_FromString("__format__");
-        if (format_str == NULL)
-            return NULL;
-    }
-
-    /* Make sure the type is initialized.  float gets initialized late */
-    if (Py_TYPE(fieldobj)->tp_dict == NULL)
-        if (PyType_Ready(Py_TYPE(fieldobj)) < 0)
-            return NULL;
-
-    /* we need to create an object out of the pointers we have */
-    spec = SubString_new_object_or_empty(format_spec);
-    if (spec == NULL)
-        goto done;
-
-    /* Find the (unbound!) __format__ method (a borrowed reference) */
-    meth = _PyType_Lookup(Py_TYPE(fieldobj), format_str);
-    if (meth == NULL) {
-        PyErr_Format(PyExc_TypeError,
-                     "Type %.100s doesn't define __format__",
-                     Py_TYPE(fieldobj)->tp_name);
-        goto done;
-    }
-
-    /* And call it, binding it to the value */
-    result = PyObject_CallFunctionObjArgs(meth, fieldobj, spec, NULL);
-    if (result == NULL)
-        goto done;
-
-    if (!STRINGLIB_CHECK(result)) {
-        PyErr_SetString(PyExc_TypeError,
-                        "__format__ method did not return "
-                        STRINGLIB_TYPE_NAME);
-        Py_DECREF(result);
-        result = NULL;
-        goto done;
-    }
-
-done:
-    Py_XDECREF(spec);
-    return result;
-}
-
-/*
     render_field calls fieldobj.__format__(format_spec) method, and
     appends to the output.
 */
@@ -537,14 +482,21 @@
 render_field(PyObject *fieldobj, SubString *format_spec, OutputString *output)
 {
     int ok = 0;
-    PyObject *result = format(fieldobj, format_spec);
+    PyObject *result = NULL;
 
+    /* we need to create an object out of the pointers we have */
+    PyObject *format_spec_object = SubString_new_object_or_empty(format_spec);
+    if (format_spec_object == NULL)
+        goto done;
+
+    result = PyObject_Format(fieldobj, format_spec_object);
     if (result == NULL)
         goto done;
 
     ok = output_data(output,
                      STRINGLIB_STR(result), STRINGLIB_LEN(result));
 done:
+    Py_DECREF(format_spec_object);
     Py_XDECREF(result);
     return ok;
 }
@@ -770,7 +722,7 @@
     case 'r':
         return PyObject_Repr(obj);
     case 's':
-        return PyObject_Str(obj);
+        return STRINGLIB_TOSTR(obj);
     default:
         PyErr_Format(PyExc_ValueError,
                      "Unknown converion specifier %c",
@@ -845,7 +797,7 @@
 }
 
 /*
-    do_markup is the top-level loop for the format() function.  It
+    do_markup is the top-level loop for the format() method.  It
     searches through the format string for escapes to markup codes, and
     calls other functions to move non-markup text to the output,
     and to perform the markup to the output.
@@ -958,7 +910,7 @@
 typedef struct {
     PyObject_HEAD
 
-    PyUnicodeObject *str;
+    STRINGLIB_OBJECT *str;
 
     MarkupIterator it_markup;
 } formatteriterobject;
@@ -984,7 +936,7 @@
     SubString literal;
     SubString field_name;
     SubString format_spec;
-    Py_UNICODE conversion;
+    STRINGLIB_CHAR conversion;
     int format_spec_needs_expanding;
     int result = MarkupIterator_next(&it->it_markup, &literal, &field_name,
                                      &format_spec, &conversion,
@@ -1028,7 +980,7 @@
             Py_INCREF(conversion_str);
         }
         else
-            conversion_str = PyUnicode_FromUnicode(&conversion, 1);
+	    conversion_str = STRINGLIB_NEW(&conversion, 1);
         if (conversion_str == NULL)
             goto done;
 
@@ -1047,7 +999,7 @@
     {NULL,		NULL}		/* sentinel */
 };
 
-PyTypeObject PyFormatterIter_Type = {
+static PyTypeObject PyFormatterIter_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0)
     "formatteriterator",		/* tp_name */
     sizeof(formatteriterobject),	/* tp_basicsize */
@@ -1085,7 +1037,7 @@
    describing the parsed elements.  It's a wrapper around
    stringlib/string_format.h's MarkupIterator */
 static PyObject *
-formatter_parser(PyUnicodeObject *self)
+formatter_parser(STRINGLIB_OBJECT *self)
 {
     formatteriterobject *it;
 
@@ -1099,8 +1051,8 @@
 
     /* initialize the contained MarkupIterator */
     MarkupIterator_init(&it->it_markup,
-                        PyUnicode_AS_UNICODE(self),
-                        PyUnicode_GET_SIZE(self));
+                        STRINGLIB_STR(self),
+                        STRINGLIB_LEN(self));
 
     return (PyObject *)it;
 }
@@ -1118,7 +1070,7 @@
 typedef struct {
     PyObject_HEAD
 
-    PyUnicodeObject *str;
+    STRINGLIB_OBJECT *str;
 
     FieldNameIterator it_field;
 } fieldnameiterobject;
@@ -1220,7 +1172,7 @@
    field_name_split.  The iterator it returns is a
    FieldNameIterator */
 static PyObject *
-formatter_field_name_split(PyUnicodeObject *self)
+formatter_field_name_split(STRINGLIB_OBJECT *self)
 {
     SubString first;
     Py_ssize_t first_idx;

Modified: python/branches/py3k/Objects/stringlib/stringdefs.h
==============================================================================
--- python/branches/py3k/Objects/stringlib/stringdefs.h	(original)
+++ python/branches/py3k/Objects/stringlib/stringdefs.h	Sun Feb 17 20:48:00 2008
@@ -6,12 +6,15 @@
    compiled as unicode. */
 #define STRINGLIB_IS_UNICODE     0
 
+#define STRINGLIB_OBJECT         PyStringObject
 #define STRINGLIB_CHAR           char
 #define STRINGLIB_TYPE_NAME      "string"
 #define STRINGLIB_PARSE_CODE     "S"
-#define STRINGLIB_EMPTY          string_empty
+#define STRINGLIB_EMPTY          nullstring
 #define STRINGLIB_ISDECIMAL(x)   ((x >= '0') && (x <= '9'))
 #define STRINGLIB_TODECIMAL(x)   (STRINGLIB_ISDECIMAL(x) ? (x - '0') : -1)
+#define STRINGLIB_TOUPPER        toupper
+#define STRINGLIB_TOLOWER        tolower
 #define STRINGLIB_FILL           memset
 #define STRINGLIB_STR            PyString_AS_STRING
 #define STRINGLIB_LEN            PyString_GET_SIZE

Modified: python/branches/py3k/Objects/stringlib/unicodedefs.h
==============================================================================
--- python/branches/py3k/Objects/stringlib/unicodedefs.h	(original)
+++ python/branches/py3k/Objects/stringlib/unicodedefs.h	Sun Feb 17 20:48:00 2008
@@ -6,6 +6,7 @@
    compiled as unicode. */
 #define STRINGLIB_IS_UNICODE     1
 
+#define STRINGLIB_OBJECT         PyUnicodeObject
 #define STRINGLIB_CHAR           Py_UNICODE
 #define STRINGLIB_TYPE_NAME      "unicode"
 #define STRINGLIB_PARSE_CODE     "U"
@@ -20,7 +21,12 @@
 #define STRINGLIB_NEW            PyUnicode_FromUnicode
 #define STRINGLIB_RESIZE         PyUnicode_Resize
 #define STRINGLIB_CHECK          PyUnicode_Check
+
+#if PY_VERSION_HEX < 0x03000000
+#define STRINGLIB_TOSTR          PyObject_Unicode
+#else
 #define STRINGLIB_TOSTR          PyObject_Str
+#endif
 
 #define STRINGLIB_WANT_CONTAINS_OBJ 1
 

Modified: python/branches/py3k/Python/bltinmodule.c
==============================================================================
--- python/branches/py3k/Python/bltinmodule.c	(original)
+++ python/branches/py3k/Python/bltinmodule.c	Sun Feb 17 20:48:00 2008
@@ -304,58 +304,13 @@
 static PyObject *
 builtin_format(PyObject *self, PyObject *args)
 {
-    static PyObject * format_str = NULL;
     PyObject *value;
-    PyObject *spec = NULL;
-    PyObject *meth;
-    PyObject *empty = NULL;
-    PyObject *result = NULL;
-
-    /* Initialize cached value */
-    if (format_str == NULL) {
-        /* Initialize static variable needed by _PyType_Lookup */
-        format_str = PyUnicode_FromString("__format__");
-        if (format_str == NULL)
-            goto done;
-    }
-
-    if (!PyArg_ParseTuple(args, "O|U:format", &value, &spec))
-        goto done;
-
-    /* initialize the default value */
-    if (spec == NULL) {
-        empty = PyUnicode_FromUnicode(NULL, 0);
-        spec = empty;
-    }
-
-    /* Make sure the type is initialized.  float gets initialized late */
-    if (Py_TYPE(value)->tp_dict == NULL)
-        if (PyType_Ready(Py_TYPE(value)) < 0)
-            goto done;
-
-    /* Find the (unbound!) __format__ method (a borrowed reference) */
-    meth = _PyType_Lookup(Py_TYPE(value), format_str);
-    if (meth == NULL) {
-        PyErr_Format(PyExc_TypeError,
-                "Type %.100s doesn't define __format__",
-                Py_TYPE(value)->tp_name);
-            goto done;
-    }
-
-    /* And call it, binding it to the value */
-    result = PyObject_CallFunctionObjArgs(meth, value, spec, NULL);
-
-    if (result && !PyUnicode_Check(result)) {
-        PyErr_SetString(PyExc_TypeError,
-            "__format__ method did not return string");
-        Py_DECREF(result);
-        result = NULL;
-        goto done;
-    }
-
-done:
-    Py_XDECREF(empty);
-    return result;
+    PyObject *format_spec = NULL;
+
+    if (!PyArg_ParseTuple(args, "O|U:format", &value, &format_spec))
+        return NULL;
+
+    return PyObject_Format(value, format_spec);
 }
 
 PyDoc_STRVAR(format_doc,


More information about the Python-3000-checkins mailing list