summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_pep3151.py60
-rw-r--r--Misc/NEWS3
-rw-r--r--Objects/exceptions.c216
3 files changed, 217 insertions, 62 deletions
diff --git a/Lib/test/test_pep3151.py b/Lib/test/test_pep3151.py
index e327f42..8af9e0c 100644
--- a/Lib/test/test_pep3151.py
+++ b/Lib/test/test_pep3151.py
@@ -12,6 +12,23 @@ from test import support
class SubOSError(OSError):
pass
+class SubOSErrorWithInit(OSError):
+ def __init__(self, message, bar):
+ self.bar = bar
+ super().__init__(message)
+
+class SubOSErrorWithNew(OSError):
+ def __new__(cls, message, baz):
+ self = super().__new__(cls, message)
+ self.baz = baz
+ return self
+
+class SubOSErrorCombinedInitFirst(SubOSErrorWithInit, SubOSErrorWithNew):
+ pass
+
+class SubOSErrorCombinedNewFirst(SubOSErrorWithNew, SubOSErrorWithInit):
+ pass
+
class HierarchyTest(unittest.TestCase):
@@ -74,11 +91,6 @@ class HierarchyTest(unittest.TestCase):
e = OSError(errcode, "Some message")
self.assertIs(type(e), OSError)
- def test_OSError_subclass_mapping(self):
- # When constructing an OSError subclass, errno mapping isn't done
- e = SubOSError(EEXIST, "Bad file descriptor")
- self.assertIs(type(e), SubOSError)
-
def test_try_except(self):
filename = "some_hopefully_non_existing_file"
@@ -144,6 +156,44 @@ class AttributesTest(unittest.TestCase):
# XXX VMSError not tested
+class ExplicitSubclassingTest(unittest.TestCase):
+
+ def test_errno_mapping(self):
+ # When constructing an OSError subclass, errno mapping isn't done
+ e = SubOSError(EEXIST, "Bad file descriptor")
+ self.assertIs(type(e), SubOSError)
+
+ def test_init_overriden(self):
+ e = SubOSErrorWithInit("some message", "baz")
+ self.assertEqual(e.bar, "baz")
+ self.assertEqual(e.args, ("some message",))
+
+ def test_init_kwdargs(self):
+ e = SubOSErrorWithInit("some message", bar="baz")
+ self.assertEqual(e.bar, "baz")
+ self.assertEqual(e.args, ("some message",))
+
+ def test_new_overriden(self):
+ e = SubOSErrorWithNew("some message", "baz")
+ self.assertEqual(e.baz, "baz")
+ self.assertEqual(e.args, ("some message",))
+
+ def test_new_kwdargs(self):
+ e = SubOSErrorWithNew("some message", baz="baz")
+ self.assertEqual(e.baz, "baz")
+ self.assertEqual(e.args, ("some message",))
+
+ def test_init_new_overriden(self):
+ e = SubOSErrorCombinedInitFirst("some message", "baz")
+ self.assertEqual(e.bar, "baz")
+ self.assertEqual(e.baz, "baz")
+ self.assertEqual(e.args, ("some message",))
+ e = SubOSErrorCombinedNewFirst("some message", "baz")
+ self.assertEqual(e.bar, "baz")
+ self.assertEqual(e.baz, "baz")
+ self.assertEqual(e.args, ("some message",))
+
+
def test_main():
support.run_unittest(__name__)
diff --git a/Misc/NEWS b/Misc/NEWS
index d2fbdfc..cd9b061 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@ What's New in Python 3.3 Alpha 1?
Core and Builtins
-----------------
+- Fix OSError.__init__ and OSError.__new__ so that each of them can be
+ overriden and take additional arguments (followup to issue #12555).
+
- Fix the fix for issue #12149: it was incorrect, although it had the side
effect of appearing to resolve the issue. Thanks to Mark Shannon for
noticing.
diff --git a/Objects/exceptions.c b/Objects/exceptions.c
index 37b7875..3318115 100644
--- a/Objects/exceptions.c
+++ b/Objects/exceptions.c
@@ -58,7 +58,7 @@ BaseException_init(PyBaseExceptionObject *self, PyObject *args, PyObject *kwds)
if (!_PyArg_NoKeywords(Py_TYPE(self)->tp_name, kwds))
return -1;
- Py_DECREF(self->args);
+ Py_XDECREF(self->args);
self->args = args;
Py_INCREF(self->args);
@@ -587,37 +587,34 @@ SimpleExtendsException(PyExc_Exception, ImportError,
* when it was supplied.
*/
-static PyObject *
-OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
-{
- PyOSErrorObject *self = NULL;
- Py_ssize_t nargs;
-
- PyObject *myerrno = NULL, *strerror = NULL, *filename = NULL;
- PyObject *subslice = NULL;
+/* This function doesn't cleanup on error, the caller should */
+static int
+oserror_parse_args(PyObject **p_args,
+ PyObject **myerrno, PyObject **strerror,
+ PyObject **filename
#ifdef MS_WINDOWS
- PyObject *winerror = NULL;
- long winerrcode = 0;
+ , PyObject **winerror
#endif
+ )
+{
+ Py_ssize_t nargs;
+ PyObject *args = *p_args;
- if (!_PyArg_NoKeywords(type->tp_name, kwds))
- return NULL;
- Py_INCREF(args);
nargs = PyTuple_GET_SIZE(args);
#ifdef MS_WINDOWS
if (nargs >= 2 && nargs <= 4) {
if (!PyArg_UnpackTuple(args, "OSError", 2, 4,
- &myerrno, &strerror, &filename, &winerror))
- goto error;
- if (winerror && PyLong_Check(winerror)) {
- long errcode;
+ myerrno, strerror, filename, winerror))
+ return -1;
+ if (*winerror && PyLong_Check(*winerror)) {
+ long errcode, winerrcode;
PyObject *newargs;
Py_ssize_t i;
- winerrcode = PyLong_AsLong(winerror);
+ winerrcode = PyLong_AsLong(*winerror);
if (winerrcode == -1 && PyErr_Occurred())
- goto error;
+ return -1;
/* Set errno to the corresponding POSIX errno (overriding
first argument). Windows Socket error codes (>= 10000)
have the same value as their POSIX counterparts.
@@ -626,59 +623,55 @@ OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
errcode = winerror_to_errno(winerrcode);
else
errcode = winerrcode;
- myerrno = PyLong_FromLong(errcode);
- if (!myerrno)
- goto error;
+ *myerrno = PyLong_FromLong(errcode);
+ if (!*myerrno)
+ return -1;
newargs = PyTuple_New(nargs);
if (!newargs)
- goto error;
- PyTuple_SET_ITEM(newargs, 0, myerrno);
+ return -1;
+ PyTuple_SET_ITEM(newargs, 0, *myerrno);
for (i = 1; i < nargs; i++) {
PyObject *val = PyTuple_GET_ITEM(args, i);
Py_INCREF(val);
PyTuple_SET_ITEM(newargs, i, val);
}
Py_DECREF(args);
- args = newargs;
+ args = *p_args = newargs;
}
}
#else
if (nargs >= 2 && nargs <= 3) {
if (!PyArg_UnpackTuple(args, "OSError", 2, 3,
- &myerrno, &strerror, &filename))
- goto error;
+ myerrno, strerror, filename))
+ return -1;
}
#endif
- if (myerrno && PyLong_Check(myerrno) &&
- errnomap && (PyObject *) type == PyExc_OSError) {
- PyObject *newtype;
- newtype = PyDict_GetItem(errnomap, myerrno);
- if (newtype) {
- assert(PyType_Check(newtype));
- type = (PyTypeObject *) newtype;
- }
- else if (PyErr_Occurred())
- goto error;
- }
- self = (PyOSErrorObject *) type->tp_alloc(type, 0);
- if (!self)
- goto error;
+ return 0;
+}
- self->dict = NULL;
- self->traceback = self->cause = self->context = NULL;
- self->written = -1;
+static int
+oserror_init(PyOSErrorObject *self, PyObject **p_args,
+ PyObject *myerrno, PyObject *strerror,
+ PyObject *filename
+#ifdef MS_WINDOWS
+ , PyObject *winerror
+#endif
+ )
+{
+ PyObject *args = *p_args;
+ Py_ssize_t nargs = PyTuple_GET_SIZE(args);
/* self->filename will remain Py_None otherwise */
if (filename && filename != Py_None) {
- if ((PyObject *) type == PyExc_BlockingIOError &&
+ if (Py_TYPE(self) == (PyTypeObject *) PyExc_BlockingIOError &&
PyNumber_Check(filename)) {
/* BlockingIOError's 3rd argument can be the number of
* characters written.
*/
self->written = PyNumber_AsSsize_t(filename, PyExc_ValueError);
if (self->written == -1 && PyErr_Occurred())
- goto error;
+ return -1;
}
else {
Py_INCREF(filename);
@@ -687,20 +680,15 @@ OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
if (nargs >= 2 && nargs <= 3) {
/* filename is removed from the args tuple (for compatibility
purposes, see test_exceptions.py) */
- subslice = PyTuple_GetSlice(args, 0, 2);
+ PyObject *subslice = PyTuple_GetSlice(args, 0, 2);
if (!subslice)
- goto error;
+ return -1;
Py_DECREF(args); /* replacing args */
- args = subslice;
+ *p_args = args = subslice;
}
}
}
-
- /* Steals the reference to args */
- self->args = args;
- args = NULL;
-
Py_XINCREF(myerrno);
self->myerrno = myerrno;
@@ -712,6 +700,90 @@ OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
self->winerror = winerror;
#endif
+ /* Steals the reference to args */
+ self->args = args;
+ args = NULL;
+
+ return 0;
+}
+
+static PyObject *
+OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
+static int
+OSError_init(PyOSErrorObject *self, PyObject *args, PyObject *kwds);
+
+static int
+oserror_use_init(PyTypeObject *type)
+{
+ /* When __init__ is defined in a OSError subclass, we want any
+ extraneous argument to __new__ to be ignored. The only reasonable
+ solution, given __new__ takes a variable number of arguments,
+ is to defer arg parsing and initialization to __init__.
+
+ But when __new__ is overriden as well, it should call our __new__
+ with the right arguments.
+
+ (see http://bugs.python.org/issue12555#msg148829 )
+ */
+ if (type->tp_init != (initproc) OSError_init &&
+ type->tp_new == (newfunc) OSError_new) {
+ assert((PyObject *) type != PyExc_OSError);
+ return 1;
+ }
+ return 0;
+}
+
+static PyObject *
+OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyOSErrorObject *self = NULL;
+ PyObject *myerrno = NULL, *strerror = NULL, *filename = NULL;
+#ifdef MS_WINDOWS
+ PyObject *winerror = NULL;
+#endif
+
+ if (!oserror_use_init(type)) {
+ if (!_PyArg_NoKeywords(type->tp_name, kwds))
+ return NULL;
+
+ Py_INCREF(args);
+ if (oserror_parse_args(&args, &myerrno, &strerror, &filename
+#ifdef MS_WINDOWS
+ , &winerror
+#endif
+ ))
+ goto error;
+
+ if (myerrno && PyLong_Check(myerrno) &&
+ errnomap && (PyObject *) type == PyExc_OSError) {
+ PyObject *newtype;
+ newtype = PyDict_GetItem(errnomap, myerrno);
+ if (newtype) {
+ assert(PyType_Check(newtype));
+ type = (PyTypeObject *) newtype;
+ }
+ else if (PyErr_Occurred())
+ goto error;
+ }
+ }
+
+ self = (PyOSErrorObject *) type->tp_alloc(type, 0);
+ if (!self)
+ goto error;
+
+ self->dict = NULL;
+ self->traceback = self->cause = self->context = NULL;
+ self->written = -1;
+
+ if (!oserror_use_init(type)) {
+ if (oserror_init(self, &args, myerrno, strerror, filename
+#ifdef MS_WINDOWS
+ , winerror
+#endif
+ ))
+ goto error;
+ }
+
return (PyObject *) self;
error:
@@ -721,10 +793,40 @@ error:
}
static int
-OSError_init(PySyntaxErrorObject *self, PyObject *args, PyObject *kwds)
+OSError_init(PyOSErrorObject *self, PyObject *args, PyObject *kwds)
{
- /* Everything already done in OSError_new */
+ PyObject *myerrno = NULL, *strerror = NULL, *filename = NULL;
+#ifdef MS_WINDOWS
+ PyObject *winerror = NULL;
+#endif
+
+ if (!oserror_use_init(Py_TYPE(self)))
+ /* Everything already done in OSError_new */
+ return 0;
+
+ if (!_PyArg_NoKeywords(Py_TYPE(self)->tp_name, kwds))
+ return -1;
+
+ Py_INCREF(args);
+ if (oserror_parse_args(&args, &myerrno, &strerror, &filename
+#ifdef MS_WINDOWS
+ , &winerror
+#endif
+ ))
+ goto error;
+
+ if (oserror_init(self, &args, myerrno, strerror, filename
+#ifdef MS_WINDOWS
+ , winerror
+#endif
+ ))
+ goto error;
+
return 0;
+
+error:
+ Py_XDECREF(args);
+ return -1;
}
static int