diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -9,6 +9,15 @@ check_warnings, cpython_only) from test.test_pep352 import ignore_deprecation_warnings +class NaiveException(Exception): + def __init__(self, x): + self.x = x + +class SlottedNaiveException(Exception): + __slots__ = ('x',) + def __init__(self, x): + self.x = x + # XXX This is not really enough, each *operation* should be tested! class ExceptionTests(unittest.TestCase): @@ -266,6 +275,10 @@ {'message' : '', 'args' : (u'\u3042', 0, 1, 'ouch'), 'object' : u'\u3042', 'reason' : 'ouch', 'start' : 0, 'end' : 1}), + (NaiveException, ('foo',), + {'args': ('foo',), 'x': 'foo'}), + (SlottedNaiveException, ('foo',), + {'args': ('foo',), 'x': 'foo'}), ] try: exceptionList.append( @@ -284,7 +297,8 @@ if type(e) is not exc: raise # Verify module name - self.assertEqual(type(e).__module__, 'exceptions') + if not type(e).__name__.endswith('NaiveException'): + self.assertEqual(type(e).__module__, 'exceptions') # Verify no ref leaks in Exc_str() s = str(e) for checkArgName in expected: diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -9,6 +9,9 @@ Core and Builtins ----------------- +- Issue #1692335: Move initial args assignment to + BaseException.__new__ to help pickling of naive subclasses. + - Issue #17275: Corrected class name in init error messages of the C version of BufferedWriter and BufferedRandom. diff --git a/Objects/exceptions.c b/Objects/exceptions.c --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -37,6 +37,17 @@ /* the dict is created on the fly in PyObject_GenericSetAttr */ self->message = self->dict = NULL; + if (args) { + self->args = args; + Py_INCREF(args); + if (PyTuple_GET_SIZE(self->args) == 1) { + Py_CLEAR(self->message); + self->message = PyTuple_GET_ITEM(self->args, 0); + Py_INCREF(self->message); + } + return (PyObject *)self; + } + self->args = PyTuple_New(0); if (!self->args) { Py_DECREF(self); @@ -55,10 +66,12 @@ static int BaseException_init(PyBaseExceptionObject *self, PyObject *args, PyObject *kwds) { + PyObject *tmp; + if (!_PyArg_NoKeywords(Py_TYPE(self)->tp_name, kwds)) return -1; - Py_DECREF(self->args); + tmp = self->args; self->args = args; Py_INCREF(self->args); @@ -67,6 +80,8 @@ self->message = PyTuple_GET_ITEM(self->args, 0); Py_INCREF(self->message); } + + Py_XDECREF(tmp); return 0; }