mirror of
https://github.com/python/cpython.git
synced 2025-12-04 08:34:25 +00:00
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
3 changed files with 217 additions and 62 deletions
|
|
@ -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):
|
||||||
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):
|
||||||
# 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?
|
||||||
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)
|
||||||
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,
|
||||||
* 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)
|
||||||
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)
|
||||||
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)
|
||||||
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:
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue