Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e0e2735

Browse files
committed
Fix OSError.__init__ and OSError.__new__ so that each of them can be
overriden and take additional arguments (followup to issue #12555).
1 parent d73a9ac commit e0e2735

3 files changed

Lines changed: 217 additions & 62 deletions

File tree

Lib/test/test_pep3151.py

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,23 @@
1212
class SubOSError(OSError):
1313
pass
1414

15+
class SubOSErrorWithInit(OSError):
16+
def __init__(self, message, bar):
17+
self.bar = bar
18+
super().__init__(message)
19+
20+
class SubOSErrorWithNew(OSError):
21+
def __new__(cls, message, baz):
22+
self = super().__new__(cls, message)
23+
self.baz = baz
24+
return self
25+
26+
class SubOSErrorCombinedInitFirst(SubOSErrorWithInit, SubOSErrorWithNew):
27+
pass
28+
29+
class SubOSErrorCombinedNewFirst(SubOSErrorWithNew, SubOSErrorWithInit):
30+
pass
31+
1532

1633
class HierarchyTest(unittest.TestCase):
1734

@@ -74,11 +91,6 @@ def test_errno_mapping(self):
7491
e = OSError(errcode, "Some message")
7592
self.assertIs(type(e), OSError)
7693

77-
def test_OSError_subclass_mapping(self):
78-
# When constructing an OSError subclass, errno mapping isn't done
79-
e = SubOSError(EEXIST, "Bad file descriptor")
80-
self.assertIs(type(e), SubOSError)
81-
8294
def test_try_except(self):
8395
filename = "some_hopefully_non_existing_file"
8496

@@ -144,6 +156,44 @@ def test_blockingioerror(self):
144156
# XXX VMSError not tested
145157

146158

159+
class ExplicitSubclassingTest(unittest.TestCase):
160+
161+
def test_errno_mapping(self):
162+
# When constructing an OSError subclass, errno mapping isn't done
163+
e = SubOSError(EEXIST, "Bad file descriptor")
164+
self.assertIs(type(e), SubOSError)
165+
166+
def test_init_overriden(self):
167+
e = SubOSErrorWithInit("some message", "baz")
168+
self.assertEqual(e.bar, "baz")
169+
self.assertEqual(e.args, ("some message",))
170+
171+
def test_init_kwdargs(self):
172+
e = SubOSErrorWithInit("some message", bar="baz")
173+
self.assertEqual(e.bar, "baz")
174+
self.assertEqual(e.args, ("some message",))
175+
176+
def test_new_overriden(self):
177+
e = SubOSErrorWithNew("some message", "baz")
178+
self.assertEqual(e.baz, "baz")
179+
self.assertEqual(e.args, ("some message",))
180+
181+
def test_new_kwdargs(self):
182+
e = SubOSErrorWithNew("some message", baz="baz")
183+
self.assertEqual(e.baz, "baz")
184+
self.assertEqual(e.args, ("some message",))
185+
186+
def test_init_new_overriden(self):
187+
e = SubOSErrorCombinedInitFirst("some message", "baz")
188+
self.assertEqual(e.bar, "baz")
189+
self.assertEqual(e.baz, "baz")
190+
self.assertEqual(e.args, ("some message",))
191+
e = SubOSErrorCombinedNewFirst("some message", "baz")
192+
self.assertEqual(e.bar, "baz")
193+
self.assertEqual(e.baz, "baz")
194+
self.assertEqual(e.args, ("some message",))
195+
196+
147197
def test_main():
148198
support.run_unittest(__name__)
149199

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ What's New in Python 3.3 Alpha 1?
1010
Core and Builtins
1111
-----------------
1212

13+
- Fix OSError.__init__ and OSError.__new__ so that each of them can be
14+
overriden and take additional arguments (followup to issue #12555).
15+
1316
- Fix the fix for issue #12149: it was incorrect, although it had the side
1417
effect of appearing to resolve the issue. Thanks to Mark Shannon for
1518
noticing.

Objects/exceptions.c

Lines changed: 159 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ BaseException_init(PyBaseExceptionObject *self, PyObject *args, PyObject *kwds)
5858
if (!_PyArg_NoKeywords(Py_TYPE(self)->tp_name, kwds))
5959
return -1;
6060

61-
Py_DECREF(self->args);
61+
Py_XDECREF(self->args);
6262
self->args = args;
6363
Py_INCREF(self->args);
6464

@@ -587,37 +587,34 @@ SimpleExtendsException(PyExc_Exception, ImportError,
587587
* when it was supplied.
588588
*/
589589

590-
static PyObject *
591-
OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
592-
{
593-
PyOSErrorObject *self = NULL;
594-
Py_ssize_t nargs;
595-
596-
PyObject *myerrno = NULL, *strerror = NULL, *filename = NULL;
597-
PyObject *subslice = NULL;
590+
/* This function doesn't cleanup on error, the caller should */
591+
static int
592+
oserror_parse_args(PyObject **p_args,
593+
PyObject **myerrno, PyObject **strerror,
594+
PyObject **filename
598595
#ifdef MS_WINDOWS
599-
PyObject *winerror = NULL;
600-
long winerrcode = 0;
596+
, PyObject **winerror
601597
#endif
598+
)
599+
{
600+
Py_ssize_t nargs;
601+
PyObject *args = *p_args;
602602

603-
if (!_PyArg_NoKeywords(type->tp_name, kwds))
604-
return NULL;
605-
Py_INCREF(args);
606603
nargs = PyTuple_GET_SIZE(args);
607604

608605
#ifdef MS_WINDOWS
609606
if (nargs >= 2 && nargs <= 4) {
610607
if (!PyArg_UnpackTuple(args, "OSError", 2, 4,
611-
&myerrno, &strerror, &filename, &winerror))
612-
goto error;
613-
if (winerror && PyLong_Check(winerror)) {
614-
long errcode;
608+
myerrno, strerror, filename, winerror))
609+
return -1;
610+
if (*winerror && PyLong_Check(*winerror)) {
611+
long errcode, winerrcode;
615612
PyObject *newargs;
616613
Py_ssize_t i;
617614

618-
winerrcode = PyLong_AsLong(winerror);
615+
winerrcode = PyLong_AsLong(*winerror);
619616
if (winerrcode == -1 && PyErr_Occurred())
620-
goto error;
617+
return -1;
621618
/* Set errno to the corresponding POSIX errno (overriding
622619
first argument). Windows Socket error codes (>= 10000)
623620
have the same value as their POSIX counterparts.
@@ -626,59 +623,55 @@ OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
626623
errcode = winerror_to_errno(winerrcode);
627624
else
628625
errcode = winerrcode;
629-
myerrno = PyLong_FromLong(errcode);
630-
if (!myerrno)
631-
goto error;
626+
*myerrno = PyLong_FromLong(errcode);
627+
if (!*myerrno)
628+
return -1;
632629
newargs = PyTuple_New(nargs);
633630
if (!newargs)
634-
goto error;
635-
PyTuple_SET_ITEM(newargs, 0, myerrno);
631+
return -1;
632+
PyTuple_SET_ITEM(newargs, 0, *myerrno);
636633
for (i = 1; i < nargs; i++) {
637634
PyObject *val = PyTuple_GET_ITEM(args, i);
638635
Py_INCREF(val);
639636
PyTuple_SET_ITEM(newargs, i, val);
640637
}
641638
Py_DECREF(args);
642-
args = newargs;
639+
args = *p_args = newargs;
643640
}
644641
}
645642
#else
646643
if (nargs >= 2 && nargs <= 3) {
647644
if (!PyArg_UnpackTuple(args, "OSError", 2, 3,
648-
&myerrno, &strerror, &filename))
649-
goto error;
645+
myerrno, strerror, filename))
646+
return -1;
650647
}
651648
#endif
652-
if (myerrno && PyLong_Check(myerrno) &&
653-
errnomap && (PyObject *) type == PyExc_OSError) {
654-
PyObject *newtype;
655-
newtype = PyDict_GetItem(errnomap, myerrno);
656-
if (newtype) {
657-
assert(PyType_Check(newtype));
658-
type = (PyTypeObject *) newtype;
659-
}
660-
else if (PyErr_Occurred())
661-
goto error;
662-
}
663649

664-
self = (PyOSErrorObject *) type->tp_alloc(type, 0);
665-
if (!self)
666-
goto error;
650+
return 0;
651+
}
667652

668-
self->dict = NULL;
669-
self->traceback = self->cause = self->context = NULL;
670-
self->written = -1;
653+
static int
654+
oserror_init(PyOSErrorObject *self, PyObject **p_args,
655+
PyObject *myerrno, PyObject *strerror,
656+
PyObject *filename
657+
#ifdef MS_WINDOWS
658+
, PyObject *winerror
659+
#endif
660+
)
661+
{
662+
PyObject *args = *p_args;
663+
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
671664

672665
/* self->filename will remain Py_None otherwise */
673666
if (filename && filename != Py_None) {
674-
if ((PyObject *) type == PyExc_BlockingIOError &&
667+
if (Py_TYPE(self) == (PyTypeObject *) PyExc_BlockingIOError &&
675668
PyNumber_Check(filename)) {
676669
/* BlockingIOError's 3rd argument can be the number of
677670
* characters written.
678671
*/
679672
self->written = PyNumber_AsSsize_t(filename, PyExc_ValueError);
680673
if (self->written == -1 && PyErr_Occurred())
681-
goto error;
674+
return -1;
682675
}
683676
else {
684677
Py_INCREF(filename);
@@ -687,20 +680,15 @@ OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
687680
if (nargs >= 2 && nargs <= 3) {
688681
/* filename is removed from the args tuple (for compatibility
689682
purposes, see test_exceptions.py) */
690-
subslice = PyTuple_GetSlice(args, 0, 2);
683+
PyObject *subslice = PyTuple_GetSlice(args, 0, 2);
691684
if (!subslice)
692-
goto error;
685+
return -1;
693686

694687
Py_DECREF(args); /* replacing args */
695-
args = subslice;
688+
*p_args = args = subslice;
696689
}
697690
}
698691
}
699-
700-
/* Steals the reference to args */
701-
self->args = args;
702-
args = NULL;
703-
704692
Py_XINCREF(myerrno);
705693
self->myerrno = myerrno;
706694

@@ -712,6 +700,90 @@ OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
712700
self->winerror = winerror;
713701
#endif
714702

703+
/* Steals the reference to args */
704+
self->args = args;
705+
args = NULL;
706+
707+
return 0;
708+
}
709+
710+
static PyObject *
711+
OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
712+
static int
713+
OSError_init(PyOSErrorObject *self, PyObject *args, PyObject *kwds);
714+
715+
static int
716+
oserror_use_init(PyTypeObject *type)
717+
{
718+
/* When __init__ is defined in a OSError subclass, we want any
719+
extraneous argument to __new__ to be ignored. The only reasonable
720+
solution, given __new__ takes a variable number of arguments,
721+
is to defer arg parsing and initialization to __init__.
722+
723+
But when __new__ is overriden as well, it should call our __new__
724+
with the right arguments.
725+
726+
(see http://bugs.python.org/issue12555#msg148829 )
727+
*/
728+
if (type->tp_init != (initproc) OSError_init &&
729+
type->tp_new == (newfunc) OSError_new) {
730+
assert((PyObject *) type != PyExc_OSError);
731+
return 1;
732+
}
733+
return 0;
734+
}
735+
736+
static PyObject *
737+
OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
738+
{
739+
PyOSErrorObject *self = NULL;
740+
PyObject *myerrno = NULL, *strerror = NULL, *filename = NULL;
741+
#ifdef MS_WINDOWS
742+
PyObject *winerror = NULL;
743+
#endif
744+
745+
if (!oserror_use_init(type)) {
746+
if (!_PyArg_NoKeywords(type->tp_name, kwds))
747+
return NULL;
748+
749+
Py_INCREF(args);
750+
if (oserror_parse_args(&args, &myerrno, &strerror, &filename
751+
#ifdef MS_WINDOWS
752+
, &winerror
753+
#endif
754+
))
755+
goto error;
756+
757+
if (myerrno && PyLong_Check(myerrno) &&
758+
errnomap && (PyObject *) type == PyExc_OSError) {
759+
PyObject *newtype;
760+
newtype = PyDict_GetItem(errnomap, myerrno);
761+
if (newtype) {
762+
assert(PyType_Check(newtype));
763+
type = (PyTypeObject *) newtype;
764+
}
765+
else if (PyErr_Occurred())
766+
goto error;
767+
}
768+
}
769+
770+
self = (PyOSErrorObject *) type->tp_alloc(type, 0);
771+
if (!self)
772+
goto error;
773+
774+
self->dict = NULL;
775+
self->traceback = self->cause = self->context = NULL;
776+
self->written = -1;
777+
778+
if (!oserror_use_init(type)) {
779+
if (oserror_init(self, &args, myerrno, strerror, filename
780+
#ifdef MS_WINDOWS
781+
, winerror
782+
#endif
783+
))
784+
goto error;
785+
}
786+
715787
return (PyObject *) self;
716788

717789
error:
@@ -721,10 +793,40 @@ OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
721793
}
722794

723795
static int
724-
OSError_init(PySyntaxErrorObject *self, PyObject *args, PyObject *kwds)
796+
OSError_init(PyOSErrorObject *self, PyObject *args, PyObject *kwds)
725797
{
726-
/* Everything already done in OSError_new */
798+
PyObject *myerrno = NULL, *strerror = NULL, *filename = NULL;
799+
#ifdef MS_WINDOWS
800+
PyObject *winerror = NULL;
801+
#endif
802+
803+
if (!oserror_use_init(Py_TYPE(self)))
804+
/* Everything already done in OSError_new */
805+
return 0;
806+
807+
if (!_PyArg_NoKeywords(Py_TYPE(self)->tp_name, kwds))
808+
return -1;
809+
810+
Py_INCREF(args);
811+
if (oserror_parse_args(&args, &myerrno, &strerror, &filename
812+
#ifdef MS_WINDOWS
813+
, &winerror
814+
#endif
815+
))
816+
goto error;
817+
818+
if (oserror_init(self, &args, myerrno, strerror, filename
819+
#ifdef MS_WINDOWS
820+
, winerror
821+
#endif
822+
))
823+
goto error;
824+
727825
return 0;
826+
827+
error:
828+
Py_XDECREF(args);
829+
return -1;
728830
}
729831

730832
static int

0 commit comments

Comments
 (0)