]> granicus.if.org Git - python/commitdiff
No more raising of string exceptions!
authorBrett Cannon <bcannon@gmail.com>
Tue, 30 Jan 2007 21:34:36 +0000 (21:34 +0000)
committerBrett Cannon <bcannon@gmail.com>
Tue, 30 Jan 2007 21:34:36 +0000 (21:34 +0000)
The next step of PEP 352 (for 2.6) causes raising a string exception to trigger
a TypeError.  Trying to catch a string exception raises a DeprecationWarning.
References to string exceptions has been removed from the docs since they are
now just an error.

Doc/lib/libexcs.tex
Doc/ref/ref4.tex
Lib/test/test_pep352.py
Misc/NEWS
Python/ceval.c

index 6d2a3c5d2f34070dce571d4692ffa29d5a4646da..b793fd39d5dcdd67c4366fe8fc2f242119059da0 100644 (file)
@@ -10,22 +10,6 @@ module never needs to be imported explicitly: the exceptions are
 provided in the built-in namespace as well as the \module{exceptions}
 module.
 
-\begin{notice}
-In past versions of Python string exceptions were supported.  In
-Python 1.5 and newer versions, all standard exceptions have been
-converted to class objects and users are encouraged to do the same.
-String exceptions will raise a \code{DeprecationWarning} in Python 2.5 and
-newer.
-In future versions, support for string exceptions will be removed.
-
-Two distinct string objects with the same value are considered different
-exceptions.  This is done to force programmers to use exception names
-rather than their string value when specifying exception handlers.
-The string value of all built-in exceptions is their name, but this is
-not a requirement for user-defined exceptions or exceptions defined by
-library modules.
-\end{notice}
-
 For class exceptions, in a \keyword{try}\stindex{try} statement with
 an \keyword{except}\stindex{except} clause that mentions a particular
 class, that clause also handles any exception classes derived from
index 12a2b92e1676376c0e31ac31e16b82e2fa7c8df4..f38b948077346652763b759bc0ed763b9588a2a1 100644 (file)
@@ -203,10 +203,6 @@ Exceptions can also be identified by strings, in which case the
 value can be raised along with the identifying string which can be
 passed to the handler.
 
-\deprecated{2.5}{String exceptions should not be used in new code.
-They will not be supported in a future version of Python.  Old code
-should be rewritten to use class exceptions instead.}
-
 \begin{notice}[warning]
 Messages to exceptions are not part of the Python API.  Their contents may
 change from one version of Python to the next without warning and should not
index b2322b0a3a784acdb2526da9cbb6860c2fc82c56..4ce25d68eebf93ddafcfadf95071d84aba34e228 100644 (file)
@@ -2,7 +2,7 @@ import unittest
 import __builtin__
 import exceptions
 import warnings
-from test.test_support import run_unittest
+from test.test_support import run_unittest, guard_warnings_filter
 import os
 from platform import system as platform_system
 
@@ -113,13 +113,8 @@ class UsageTests(unittest.TestCase):
 
     """Test usage of exceptions"""
 
-    def setUp(self):
-        self._filters = warnings.filters[:]
-
-    def tearDown(self):
-        warnings.filters = self._filters[:]
-
     def test_raise_classic(self):
+        # Raising a classic class is okay (for now).
         class ClassicClass:
             pass
         try:
@@ -136,6 +131,10 @@ class UsageTests(unittest.TestCase):
             self.fail("unable to raise class class instance")
 
     def test_raise_new_style_non_exception(self):
+        # You cannot raise a new-style class that does not inherit from
+        # BaseException; the ability was not possible until BaseException's
+        # introduction so no need to support new-style objects that do not
+        # inherit from it.
         class NewStyleClass(object):
             pass
         try:
@@ -143,35 +142,52 @@ class UsageTests(unittest.TestCase):
         except TypeError:
             pass
         except:
-            self.fail("unable to raise new-style class")
+            self.fail("able to raise new-style class")
         try:
             raise NewStyleClass()
         except TypeError:
             pass
         except:
-            self.fail("unable to raise new-style class instance")
+            self.fail("able to raise new-style class instance")
 
     def test_raise_string(self):
-        warnings.resetwarnings()
-        warnings.filterwarnings("error")
+        # Raising a string raises TypeError.
         try:
             raise "spam"
-        except DeprecationWarning:
+        except TypeError:
             pass
         except:
-            self.fail("raising a string did not cause a DeprecationWarning")
+            self.fail("was able to raise a string exception")
 
     def test_catch_string(self):
-        # Test will be pertinent when catching exceptions raises a
-        #   DeprecationWarning
-        warnings.filterwarnings("ignore", "raising")
-        str_exc = "spam"
-        try:
-            raise str_exc
-        except str_exc:
-            pass
-        except:
-            self.fail("catching a string exception failed")
+        # Catching a string should trigger a DeprecationWarning.
+        with guard_warnings_filter():
+            warnings.resetwarnings()
+            warnings.filterwarnings("error")
+            str_exc = "spam"
+            try:
+                try:
+                    raise StandardError
+                except str_exc:
+                    pass
+            except DeprecationWarning:
+                pass
+            except StandardError:
+                self.fail("catching a string exception did not raise "
+                            "DeprecationWarning")
+            # Make sure that even if the string exception is listed in a tuple
+            # that a warning is raised.
+            try:
+                try:
+                    raise StandardError
+                except (AssertionError, str_exc):
+                    pass
+            except DeprecationWarning:
+                pass
+            except StandardError:
+                self.fail("catching a string exception specified in a tuple did "
+                            "not raise DeprecationWarning")
+
 
 def test_main():
     run_unittest(ExceptionClassTests, UsageTests)
index 7dc2bc3574972594afcf45e1298b8ec40e2a0d35..fd760aa354267a7490bf61771272563547e92087 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,9 @@ What's New in Python 2.6 alpha 1?
 Core and builtins
 -----------------
 
+- PEP 352: Raising a string exception now triggers a TypeError.  Attempting to
+  catch a string exception raises DeprecationWarning.
+
 - Bug #1377858: Fix the segfaulting of the interpreter when an object created
   a weakref on itself during a __del__ call for new-style classes (classic
   classes still have the bug).
index 67eefba07ad80ab249bf3d1d199846ec3e213584..ffdc75bfa67bd0583082fd5d80885ee4fcda4e1d 100644 (file)
@@ -2206,8 +2206,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
                case SETUP_LOOP:
                case SETUP_EXCEPT:
                case SETUP_FINALLY:
-                       /* NOTE: If you add any new block-setup opcodes that are not try/except/finally
-                          handlers, you may need to update the PyGen_NeedsFinalizing() function. */
+                       /* NOTE: If you add any new block-setup opcodes that are
+                          not try/except/finally handlers, you may need to
+                          update the PyGen_NeedsFinalizing() function. */
 
                        PyFrame_BlockSetup(f, opcode, INSTR_OFFSET() + oparg,
                                           STACK_LEVEL());
@@ -3069,15 +3070,7 @@ do_raise(PyObject *type, PyObject *value, PyObject *tb)
                Py_DECREF(tmp);
        }
 
-       if (PyString_CheckExact(type)) {
-               /* Raising builtin string is deprecated but still allowed --
-                * do nothing.  Raising an instance of a new-style str
-                * subclass is right out. */
-               if (PyErr_Warn(PyExc_DeprecationWarning,
-                          "raising a string exception is deprecated"))
-                       goto raise_error;
-       }
-       else if (PyExceptionClass_Check(type))
+       if (PyExceptionClass_Check(type))
                PyErr_NormalizeException(&type, &value, &tb);
 
        else if (PyExceptionInstance_Check(type)) {
@@ -3099,8 +3092,7 @@ do_raise(PyObject *type, PyObject *value, PyObject *tb)
                /* Not something you can raise.  You get an exception
                   anyway, just not what you specified :-) */
                PyErr_Format(PyExc_TypeError,
-                            "exceptions must be classes, instances, or "
-                            "strings (deprecated), not %s",
+                            "exceptions must be classes or instances, not %s",
                             type->ob_type->tp_name);
                goto raise_error;
        }
@@ -3985,6 +3977,35 @@ cmp_outcome(int op, register PyObject *v, register PyObject *w)
                res = !res;
                break;
        case PyCmp_EXC_MATCH:
+               if (PyTuple_Check(w)) {
+                       Py_ssize_t i, length;
+                       length = PyTuple_Size(w);
+                       for (i = 0; i < length; i += 1) {
+                               PyObject *exc = PyTuple_GET_ITEM(w, i);
+                               if (PyString_Check(exc)) {
+                                       int ret_val;
+                                       ret_val = PyErr_WarnEx(
+                                                       PyExc_DeprecationWarning,
+                                                       "catching of string "
+                                                       "exceptions is "
+                                                       "deprecated", 1);
+                                       if (ret_val == -1)
+                                               return NULL;
+                               }
+                       }
+               }
+               else {
+                       if (PyString_Check(w)) {
+                               int ret_val;
+                               ret_val = PyErr_WarnEx(
+                                               PyExc_DeprecationWarning,
+                                               "catching of string "
+                                               "exceptions is deprecated",
+                                               1);
+                               if (ret_val == -1)
+                                       return NULL;
+                       }
+               }
                res = PyErr_GivenExceptionMatches(v, w);
                break;
        default: