Commit e0e2735f authored by Antoine Pitrou's avatar Antoine Pitrou

Fix OSError.__init__ and OSError.__new__ so that each of them can be

overriden and take additional arguments (followup to issue #12555).
parent d73a9acb
...@@ -12,6 +12,23 @@ from test import support ...@@ -12,6 +12,23 @@ from test import support
class SubOSError(OSError): class SubOSError(OSError):
pass 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): class HierarchyTest(unittest.TestCase):
...@@ -74,11 +91,6 @@ class HierarchyTest(unittest.TestCase): ...@@ -74,11 +91,6 @@ class HierarchyTest(unittest.TestCase):
e = OSError(errcode, "Some message") e = OSError(errcode, "Some message")
self.assertIs(type(e), OSError) 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): def test_try_except(self):
filename = "some_hopefully_non_existing_file" filename = "some_hopefully_non_existing_file"
...@@ -144,6 +156,44 @@ class AttributesTest(unittest.TestCase): ...@@ -144,6 +156,44 @@ class AttributesTest(unittest.TestCase):
# XXX VMSError not tested # 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(): def test_main():
support.run_unittest(__name__) support.run_unittest(__name__)
......
...@@ -10,6 +10,9 @@ What's New in Python 3.3 Alpha 1? ...@@ -10,6 +10,9 @@ What's New in Python 3.3 Alpha 1?
Core and Builtins 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 - 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 effect of appearing to resolve the issue. Thanks to Mark Shannon for
noticing. noticing.
......
...@@ -58,7 +58,7 @@ BaseException_init(PyBaseExceptionObject *self, PyObject *args, PyObject *kwds) ...@@ -58,7 +58,7 @@ BaseException_init(PyBaseExceptionObject *self, PyObject *args, PyObject *kwds)
if (!_PyArg_NoKeywords(Py_TYPE(self)->tp_name, kwds)) if (!_PyArg_NoKeywords(Py_TYPE(self)->tp_name, kwds))
return -1; return -1;
Py_DECREF(self->args); Py_XDECREF(self->args);
self->args = args; self->args = args;
Py_INCREF(self->args); Py_INCREF(self->args);
...@@ -587,37 +587,34 @@ SimpleExtendsException(PyExc_Exception, ImportError, ...@@ -587,37 +587,34 @@ SimpleExtendsException(PyExc_Exception, ImportError,
* when it was supplied. * when it was supplied.
*/ */
static PyObject * /* This function doesn't cleanup on error, the caller should */
OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static int
{ oserror_parse_args(PyObject **p_args,
PyOSErrorObject *self = NULL; PyObject **myerrno, PyObject **strerror,
Py_ssize_t nargs; PyObject **filename
PyObject *myerrno = NULL, *strerror = NULL, *filename = NULL;
PyObject *subslice = NULL;
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
PyObject *winerror = NULL; , PyObject **winerror
long winerrcode = 0;
#endif #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); nargs = PyTuple_GET_SIZE(args);
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (nargs >= 2 && nargs <= 4) { if (nargs >= 2 && nargs <= 4) {
if (!PyArg_UnpackTuple(args, "OSError", 2, 4, if (!PyArg_UnpackTuple(args, "OSError", 2, 4,
&myerrno, &strerror, &filename, &winerror)) myerrno, strerror, filename, winerror))
goto error; return -1;
if (winerror && PyLong_Check(winerror)) { if (*winerror && PyLong_Check(*winerror)) {
long errcode; long errcode, winerrcode;
PyObject *newargs; PyObject *newargs;
Py_ssize_t i; Py_ssize_t i;
winerrcode = PyLong_AsLong(winerror); winerrcode = PyLong_AsLong(*winerror);
if (winerrcode == -1 && PyErr_Occurred()) if (winerrcode == -1 && PyErr_Occurred())
goto error; return -1;
/* Set errno to the corresponding POSIX errno (overriding /* Set errno to the corresponding POSIX errno (overriding
first argument). Windows Socket error codes (>= 10000) first argument). Windows Socket error codes (>= 10000)
have the same value as their POSIX counterparts. have the same value as their POSIX counterparts.
...@@ -626,59 +623,55 @@ OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds) ...@@ -626,59 +623,55 @@ OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
errcode = winerror_to_errno(winerrcode); errcode = winerror_to_errno(winerrcode);
else else
errcode = winerrcode; errcode = winerrcode;
myerrno = PyLong_FromLong(errcode); *myerrno = PyLong_FromLong(errcode);
if (!myerrno) if (!*myerrno)
goto error; return -1;
newargs = PyTuple_New(nargs); newargs = PyTuple_New(nargs);
if (!newargs) if (!newargs)
goto error; return -1;
PyTuple_SET_ITEM(newargs, 0, myerrno); PyTuple_SET_ITEM(newargs, 0, *myerrno);
for (i = 1; i < nargs; i++) { for (i = 1; i < nargs; i++) {
PyObject *val = PyTuple_GET_ITEM(args, i); PyObject *val = PyTuple_GET_ITEM(args, i);
Py_INCREF(val); Py_INCREF(val);
PyTuple_SET_ITEM(newargs, i, val); PyTuple_SET_ITEM(newargs, i, val);
} }
Py_DECREF(args); Py_DECREF(args);
args = newargs; args = *p_args = newargs;
} }
} }
#else #else
if (nargs >= 2 && nargs <= 3) { if (nargs >= 2 && nargs <= 3) {
if (!PyArg_UnpackTuple(args, "OSError", 2, 3, if (!PyArg_UnpackTuple(args, "OSError", 2, 3,
&myerrno, &strerror, &filename)) myerrno, strerror, filename))
goto error; return -1;
} }
#endif #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); return 0;
if (!self) }
goto error;
self->dict = NULL; static int
self->traceback = self->cause = self->context = NULL; oserror_init(PyOSErrorObject *self, PyObject **p_args,
self->written = -1; 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 */ /* self->filename will remain Py_None otherwise */
if (filename && filename != Py_None) { if (filename && filename != Py_None) {
if ((PyObject *) type == PyExc_BlockingIOError && if (Py_TYPE(self) == (PyTypeObject *) PyExc_BlockingIOError &&
PyNumber_Check(filename)) { PyNumber_Check(filename)) {
/* BlockingIOError's 3rd argument can be the number of /* BlockingIOError's 3rd argument can be the number of
* characters written. * characters written.
*/ */
self->written = PyNumber_AsSsize_t(filename, PyExc_ValueError); self->written = PyNumber_AsSsize_t(filename, PyExc_ValueError);
if (self->written == -1 && PyErr_Occurred()) if (self->written == -1 && PyErr_Occurred())
goto error; return -1;
} }
else { else {
Py_INCREF(filename); Py_INCREF(filename);
...@@ -687,20 +680,15 @@ OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds) ...@@ -687,20 +680,15 @@ OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
if (nargs >= 2 && nargs <= 3) { if (nargs >= 2 && nargs <= 3) {
/* filename is removed from the args tuple (for compatibility /* filename is removed from the args tuple (for compatibility
purposes, see test_exceptions.py) */ purposes, see test_exceptions.py) */
subslice = PyTuple_GetSlice(args, 0, 2); PyObject *subslice = PyTuple_GetSlice(args, 0, 2);
if (!subslice) if (!subslice)
goto error; return -1;
Py_DECREF(args); /* replacing args */ Py_DECREF(args); /* replacing args */
args = subslice; *p_args = args = subslice;
} }
} }
} }
/* Steals the reference to args */
self->args = args;
args = NULL;
Py_XINCREF(myerrno); Py_XINCREF(myerrno);
self->myerrno = myerrno; self->myerrno = myerrno;
...@@ -712,6 +700,90 @@ OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds) ...@@ -712,6 +700,90 @@ OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
self->winerror = winerror; self->winerror = winerror;
#endif #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; return (PyObject *) self;
error: error:
...@@ -721,10 +793,40 @@ error: ...@@ -721,10 +793,40 @@ error:
} }
static int 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; return 0;
error:
Py_XDECREF(args);
return -1;
} }
static int static int
......
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