diff options
author | Brett Cannon <bcannon@gmail.com> | 2007-05-05 01:34:02 (GMT) |
---|---|---|
committer | Brett Cannon <bcannon@gmail.com> | 2007-05-05 01:34:02 (GMT) |
commit | 229cee2d3dc2d962d5cbda96f2411c5cec0a9293 (patch) | |
tree | 3a474da4aafdfa18c6b6f8848c4792e70e5b9ad0 | |
parent | 2ebc4d8054615e9e5ad8ef6e35aad6ac419233c8 (diff) | |
download | cpython-229cee2d3dc2d962d5cbda96f2411c5cec0a9293.zip cpython-229cee2d3dc2d962d5cbda96f2411c5cec0a9293.tar.gz cpython-229cee2d3dc2d962d5cbda96f2411c5cec0a9293.tar.bz2 |
Deprecate BaseException.message as per PEP 352.
-rw-r--r-- | Doc/lib/libexcs.tex | 22 | ||||
-rw-r--r-- | Lib/ConfigParser.py | 15 | ||||
-rw-r--r-- | Lib/test/test_defaultdict.py | 2 | ||||
-rw-r--r-- | Lib/test/test_exceptions.py | 58 | ||||
-rw-r--r-- | Lib/test/test_pep352.py | 76 | ||||
-rw-r--r-- | Misc/NEWS | 2 | ||||
-rw-r--r-- | Objects/exceptions.c | 42 |
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): @@ -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 */ |