Fix OSError.__init__ and OSError.__new__ so that each of them can be
overriden and take additional arguments (followup to issue #12555).
This commit is contained in:
parent
d73a9acb63
commit
e0e2735f41
|
@ -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__)
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue