summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/lib/libexcs.tex22
-rw-r--r--Lib/ConfigParser.py15
-rw-r--r--Lib/test/test_defaultdict.py2
-rw-r--r--Lib/test/test_exceptions.py58
-rw-r--r--Lib/test/test_pep352.py76
-rw-r--r--Misc/NEWS2
-rw-r--r--Objects/exceptions.c42
7 files changed, 148 insertions, 69 deletions
diff --git a/Doc/lib/libexcs.tex b/Doc/lib/libexcs.tex
index b793fd3..b0f80b6 100644
--- a/Doc/lib/libexcs.tex
+++ b/Doc/lib/libexcs.tex
@@ -23,14 +23,10 @@ an ``associated value'' indicating the detailed cause of the error.
This may be a string or a tuple containing several items of
information (e.g., an error code and a string explaining the code).
The associated value is the second argument to the
-\keyword{raise}\stindex{raise} statement. For string exceptions, the
-associated value itself will be stored in the variable named as the
-second argument of the \keyword{except} clause (if any). For class
-exceptions, that variable receives the exception instance. If the
-exception class is derived from the standard root class
-\exception{BaseException}, the associated value is present as the
-exception instance's \member{args} attribute. If there is a single argument
-(as is preferred), it is bound to the \member{message} attribute.
+\keyword{raise}\stindex{raise} statement. If the exception class is
+derived from the standard root class \exception{BaseException}, the
+associated value is present as the exception instance's \member{args}
+attribute.
User code can raise built-in exceptions. This can be used to test an
exception handler or to report an error condition ``just like'' the
@@ -56,14 +52,8 @@ The base class for all built-in exceptions. It is not meant to be directly
inherited by user-defined classes (for that use \exception{Exception}). If
\function{str()} or \function{unicode()} is called on an instance of this
class, the representation of the argument(s) to the instance are returned or
-the emptry string when there were no arguments. If only a single argument is
-passed in, it is stored in the \member{message} attribute. If more than one
-argument is passed in, \member{message} is set to the empty string. These
-semantics are meant to reflect the fact that \member{message} is to store a
-text message explaining why the exception had been raised. If more data needs
-to be attached to the exception, attach it through arbitrary attributes on the
-instance. All arguments are also stored in \member{args} as a tuple, but it will
-eventually be deprecated and thus its use is discouraged.
+the emptry string when there were no arguments. All arguments are
+stored in \member{args} as a tuple.
\versionadded{2.5}
\end{excdesc}
diff --git a/Lib/ConfigParser.py b/Lib/ConfigParser.py
index 2902939..131d697 100644
--- a/Lib/ConfigParser.py
+++ b/Lib/ConfigParser.py
@@ -106,6 +106,21 @@ MAX_INTERPOLATION_DEPTH = 10
class Error(Exception):
"""Base class for ConfigParser exceptions."""
+ def _get_message(self):
+ """Getter for 'message'; needed only to override deprecation in
+ BaseException."""
+ return self.__message
+
+ def _set_message(self, value):
+ """Setter for 'message'; needed only to override deprecation in
+ BaseException."""
+ self.__message = value
+
+ # BaseException.message has been deprecated since Python 2.6. To prevent
+ # DeprecationWarning from popping up over this pre-existing attribute, use
+ # a new property that takes lookup precedence.
+ message = property(_get_message, _set_message)
+
def __init__(self, msg=''):
self.message = msg
Exception.__init__(self, msg)
diff --git a/Lib/test/test_defaultdict.py b/Lib/test/test_defaultdict.py
index 08be005..6108840 100644
--- a/Lib/test/test_defaultdict.py
+++ b/Lib/test/test_defaultdict.py
@@ -137,7 +137,7 @@ class TestDefaultDict(unittest.TestCase):
try:
d1[(1,)]
except KeyError, err:
- self.assertEqual(err.message, (1,))
+ self.assertEqual(err.args[0], (1,))
else:
self.fail("expected KeyError")
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
index d9a00b9..1f7105e 100644
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -5,7 +5,9 @@ import sys
import unittest
import pickle, cPickle
-from test.test_support import TESTFN, unlink, run_unittest
+from test.test_support import (TESTFN, unlink, run_unittest,
+ guard_warnings_filter)
+from test.test_pep352 import ignore_message_warning
# XXX This is not really enough, each *operation* should be tested!
@@ -272,32 +274,34 @@ class ExceptionTests(unittest.TestCase):
except NameError:
pass
- for exc, args, expected in exceptionList:
- try:
- raise exc(*args)
- except BaseException, e:
- if type(e) is not exc:
- raise
- # Verify module name
- self.assertEquals(type(e).__module__, 'exceptions')
- # Verify no ref leaks in Exc_str()
- s = str(e)
- for checkArgName in expected:
- self.assertEquals(repr(getattr(e, checkArgName)),
- repr(expected[checkArgName]),
- 'exception "%s", attribute "%s"' %
- (repr(e), checkArgName))
-
- # test for pickling support
- for p in pickle, cPickle:
- for protocol in range(p.HIGHEST_PROTOCOL + 1):
- new = p.loads(p.dumps(e, protocol))
- for checkArgName in expected:
- got = repr(getattr(new, checkArgName))
- want = repr(expected[checkArgName])
- self.assertEquals(got, want,
- 'pickled "%r", attribute "%s' %
- (e, checkArgName))
+ with guard_warnings_filter():
+ ignore_message_warning()
+ for exc, args, expected in exceptionList:
+ try:
+ raise exc(*args)
+ except BaseException, e:
+ if type(e) is not exc:
+ raise
+ # Verify module name
+ self.assertEquals(type(e).__module__, 'exceptions')
+ # Verify no ref leaks in Exc_str()
+ s = str(e)
+ for checkArgName in expected:
+ self.assertEquals(repr(getattr(e, checkArgName)),
+ repr(expected[checkArgName]),
+ 'exception "%s", attribute "%s"' %
+ (repr(e), checkArgName))
+
+ # test for pickling support
+ for p in pickle, cPickle:
+ for protocol in range(p.HIGHEST_PROTOCOL + 1):
+ new = p.loads(p.dumps(e, protocol))
+ for checkArgName in expected:
+ got = repr(getattr(new, checkArgName))
+ want = repr(expected[checkArgName])
+ self.assertEquals(got, want,
+ 'pickled "%r", attribute "%s' %
+ (e, checkArgName))
def testSlicing(self):
# Test that you can slice an exception directly instead of requiring
diff --git a/Lib/test/test_pep352.py b/Lib/test/test_pep352.py
index abed627..89b2fdc 100644
--- a/Lib/test/test_pep352.py
+++ b/Lib/test/test_pep352.py
@@ -6,6 +6,13 @@ from test.test_support import run_unittest, guard_warnings_filter
import os
from platform import system as platform_system
+def ignore_message_warning():
+ """Ignore the DeprecationWarning for BaseException.message."""
+ warnings.resetwarnings()
+ warnings.filterwarnings("ignore", "BaseException.message",
+ DeprecationWarning)
+
+
class ExceptionClassTests(unittest.TestCase):
"""Tests for anything relating to exception objects themselves (e.g.,
@@ -15,9 +22,13 @@ class ExceptionClassTests(unittest.TestCase):
self.failUnless(issubclass(Exception, object))
def verify_instance_interface(self, ins):
- for attr in ("args", "message", "__str__", "__repr__", "__getitem__"):
- self.failUnless(hasattr(ins, attr), "%s missing %s attribute" %
- (ins.__class__.__name__, attr))
+ with guard_warnings_filter():
+ ignore_message_warning()
+ for attr in ("args", "message", "__str__", "__repr__",
+ "__getitem__"):
+ self.failUnless(hasattr(ins, attr),
+ "%s missing %s attribute" %
+ (ins.__class__.__name__, attr))
def test_inheritance(self):
# Make sure the inheritance hierarchy matches the documentation
@@ -84,30 +95,61 @@ class ExceptionClassTests(unittest.TestCase):
# Make sure interface works properly when given a single argument
arg = "spam"
exc = Exception(arg)
- results = ([len(exc.args), 1], [exc.args[0], arg], [exc.message, arg],
- [str(exc), str(arg)], [unicode(exc), unicode(arg)],
- [repr(exc), exc.__class__.__name__ + repr(exc.args)], [exc[0], arg])
- self.interface_test_driver(results)
+ with guard_warnings_filter():
+ ignore_message_warning()
+ results = ([len(exc.args), 1], [exc.args[0], arg],
+ [exc.message, arg],
+ [str(exc), str(arg)], [unicode(exc), unicode(arg)],
+ [repr(exc), exc.__class__.__name__ + repr(exc.args)], [exc[0],
+ arg])
+ self.interface_test_driver(results)
def test_interface_multi_arg(self):
# Make sure interface correct when multiple arguments given
arg_count = 3
args = tuple(range(arg_count))
exc = Exception(*args)
- results = ([len(exc.args), arg_count], [exc.args, args],
- [exc.message, ''], [str(exc), str(args)],
- [unicode(exc), unicode(args)],
- [repr(exc), exc.__class__.__name__ + repr(exc.args)],
- [exc[-1], args[-1]])
- self.interface_test_driver(results)
+ with guard_warnings_filter():
+ ignore_message_warning()
+ results = ([len(exc.args), arg_count], [exc.args, args],
+ [exc.message, ''], [str(exc), str(args)],
+ [unicode(exc), unicode(args)],
+ [repr(exc), exc.__class__.__name__ + repr(exc.args)],
+ [exc[-1], args[-1]])
+ self.interface_test_driver(results)
def test_interface_no_arg(self):
# Make sure that with no args that interface is correct
exc = Exception()
- results = ([len(exc.args), 0], [exc.args, tuple()], [exc.message, ''],
- [str(exc), ''], [unicode(exc), u''],
- [repr(exc), exc.__class__.__name__ + '()'], [True, True])
- self.interface_test_driver(results)
+ with guard_warnings_filter():
+ ignore_message_warning()
+ results = ([len(exc.args), 0], [exc.args, tuple()],
+ [exc.message, ''],
+ [str(exc), ''], [unicode(exc), u''],
+ [repr(exc), exc.__class__.__name__ + '()'], [True, True])
+ self.interface_test_driver(results)
+
+
+ def test_message_deprecation(self):
+ # As of Python 2.6, BaseException.message is deprecated.
+ with guard_warnings_filter():
+ warnings.resetwarnings()
+ warnings.filterwarnings('error')
+
+ try:
+ BaseException().message
+ except DeprecationWarning:
+ pass
+ else:
+ self.fail("BaseException.message not deprecated")
+
+ exc = BaseException()
+ try:
+ exc.message = ''
+ except DeprecationWarning:
+ pass
+ else:
+ self.fail("BaseException.message assignment not deprecated")
class UsageTests(unittest.TestCase):
diff --git a/Misc/NEWS b/Misc/NEWS
index 2a1ee0a..6229cb6 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,8 @@ What's New in Python 2.6 alpha 1?
Core and builtins
-----------------
+- Deprecate BaseException.message as per PEP 352.
+
- Bug #1303614: don't expose object's __dict__ when the dict is
inherited from a builtin base.
diff --git a/Objects/exceptions.c b/Objects/exceptions.c
index 65419de..927114e 100644
--- a/Objects/exceptions.c
+++ b/Objects/exceptions.c
@@ -212,13 +212,6 @@ static PySequenceMethods BaseException_as_sequence = {
0 /* sq_inplace_repeat; */
};
-static PyMemberDef BaseException_members[] = {
- {"message", T_OBJECT, offsetof(PyBaseExceptionObject, message), 0,
- PyDoc_STR("exception message")},
- {NULL} /* Sentinel */
-};
-
-
static PyObject *
BaseException_get_dict(PyBaseExceptionObject *self)
{
@@ -274,9 +267,42 @@ BaseException_set_args(PyBaseExceptionObject *self, PyObject *val)
return 0;
}
+static PyObject *
+BaseException_get_message(PyBaseExceptionObject *self)
+{
+ int ret;
+ ret = PyErr_WarnEx(PyExc_DeprecationWarning,
+ "BaseException.message has been deprecated as "
+ "of Python 2.6",
+ 1);
+ if (ret == -1)
+ return NULL;
+
+ Py_INCREF(self->message);
+ return self->message;
+}
+
+static int
+BaseException_set_message(PyBaseExceptionObject *self, PyObject *val)
+{
+ int ret;
+ ret = PyErr_WarnEx(PyExc_DeprecationWarning,
+ "BaseException.message has been deprecated as "
+ "of Python 2.6",
+ 1);
+ if (ret == -1)
+ return -1;
+ Py_INCREF(val);
+ Py_DECREF(self->message);
+ self->message = val;
+ return 0;
+}
+
static PyGetSetDef BaseException_getset[] = {
{"__dict__", (getter)BaseException_get_dict, (setter)BaseException_set_dict},
{"args", (getter)BaseException_get_args, (setter)BaseException_set_args},
+ {"message", (getter)BaseException_get_message,
+ (setter)BaseException_set_message},
{NULL},
};
@@ -312,7 +338,7 @@ static PyTypeObject _PyExc_BaseException = {
0, /* tp_iter */
0, /* tp_iternext */
BaseException_methods, /* tp_methods */
- BaseException_members, /* tp_members */
+ 0, /* tp_members */
BaseException_getset, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */