Commit 229cee2d authored by Brett Cannon's avatar Brett Cannon

Deprecate BaseException.message as per PEP 352.

parent 2ebc4d80
...@@ -23,14 +23,10 @@ an ``associated value'' indicating the detailed cause of the error. ...@@ -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 This may be a string or a tuple containing several items of
information (e.g., an error code and a string explaining the code). information (e.g., an error code and a string explaining the code).
The associated value is the second argument to the The associated value is the second argument to the
\keyword{raise}\stindex{raise} statement. For string exceptions, the \keyword{raise}\stindex{raise} statement. If the exception class is
associated value itself will be stored in the variable named as the derived from the standard root class \exception{BaseException}, the
second argument of the \keyword{except} clause (if any). For class associated value is present as the exception instance's \member{args}
exceptions, that variable receives the exception instance. If the attribute.
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.
User code can raise built-in exceptions. This can be used to test an 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 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 ...@@ -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 inherited by user-defined classes (for that use \exception{Exception}). If
\function{str()} or \function{unicode()} is called on an instance of this \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 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 the emptry string when there were no arguments. All arguments are
passed in, it is stored in the \member{message} attribute. If more than one stored in \member{args} as a tuple.
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.
\versionadded{2.5} \versionadded{2.5}
\end{excdesc} \end{excdesc}
......
...@@ -106,6 +106,21 @@ MAX_INTERPOLATION_DEPTH = 10 ...@@ -106,6 +106,21 @@ MAX_INTERPOLATION_DEPTH = 10
class Error(Exception): class Error(Exception):
"""Base class for ConfigParser exceptions.""" """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=''): def __init__(self, msg=''):
self.message = msg self.message = msg
Exception.__init__(self, msg) Exception.__init__(self, msg)
......
...@@ -137,7 +137,7 @@ class TestDefaultDict(unittest.TestCase): ...@@ -137,7 +137,7 @@ class TestDefaultDict(unittest.TestCase):
try: try:
d1[(1,)] d1[(1,)]
except KeyError, err: except KeyError, err:
self.assertEqual(err.message, (1,)) self.assertEqual(err.args[0], (1,))
else: else:
self.fail("expected KeyError") self.fail("expected KeyError")
......
...@@ -5,7 +5,9 @@ import sys ...@@ -5,7 +5,9 @@ import sys
import unittest import unittest
import pickle, cPickle 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! # XXX This is not really enough, each *operation* should be tested!
...@@ -272,6 +274,8 @@ class ExceptionTests(unittest.TestCase): ...@@ -272,6 +274,8 @@ class ExceptionTests(unittest.TestCase):
except NameError: except NameError:
pass pass
with guard_warnings_filter():
ignore_message_warning()
for exc, args, expected in exceptionList: for exc, args, expected in exceptionList:
try: try:
raise exc(*args) raise exc(*args)
......
...@@ -6,6 +6,13 @@ from test.test_support import run_unittest, guard_warnings_filter ...@@ -6,6 +6,13 @@ from test.test_support import run_unittest, guard_warnings_filter
import os import os
from platform import system as platform_system 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): class ExceptionClassTests(unittest.TestCase):
"""Tests for anything relating to exception objects themselves (e.g., """Tests for anything relating to exception objects themselves (e.g.,
...@@ -15,8 +22,12 @@ class ExceptionClassTests(unittest.TestCase): ...@@ -15,8 +22,12 @@ class ExceptionClassTests(unittest.TestCase):
self.failUnless(issubclass(Exception, object)) self.failUnless(issubclass(Exception, object))
def verify_instance_interface(self, ins): def verify_instance_interface(self, ins):
for attr in ("args", "message", "__str__", "__repr__", "__getitem__"): with guard_warnings_filter():
self.failUnless(hasattr(ins, attr), "%s missing %s attribute" % ignore_message_warning()
for attr in ("args", "message", "__str__", "__repr__",
"__getitem__"):
self.failUnless(hasattr(ins, attr),
"%s missing %s attribute" %
(ins.__class__.__name__, attr)) (ins.__class__.__name__, attr))
def test_inheritance(self): def test_inheritance(self):
...@@ -84,9 +95,13 @@ class ExceptionClassTests(unittest.TestCase): ...@@ -84,9 +95,13 @@ class ExceptionClassTests(unittest.TestCase):
# Make sure interface works properly when given a single argument # Make sure interface works properly when given a single argument
arg = "spam" arg = "spam"
exc = Exception(arg) exc = Exception(arg)
results = ([len(exc.args), 1], [exc.args[0], arg], [exc.message, arg], 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)], [str(exc), str(arg)], [unicode(exc), unicode(arg)],
[repr(exc), exc.__class__.__name__ + repr(exc.args)], [exc[0], arg]) [repr(exc), exc.__class__.__name__ + repr(exc.args)], [exc[0],
arg])
self.interface_test_driver(results) self.interface_test_driver(results)
def test_interface_multi_arg(self): def test_interface_multi_arg(self):
...@@ -94,6 +109,8 @@ class ExceptionClassTests(unittest.TestCase): ...@@ -94,6 +109,8 @@ class ExceptionClassTests(unittest.TestCase):
arg_count = 3 arg_count = 3
args = tuple(range(arg_count)) args = tuple(range(arg_count))
exc = Exception(*args) exc = Exception(*args)
with guard_warnings_filter():
ignore_message_warning()
results = ([len(exc.args), arg_count], [exc.args, args], results = ([len(exc.args), arg_count], [exc.args, args],
[exc.message, ''], [str(exc), str(args)], [exc.message, ''], [str(exc), str(args)],
[unicode(exc), unicode(args)], [unicode(exc), unicode(args)],
...@@ -104,11 +121,36 @@ class ExceptionClassTests(unittest.TestCase): ...@@ -104,11 +121,36 @@ class ExceptionClassTests(unittest.TestCase):
def test_interface_no_arg(self): def test_interface_no_arg(self):
# Make sure that with no args that interface is correct # Make sure that with no args that interface is correct
exc = Exception() exc = Exception()
results = ([len(exc.args), 0], [exc.args, tuple()], [exc.message, ''], with guard_warnings_filter():
ignore_message_warning()
results = ([len(exc.args), 0], [exc.args, tuple()],
[exc.message, ''],
[str(exc), ''], [unicode(exc), u''], [str(exc), ''], [unicode(exc), u''],
[repr(exc), exc.__class__.__name__ + '()'], [True, True]) [repr(exc), exc.__class__.__name__ + '()'], [True, True])
self.interface_test_driver(results) 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): class UsageTests(unittest.TestCase):
"""Test usage of exceptions""" """Test usage of exceptions"""
......
...@@ -12,6 +12,8 @@ What's New in Python 2.6 alpha 1? ...@@ -12,6 +12,8 @@ What's New in Python 2.6 alpha 1?
Core and builtins Core and builtins
----------------- -----------------
- Deprecate BaseException.message as per PEP 352.
- Bug #1303614: don't expose object's __dict__ when the dict is - Bug #1303614: don't expose object's __dict__ when the dict is
inherited from a builtin base. inherited from a builtin base.
......
...@@ -212,13 +212,6 @@ static PySequenceMethods BaseException_as_sequence = { ...@@ -212,13 +212,6 @@ static PySequenceMethods BaseException_as_sequence = {
0 /* sq_inplace_repeat; */ 0 /* sq_inplace_repeat; */
}; };
static PyMemberDef BaseException_members[] = {
{"message", T_OBJECT, offsetof(PyBaseExceptionObject, message), 0,
PyDoc_STR("exception message")},
{NULL} /* Sentinel */
};
static PyObject * static PyObject *
BaseException_get_dict(PyBaseExceptionObject *self) BaseException_get_dict(PyBaseExceptionObject *self)
{ {
...@@ -274,9 +267,42 @@ BaseException_set_args(PyBaseExceptionObject *self, PyObject *val) ...@@ -274,9 +267,42 @@ BaseException_set_args(PyBaseExceptionObject *self, PyObject *val)
return 0; 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[] = { static PyGetSetDef BaseException_getset[] = {
{"__dict__", (getter)BaseException_get_dict, (setter)BaseException_set_dict}, {"__dict__", (getter)BaseException_get_dict, (setter)BaseException_set_dict},
{"args", (getter)BaseException_get_args, (setter)BaseException_set_args}, {"args", (getter)BaseException_get_args, (setter)BaseException_set_args},
{"message", (getter)BaseException_get_message,
(setter)BaseException_set_message},
{NULL}, {NULL},
}; };
...@@ -312,7 +338,7 @@ static PyTypeObject _PyExc_BaseException = { ...@@ -312,7 +338,7 @@ static PyTypeObject _PyExc_BaseException = {
0, /* tp_iter */ 0, /* tp_iter */
0, /* tp_iternext */ 0, /* tp_iternext */
BaseException_methods, /* tp_methods */ BaseException_methods, /* tp_methods */
BaseException_members, /* tp_members */ 0, /* tp_members */
BaseException_getset, /* tp_getset */ BaseException_getset, /* tp_getset */
0, /* tp_base */ 0, /* tp_base */
0, /* tp_dict */ 0, /* tp_dict */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment