[Python-checkins] r79596 - in python/trunk: Lib/test/test_builtin.py Misc/NEWS Objects/abstract.c Objects/typeobject.c

eric.smith python-checkins at python.org
Fri Apr 2 14:30:56 CEST 2010


Author: eric.smith
Date: Fri Apr  2 14:30:56 2010
New Revision: 79596

Log:
Issue 7994: Make object.__format__ with a non-empty format string a PendingDecprecationWarning. Still need to remove uses of this from various tests.

Modified:
   python/trunk/Lib/test/test_builtin.py
   python/trunk/Misc/NEWS
   python/trunk/Objects/abstract.c
   python/trunk/Objects/typeobject.c

Modified: python/trunk/Lib/test/test_builtin.py
==============================================================================
--- python/trunk/Lib/test/test_builtin.py	(original)
+++ python/trunk/Lib/test/test_builtin.py	Fri Apr  2 14:30:56 2010
@@ -4,6 +4,7 @@
 import unittest
 from test.test_support import fcmp, have_unicode, TESTFN, unlink, \
                               run_unittest, check_py3k_warnings
+import warnings
 from operator import neg
 
 import sys, cStringIO, random, UserDict
@@ -1483,6 +1484,41 @@
         self.assertRaises(TypeError, object().__format__, object())
         self.assertRaises(TypeError, object().__format__, None)
 
+        # --------------------------------------------------------------------
+        # Issue #7994: object.__format__ with a non-empty format string is
+        #  pending deprecated
+        def test_deprecated_format_string(obj, fmt_str, should_raise_warning):
+            with warnings.catch_warnings(record=True) as w:
+                warnings.simplefilter("always", PendingDeprecationWarning)
+                format(obj, fmt_str)
+            if should_raise_warning:
+                self.assertEqual(len(w), 1)
+                self.assertIsInstance(w[0].message, PendingDeprecationWarning)
+                self.assertIn('object.__format__ with a non-empty format '
+                              'string', str(w[0].message))
+            else:
+                self.assertEqual(len(w), 0)
+
+        fmt_strs = ['', 's', u'', u's']
+
+        class A:
+            def __format__(self, fmt_str):
+                return format('', fmt_str)
+
+        for fmt_str in fmt_strs:
+            test_deprecated_format_string(A(), fmt_str, False)
+
+        class B:
+            pass
+
+        class C(object):
+            pass
+
+        for cls in [object, B, C]:
+            for fmt_str in fmt_strs:
+                test_deprecated_format_string(cls(), fmt_str, len(fmt_str) != 0)
+        # --------------------------------------------------------------------
+
         # make sure we can take a subclass of str as a format spec
         class DerivedFromStr(str): pass
         self.assertEqual(format(0, DerivedFromStr('10')), '         0')

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Fri Apr  2 14:30:56 2010
@@ -12,6 +12,14 @@
 Core and Builtins
 -----------------
 
+- Issue #7994: Issue a PendingDeprecationWarning if object.__format__
+  is called with a non-empty format string. This is an effort to
+  future-proof user code. If a derived class does not currently
+  implement __format__ but later adds its own __format__, it would
+  most likely break user code that had supplied a format string. This
+  will be changed to a DeprecationWaring in Python 3.3 and it will be
+  an error in Python 3.4.
+
 - Issue #8268: Old-style classes (not just instances) now support weak
   references.
 

Modified: python/trunk/Objects/abstract.c
==============================================================================
--- python/trunk/Objects/abstract.c	(original)
+++ python/trunk/Objects/abstract.c	Fri Apr  2 14:30:56 2010
@@ -777,8 +777,9 @@
 							      NULL);
 			Py_DECREF(bound_method);
 		} else {
-			PyObject *self_as_str;
-			PyObject *format_method;
+			PyObject *self_as_str = NULL;
+			PyObject *format_method = NULL;
+			Py_ssize_t format_len;
 
 			PyErr_Clear();
 			/* Per the PEP, convert to str (or unicode,
@@ -786,29 +787,53 @@
 			   specifier).  For new-style classes, this
 			   logic is done by object.__format__(). */
 #ifdef Py_USING_UNICODE
-			if (spec_is_unicode)
+			if (spec_is_unicode) {
+				format_len = PyUnicode_GET_SIZE(format_spec);
 				self_as_str = PyObject_Unicode(obj);
-			else
+			} else
 #endif
+			{
+				format_len = PyString_GET_SIZE(format_spec);
 				self_as_str = PyObject_Str(obj);
+			}
 			if (self_as_str == NULL)
-				goto done;
+				goto done1;
+
+			if (format_len > 0) {
+				/* See the almost identical code in
+				   typeobject.c for new-style
+				   classes. */
+				if (PyErr_WarnEx(
+					PyExc_PendingDeprecationWarning,
+					"object.__format__ with a non-empty "
+					"format string is deprecated", 1)
+				     < 0) {
+					goto done1;
+				}
+				/* Eventually this will become an
+				   error:
+				PyErr_Format(PyExc_TypeError,
+				   "non-empty format string passed to "
+				   "object.__format__");
+				goto done1;
+				*/
+			}
 
 			/* Then call str.__format__ on that result */
 			format_method = PyObject_GetAttr(self_as_str,
 							 str__format__);
 			if (format_method == NULL) {
-				Py_DECREF(self_as_str);
-				goto done;
+				goto done1;
 			}
-                        result = PyObject_CallFunctionObjArgs(format_method,
+			result = PyObject_CallFunctionObjArgs(format_method,
 							      format_spec,
 							      NULL);
-			Py_DECREF(self_as_str);
-			Py_DECREF(format_method);
+done1:
+			Py_XDECREF(self_as_str);
+			Py_XDECREF(format_method);
 			if (result == NULL)
 				goto done;
-                }
+		}
 	} else {
 		/* Not an instance of a classic class, use the code
 		   from py3k */

Modified: python/trunk/Objects/typeobject.c
==============================================================================
--- python/trunk/Objects/typeobject.c	(original)
+++ python/trunk/Objects/typeobject.c	Fri Apr  2 14:30:56 2010
@@ -3414,31 +3414,54 @@
         PyObject *self_as_str = NULL;
         PyObject *result = NULL;
         PyObject *format_meth = NULL;
+        Py_ssize_t format_len;
 
         if (!PyArg_ParseTuple(args, "O:__format__", &format_spec))
                 return NULL;
 #ifdef Py_USING_UNICODE
 	if (PyUnicode_Check(format_spec)) {
+	        format_len = PyUnicode_GET_SIZE(format_spec);
 	        self_as_str = PyObject_Unicode(self);
 	} else if (PyString_Check(format_spec)) {
 #else
         if (PyString_Check(format_spec)) {
 #endif
+	        format_len = PyString_GET_SIZE(format_spec);
 	        self_as_str = PyObject_Str(self);
 	} else {
-	        PyErr_SetString(PyExc_TypeError, "argument to __format__ must be unicode or str");
+	        PyErr_SetString(PyExc_TypeError,
+                         "argument to __format__ must be unicode or str");
 	        return NULL;
 	}
 
         if (self_as_str != NULL) {
+                /* Issue 7994: If we're converting to a string, we
+                   should reject format specifications */
+                if (format_len > 0) {
+                    if (PyErr_WarnEx(PyExc_PendingDeprecationWarning,
+                         "object.__format__ with a non-empty format "
+                         "string is deprecated", 1) < 0) {
+                            goto done;
+                    }
+                    /* Eventually this will become an error:
+                    PyErr_Format(PyExc_TypeError,
+                       "non-empty format string passed to object.__format__");
+                    goto done;
+                    */
+                }
+
                 /* find the format function */
-                format_meth = PyObject_GetAttrString(self_as_str, "__format__");
+                format_meth = PyObject_GetAttrString(self_as_str,
+                                                     "__format__");
                 if (format_meth != NULL) {
                        /* and call it */
-                        result = PyObject_CallFunctionObjArgs(format_meth, format_spec, NULL);
+                        result = PyObject_CallFunctionObjArgs(format_meth,
+                                                              format_spec,
+                                                              NULL);
                 }
         }
 
+done:
         Py_XDECREF(self_as_str);
         Py_XDECREF(format_meth);
 


More information about the Python-checkins mailing list