From b082731fbb413a7ff2412a447698fdd65015fd24 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 9 Feb 2014 22:05:19 -0800 Subject: [PATCH] Issue #20517: Functions in the os module that accept two filenames now register both filenames in the exception on failure. This required adding new C API functions allowing OSError exceptions to reference two filenames instead of one. --- Doc/c-api/exceptions.rst | 50 +++++++++-- Doc/data/refcounts.dat | 20 ++++- Doc/library/exceptions.rst | 7 +- Include/pyerrors.h | 30 +++++++ Lib/test/test_exceptions.py | 4 +- Lib/test/test_posix.py | 17 ++++ Misc/NEWS | 12 +++ Modules/posixmodule.c | 25 ++++-- Objects/exceptions.c | 119 ++++++++++++++++++------- Python/errors.c | 169 +++++++++++++++++++++++++++++++----- 10 files changed, 380 insertions(+), 73 deletions(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index d4065e08c90..070dd1782b2 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -241,6 +241,15 @@ in various ways. There is a separate error indicator for each thread. exception instance. +.. c:function:: PyObject* PyErr_SetFromErrnoWithFilenameObjects(PyObject *type, PyObject *filenameObject, PyObject *filenameObject2) + + Similar to :c:func:`PyErr_SetFromErrnoWithFilenameObject`, but takes a second + filename object, for raising errors when a function that takes two filenames + fails. + +.. versionadded:: 3.4 + + .. c:function:: PyObject* PyErr_SetFromErrnoWithFilename(PyObject *type, const char *filename) Similar to :c:func:`PyErr_SetFromErrnoWithFilenameObject`, but the filename @@ -248,6 +257,14 @@ in various ways. There is a separate error indicator for each thread. (:func:`os.fsdecode`). +.. c:function:: PyObject* PyErr_SetFromErrnoWithFilenames(PyObject *type, const char *filename, const char *filename2) + + Similar to :c:func:`PyErr_SetFromErrnoWithFilename`, but accepts a + second filename. + +.. versionadded:: 3.4 + + .. c:function:: PyObject* PyErr_SetFromWindowsErr(int ierr) This is a convenience function to raise :exc:`WindowsError`. If called with @@ -266,13 +283,6 @@ in various ways. There is a separate error indicator for each thread. specifying the exception type to be raised. Availability: Windows. -.. c:function:: PyObject* PyErr_SetFromWindowsErrWithFilenameObject(int ierr, PyObject *filenameObject) - - Similar to :c:func:`PyErr_SetFromWindowsErr`, with the additional behavior - that if *filenameObject* is not *NULL*, it is passed to the constructor of - :exc:`WindowsError` as a third parameter. Availability: Windows. - - .. c:function:: PyObject* PyErr_SetFromWindowsErrWithFilename(int ierr, const char *filename) Similar to :c:func:`PyErr_SetFromWindowsErrWithFilenameObject`, but the @@ -280,6 +290,14 @@ in various ways. There is a separate error indicator for each thread. encoding (:func:`os.fsdecode`). Availability: Windows. +.. c:function:: PyObject* PyErr_SetFromWindowsErrWithFilenames(int ierr, const char *filename, const char *filename2) + + Similar to :c:func:`PyErr_SetFromWindowsErrWithFilename`, but accepts + a second filename. Availability: Windows. + +.. versionadded:: 3.4 + + .. c:function:: PyObject* PyErr_SetExcFromWindowsErrWithFilenameObject(PyObject *type, int ierr, PyObject *filename) Similar to :c:func:`PyErr_SetFromWindowsErrWithFilenameObject`, with an @@ -287,12 +305,30 @@ in various ways. There is a separate error indicator for each thread. Availability: Windows. +.. c:function:: PyObject* PyErr_SetExcFromWindowsErrWithFilenameObjects(PyObject *type, int ierr, PyObject *filename, PyObject *filename2) + + Similar to :c:func:`PyErr_SetExcFromWindowsErrWithFilenameObject`, + but accepts a second filename object. + Availability: Windows. + +.. versionadded:: 3.4 + + .. c:function:: PyObject* PyErr_SetExcFromWindowsErrWithFilename(PyObject *type, int ierr, const char *filename) Similar to :c:func:`PyErr_SetFromWindowsErrWithFilename`, with an additional parameter specifying the exception type to be raised. Availability: Windows. +.. c:function:: PyObject* PyErr_SetExcFromWindowsErrWithFilenames(PyObject *type, int ierr, const char *filename, const char *filename2) + + Similar to :c:func:`PyErr_SetExcFromWindowsErrWithFilename`, + but accepts a second filename object. + Availability: Windows. + +.. versionadded:: 3.4 + + .. c:function:: PyObject* PyErr_SetImportError(PyObject *msg, PyObject *name, PyObject *path) This is a convenience function to raise :exc:`ImportError`. *msg* will be diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index 814c3b04379..2585a48620f 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -29,7 +29,7 @@ # reference to the item argument! # The parameter names are as they appear in the API manual, not the source -# code. +# code. PyBool_FromLong:PyObject*::+1: PyBool_FromLong:long:v:0: @@ -317,6 +317,12 @@ PyErr_SetExcFromWindowsErrWithFilename:PyObject*:type:0: PyErr_SetExcFromWindowsErrWithFilename:int:ierr:: PyErr_SetExcFromWindowsErrWithFilename:const char*:filename:: +PyErr_SetExcFromWindowsErrWithFilenames:PyObject*::null: +PyErr_SetExcFromWindowsErrWithFilenames:PyObject*:type:0: +PyErr_SetExcFromWindowsErrWithFilenames:int:ierr:: +PyErr_SetExcFromWindowsErrWithFilenames:const char*:filename:: +PyErr_SetExcFromWindowsErrWithFilenames:const char*:filename2:: + PyErr_SetFromErrno:PyObject*::null: PyErr_SetFromErrno:PyObject*:type:0: @@ -324,6 +330,11 @@ PyErr_SetFromErrnoWithFilename:PyObject*::null: PyErr_SetFromErrnoWithFilename:PyObject*:type:0: PyErr_SetFromErrnoWithFilename:const char*:filename:: +PyErr_SetFromErrnoWithFilenames:PyObject*::null: +PyErr_SetFromErrnoWithFilenames:PyObject*:type:0: +PyErr_SetFromErrnoWithFilenames:const char*:filename:: +PyErr_SetFromErrnoWithFilenames:const char*:filename2:: + PyErr_SetFromWindowsErr:PyObject*::null: PyErr_SetFromWindowsErr:int:ierr:: @@ -331,6 +342,11 @@ PyErr_SetFromWindowsErrWithFilename:PyObject*::null: PyErr_SetFromWindowsErrWithFilename:int:ierr:: PyErr_SetFromWindowsErrWithFilename:const char*:filename:: +PyErr_SetFromWindowsErrWithFilenames:PyObject*::null: +PyErr_SetFromWindowsErrWithFilenames:int:ierr:: +PyErr_SetFromWindowsErrWithFilenames:const char*:filename:: +PyErr_SetFromWindowsErrWithFilenames:const char*:filename2:: + PyErr_SetInterrupt:void::: PyErr_SetNone:void::: @@ -907,7 +923,7 @@ PyNumber_Xor:PyObject*::+1: PyNumber_Xor:PyObject*:o1:0: PyNumber_Xor:PyObject*:o2:0: -PyObject_AsFileDescriptor:int::: +PyObject_AsFileDescriptor:int::: PyObject_AsFileDescriptor:PyObject*:o:0: PyObject_Call:PyObject*::+1: diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst index 704abbbf2b8..c2017cc1d30 100644 --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -253,6 +253,11 @@ The following exceptions are the exceptions that are usually raised. For exceptions that involve a file system path (such as :func:`open` or :func:`os.unlink`), the exception instance will contain an additional attribute, :attr:`filename`, which is the file name passed to the function. + For functions that involve two file system paths (such as + :func:`os.rename`), the exception instance will contain a second + :attr:`filename2` attribute corresponding to the second file name passed + to the function. + .. versionchanged:: 3.3 :exc:`EnvironmentError`, :exc:`IOError`, :exc:`WindowsError`, @@ -263,7 +268,7 @@ The following exceptions are the exceptions that are usually raised. The :attr:`filename` attribute is now the original file name passed to the function, instead of the name encoded to or decoded from the - filesystem encoding. + filesystem encoding. Also, the :attr:`filename2` attribute was added. .. exception:: OverflowError diff --git a/Include/pyerrors.h b/Include/pyerrors.h index 3e92bb68b05..f9959699dc2 100644 --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -53,6 +53,7 @@ typedef struct { PyObject *myerrno; PyObject *strerror; PyObject *filename; + PyObject *filename2; #ifdef MS_WINDOWS PyObject *winerror; #endif @@ -225,13 +226,23 @@ PyAPI_FUNC(PyObject *) PyErr_NoMemory(void); PyAPI_FUNC(PyObject *) PyErr_SetFromErrno(PyObject *); PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithFilenameObject( PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithFilenameObjects( + PyObject *, PyObject *, PyObject *); PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithFilename( PyObject *exc, const char *filename /* decoded from the filesystem encoding */ ); +PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithFilenames( + PyObject *exc, + /* decoded from the filesystem encoding */ + const char *filename, + const char *filename2 + ); #if defined(MS_WINDOWS) && !defined(Py_LIMITED_API) PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithUnicodeFilename( PyObject *, const Py_UNICODE *); +PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithUnicodeFilenames( + PyObject *, const Py_UNICODE *, const Py_UNICODE *); #endif /* MS_WINDOWS */ PyAPI_FUNC(PyObject *) PyErr_Format( @@ -245,22 +256,41 @@ PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithFilename( int ierr, const char *filename /* decoded from the filesystem encoding */ ); +PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithFilenames( + int ierr, + /* decoded from the filesystem encoding */ + const char *filename, + const char *filename2 + ); #ifndef Py_LIMITED_API /* XXX redeclare to use WSTRING */ PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithUnicodeFilename( int, const Py_UNICODE *); +PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithUnicodeFilenames( + int, const Py_UNICODE *, const Py_UNICODE *); #endif PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErr(int); PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithFilenameObject( PyObject *,int, PyObject *); +PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithFilenameObjects( + PyObject *,int, PyObject *, PyObject *); PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithFilename( PyObject *exc, int ierr, const char *filename /* decoded from the filesystem encoding */ ); +PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithFilenames( + PyObject *exc, + int ierr, + /* decoded from the filesystem encoding */ + const char *filename, + const char *filename2 + ); #ifndef Py_LIMITED_API PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithUnicodeFilename( PyObject *,int, const Py_UNICODE *); +PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithUnicodeFilenames( + PyObject *,int, const Py_UNICODE *, const Py_UNICODE *); #endif PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErr(PyObject *, int); #endif /* MS_WINDOWS */ diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 63e1da33a55..62a2e324b1c 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -266,8 +266,8 @@ class ExceptionTests(unittest.TestCase): (OSError, ('foo', 'bar', 'baz'), {'args' : ('foo', 'bar'), 'filename' : 'baz', 'errno' : 'foo', 'strerror' : 'bar'}), - (OSError, ('foo', 'bar', 'baz', 'quux'), - {'args' : ('foo', 'bar', 'baz', 'quux')}), + (OSError, ('foo', 'bar', 'baz', None, 'quux'), + {'args' : ('foo', 'bar'), 'filename' : 'baz', 'filename2': 'quux'}), (OSError, ('errnoStr', 'strErrorStr', 'filenameStr'), {'args' : ('errnoStr', 'strErrorStr'), 'strerror' : 'strErrorStr', 'errno' : 'errnoStr', diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index 4e50cbef946..009f29da32e 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -1121,6 +1121,23 @@ class PosixTester(unittest.TestCase): # http://lists.freebsd.org/pipermail/freebsd-amd64/2012-January/014332.html raise unittest.SkipTest("OSError raised!") + def test_path_error2(self): + """ + Test functions that call path_error2(), providing two filenames in their exceptions. + """ + for name in ("rename", "replace", "link", "symlink"): + function = getattr(os, name, None) + + if function: + for dst in ("noodly2", support.TESTFN): + try: + function('doesnotexistfilename', dst) + except OSError as e: + self.assertIn("'doesnotexistfilename' -> '{}'".format(dst), str(e)) + break + else: + self.fail("No valid path_error2() test for os." + name) + class PosixGroupsTester(unittest.TestCase): def setUp(self): diff --git a/Misc/NEWS b/Misc/NEWS index 8bd0dd39da5..b5bc34fa533 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -32,6 +32,9 @@ Core and Builtins Library ------- +- Issue #20517: Functions in the os module that accept two filenames + now register both filenames in the exception on failure. + - Issue #20563: The ipaddress module API is now considered stable. - Issue #14983: email.generator now always adds a line end after each MIME @@ -236,6 +239,15 @@ Build - Issue #20465: Update SQLite shipped with OS X installer to 3.8.3. +C-API +----- + +- Issue #20517: Added new functions allowing OSError exceptions to reference + two filenames instead of one: PyErr_SetFromErrnoWithFilenameObjects(), + PyErr_SetFromErrnoWithFilenames(), PyErr_SetFromWindowsErrWithFilenames(), + PyErr_SetExcFromWindowsErrWithFilenameObjects(), and + PyErr_SetExcFromWindowsErrWithFilenames(). + Documentation ------------- diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 60d0099910d..71abc9871d2 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -1313,6 +1313,19 @@ path_error(path_t *path) } +static PyObject * +path_error2(path_t *path, path_t *path2) +{ +#ifdef MS_WINDOWS + return PyErr_SetExcFromWindowsErrWithFilenameObjects(PyExc_OSError, + 0, path->object, path2->object); +#else + return PyErr_SetFromErrnoWithFilenameObjects(PyExc_OSError, + path->object, path2->object); +#endif +} + + /* POSIX generic methods */ static PyObject * @@ -3518,7 +3531,7 @@ posix_link(PyObject *self, PyObject *args, PyObject *kwargs) Py_END_ALLOW_THREADS if (!result) { - return_value = path_error(&src); + return_value = path_error2(&src, &dst); goto exit; } #else @@ -3536,7 +3549,7 @@ posix_link(PyObject *self, PyObject *args, PyObject *kwargs) Py_END_ALLOW_THREADS if (result) { - return_value = path_error(&src); + return_value = path_error2(&src, &dst); goto exit; } #endif @@ -4284,7 +4297,7 @@ internal_rename(PyObject *args, PyObject *kwargs, int is_replace) Py_END_ALLOW_THREADS if (!result) { - return_value = path_error(&src); + return_value = path_error2(&src, &dst); goto exit; } @@ -4299,7 +4312,7 @@ internal_rename(PyObject *args, PyObject *kwargs, int is_replace) Py_END_ALLOW_THREADS if (result) { - return_value = path_error(&src); + return_value = path_error2(&src, &dst); goto exit; } #endif @@ -7345,7 +7358,7 @@ posix_symlink(PyObject *self, PyObject *args, PyObject *kwargs) Py_END_ALLOW_THREADS if (!result) { - return_value = path_error(&src); + return_value = path_error2(&src, &dst); goto exit; } @@ -7361,7 +7374,7 @@ posix_symlink(PyObject *self, PyObject *args, PyObject *kwargs) Py_END_ALLOW_THREADS if (result) { - return_value = path_error(&src); + return_value = path_error2(&src, &dst); goto exit; } #endif diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 2531ead50fb..11dcaec2775 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -724,13 +724,17 @@ ComplexExtendsException(PyExc_Exception, ImportError, * we hack args so that it only contains two items. This also * means we need our own __str__() which prints out the filename * when it was supplied. + * + * (If a function has two filenames, such as rename(), symlink(), + * or copy(), PyErr_SetFromErrnoWithFilenames() is called, which + * allows passing in a second filename.) */ /* This function doesn't cleanup on error, the caller should */ static int oserror_parse_args(PyObject **p_args, PyObject **myerrno, PyObject **strerror, - PyObject **filename + PyObject **filename, PyObject **filename2 #ifdef MS_WINDOWS , PyObject **winerror #endif @@ -738,14 +742,23 @@ oserror_parse_args(PyObject **p_args, { Py_ssize_t nargs; PyObject *args = *p_args; +#ifndef MS_WINDOWS + /* + * ignored on non-Windows platforms, + * but parsed so OSError has a consistent signature + */ + PyObject *_winerror = NULL; + PyObject **winerror = &_winerror; +#endif /* MS_WINDOWS */ nargs = PyTuple_GET_SIZE(args); -#ifdef MS_WINDOWS - if (nargs >= 2 && nargs <= 4) { - if (!PyArg_UnpackTuple(args, "OSError", 2, 4, - myerrno, strerror, filename, winerror)) + if (nargs >= 2 && nargs <= 5) { + if (!PyArg_UnpackTuple(args, "OSError", 2, 5, + myerrno, strerror, + filename, winerror, filename2)) return -1; +#ifdef MS_WINDOWS if (*winerror && PyLong_Check(*winerror)) { long errcode, winerrcode; PyObject *newargs; @@ -777,14 +790,8 @@ oserror_parse_args(PyObject **p_args, Py_DECREF(args); args = *p_args = newargs; } +#endif /* MS_WINDOWS */ } -#else - if (nargs >= 2 && nargs <= 3) { - if (!PyArg_UnpackTuple(args, "OSError", 2, 3, - myerrno, strerror, filename)) - return -1; - } -#endif return 0; } @@ -792,7 +799,7 @@ oserror_parse_args(PyObject **p_args, static int oserror_init(PyOSErrorObject *self, PyObject **p_args, PyObject *myerrno, PyObject *strerror, - PyObject *filename + PyObject *filename, PyObject *filename2 #ifdef MS_WINDOWS , PyObject *winerror #endif @@ -816,9 +823,14 @@ oserror_init(PyOSErrorObject *self, PyObject **p_args, Py_INCREF(filename); self->filename = filename; - if (nargs >= 2 && nargs <= 3) { - /* filename is removed from the args tuple (for compatibility - purposes, see test_exceptions.py) */ + if (filename2 && filename2 != Py_None) { + Py_INCREF(filename2); + self->filename2 = filename2; + } + + if (nargs >= 2 && nargs <= 5) { + /* filename, filename2, and winerror are removed from the args tuple + (for compatibility purposes, see test_exceptions.py) */ PyObject *subslice = PyTuple_GetSlice(args, 0, 2); if (!subslice) return -1; @@ -877,7 +889,8 @@ static PyObject * OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyOSErrorObject *self = NULL; - PyObject *myerrno = NULL, *strerror = NULL, *filename = NULL; + PyObject *myerrno = NULL, *strerror = NULL; + PyObject *filename = NULL, *filename2 = NULL; #ifdef MS_WINDOWS PyObject *winerror = NULL; #endif @@ -888,7 +901,8 @@ OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (!_PyArg_NoKeywords(type->tp_name, kwds)) goto error; - if (oserror_parse_args(&args, &myerrno, &strerror, &filename + if (oserror_parse_args(&args, &myerrno, &strerror, + &filename, &filename2 #ifdef MS_WINDOWS , &winerror #endif @@ -917,7 +931,7 @@ OSError_new(PyTypeObject *type, PyObject *args, PyObject *kwds) self->written = -1; if (!oserror_use_init(type)) { - if (oserror_init(self, &args, myerrno, strerror, filename + if (oserror_init(self, &args, myerrno, strerror, filename, filename2 #ifdef MS_WINDOWS , winerror #endif @@ -942,7 +956,8 @@ error: static int OSError_init(PyOSErrorObject *self, PyObject *args, PyObject *kwds) { - PyObject *myerrno = NULL, *strerror = NULL, *filename = NULL; + PyObject *myerrno = NULL, *strerror = NULL; + PyObject *filename = NULL, *filename2 = NULL; #ifdef MS_WINDOWS PyObject *winerror = NULL; #endif @@ -955,14 +970,14 @@ OSError_init(PyOSErrorObject *self, PyObject *args, PyObject *kwds) return -1; Py_INCREF(args); - if (oserror_parse_args(&args, &myerrno, &strerror, &filename + if (oserror_parse_args(&args, &myerrno, &strerror, &filename, &filename2 #ifdef MS_WINDOWS , &winerror #endif )) goto error; - if (oserror_init(self, &args, myerrno, strerror, filename + if (oserror_init(self, &args, myerrno, strerror, filename, filename2 #ifdef MS_WINDOWS , winerror #endif @@ -982,6 +997,7 @@ OSError_clear(PyOSErrorObject *self) Py_CLEAR(self->myerrno); Py_CLEAR(self->strerror); Py_CLEAR(self->filename); + Py_CLEAR(self->filename2); #ifdef MS_WINDOWS Py_CLEAR(self->winerror); #endif @@ -1003,6 +1019,7 @@ OSError_traverse(PyOSErrorObject *self, visitproc visit, Py_VISIT(self->myerrno); Py_VISIT(self->strerror); Py_VISIT(self->filename); + Py_VISIT(self->filename2); #ifdef MS_WINDOWS Py_VISIT(self->winerror); #endif @@ -1012,23 +1029,42 @@ OSError_traverse(PyOSErrorObject *self, visitproc visit, static PyObject * OSError_str(PyOSErrorObject *self) { +#define OR_NONE(x) ((x)?(x):Py_None) #ifdef MS_WINDOWS /* If available, winerror has the priority over myerrno */ - if (self->winerror && self->filename) - return PyUnicode_FromFormat("[WinError %S] %S: %R", - self->winerror ? self->winerror: Py_None, - self->strerror ? self->strerror: Py_None, - self->filename); + if (self->winerror && self->filename) { + if (self->filename2) { + return PyUnicode_FromFormat("[WinError %S] %S: %R -> %R", + OR_NONE(self->winerror), + OR_NONE(self->strerror), + self->filename, + self->filename2); + } else { + return PyUnicode_FromFormat("[WinError %S] %S: %R", + OR_NONE(self->winerror), + OR_NONE(self->strerror), + self->filename); + } + } if (self->winerror && self->strerror) return PyUnicode_FromFormat("[WinError %S] %S", self->winerror ? self->winerror: Py_None, self->strerror ? self->strerror: Py_None); #endif - if (self->filename) - return PyUnicode_FromFormat("[Errno %S] %S: %R", - self->myerrno ? self->myerrno: Py_None, - self->strerror ? self->strerror: Py_None, - self->filename); + if (self->filename) { + if (self->filename2) { + return PyUnicode_FromFormat("[Errno %S] %S: %R -> %R", + OR_NONE(self->myerrno), + OR_NONE(self->strerror), + self->filename, + self->filename2); + } else { + return PyUnicode_FromFormat("[Errno %S] %S: %R", + OR_NONE(self->myerrno), + OR_NONE(self->strerror), + self->filename); + } + } if (self->myerrno && self->strerror) return PyUnicode_FromFormat("[Errno %S] %S", self->myerrno ? self->myerrno: Py_None, @@ -1045,7 +1081,8 @@ OSError_reduce(PyOSErrorObject *self) /* self->args is only the first two real arguments if there was a * file name given to OSError. */ if (PyTuple_GET_SIZE(args) == 2 && self->filename) { - args = PyTuple_New(3); + Py_ssize_t size = self->filename2 ? 5 : 3; + args = PyTuple_New(size); if (!args) return NULL; @@ -1059,6 +1096,20 @@ OSError_reduce(PyOSErrorObject *self) Py_INCREF(self->filename); PyTuple_SET_ITEM(args, 2, self->filename); + + if (self->filename2) { + /* + * This tuple is essentially used as OSError(*args). + * So, to recreate filename2, we need to pass in + * winerror as well. + */ + Py_INCREF(Py_None); + PyTuple_SET_ITEM(args, 3, Py_None); + + /* filename2 */ + Py_INCREF(self->filename2); + PyTuple_SET_ITEM(args, 4, self->filename2); + } } else Py_INCREF(args); @@ -1098,6 +1149,8 @@ static PyMemberDef OSError_members[] = { PyDoc_STR("exception strerror")}, {"filename", T_OBJECT, offsetof(PyOSErrorObject, filename), 0, PyDoc_STR("exception filename")}, + {"filename2", T_OBJECT, offsetof(PyOSErrorObject, filename2), 0, + PyDoc_STR("second exception filename")}, #ifdef MS_WINDOWS {"winerror", T_OBJECT, offsetof(PyOSErrorObject, winerror), 0, PyDoc_STR("Win32 exception code")}, diff --git a/Python/errors.c b/Python/errors.c index 90dc729fd1d..0057e5eb36f 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -409,6 +409,12 @@ PyErr_NoMemory(void) PyObject * PyErr_SetFromErrnoWithFilenameObject(PyObject *exc, PyObject *filenameObject) +{ + return PyErr_SetFromErrnoWithFilenameObjects(exc, filenameObject, NULL); +} + +PyObject * +PyErr_SetFromErrnoWithFilenameObjects(PyObject *exc, PyObject *filenameObject, PyObject *filenameObject2) { PyObject *message; PyObject *v, *args; @@ -480,10 +486,15 @@ PyErr_SetFromErrnoWithFilenameObject(PyObject *exc, PyObject *filenameObject) return NULL; } - if (filenameObject != NULL) - args = Py_BuildValue("(iOO)", i, message, filenameObject); - else + if (filenameObject != NULL) { + if (filenameObject2 != NULL) + args = Py_BuildValue("(iOOiO)", i, message, filenameObject, 0, filenameObject2); + else + args = Py_BuildValue("(iOO)", i, message, filenameObject); + } else { + assert(filenameObject2 == NULL); args = Py_BuildValue("(iO)", i, message); + } Py_DECREF(message); if (args != NULL) { @@ -500,16 +511,26 @@ PyErr_SetFromErrnoWithFilenameObject(PyObject *exc, PyObject *filenameObject) return NULL; } - PyObject * PyErr_SetFromErrnoWithFilename(PyObject *exc, const char *filename) { PyObject *name = filename ? PyUnicode_DecodeFSDefault(filename) : NULL; - PyObject *result = PyErr_SetFromErrnoWithFilenameObject(exc, name); + PyObject *result = PyErr_SetFromErrnoWithFilenameObjects(exc, name, NULL); Py_XDECREF(name); return result; } +PyObject * +PyErr_SetFromErrnoWithFilenames(PyObject *exc, const char *filename, const char *filename2) +{ + PyObject *name = filename ? PyUnicode_DecodeFSDefault(filename) : NULL; + PyObject *name2 = filename2 ? PyUnicode_DecodeFSDefault(filename2) : NULL; + PyObject *result = PyErr_SetFromErrnoWithFilenameObjects(exc, name, name2); + Py_XDECREF(name); + Py_XDECREF(name2); + return result; +} + #ifdef MS_WINDOWS PyObject * PyErr_SetFromErrnoWithUnicodeFilename(PyObject *exc, const Py_UNICODE *filename) @@ -517,16 +538,31 @@ PyErr_SetFromErrnoWithUnicodeFilename(PyObject *exc, const Py_UNICODE *filename) PyObject *name = filename ? PyUnicode_FromUnicode(filename, wcslen(filename)) : NULL; - PyObject *result = PyErr_SetFromErrnoWithFilenameObject(exc, name); + PyObject *result = PyErr_SetFromErrnoWithFilenameObjects(exc, name, NULL); Py_XDECREF(name); return result; } + +PyObject * +PyErr_SetFromErrnoWithUnicodeFilenames(PyObject *exc, const Py_UNICODE *filename, const Py_UNICODE *filename2) +{ + PyObject *name = filename ? + PyUnicode_FromUnicode(filename, wcslen(filename)) : + NULL; + PyObject *name2 = filename2 ? + PyUnicode_FromUnicode(filename2, wcslen(filename2)) : + NULL; + PyObject *result = PyErr_SetFromErrnoWithFilenameObjects(exc, name, name2); + Py_XDECREF(name); + Py_XDECREF(name2); + return result; +} #endif /* MS_WINDOWS */ PyObject * PyErr_SetFromErrno(PyObject *exc) { - return PyErr_SetFromErrnoWithFilenameObject(exc, NULL); + return PyErr_SetFromErrnoWithFilenameObjects(exc, NULL, NULL); } #ifdef MS_WINDOWS @@ -535,6 +571,16 @@ PyObject *PyErr_SetExcFromWindowsErrWithFilenameObject( PyObject *exc, int ierr, PyObject *filenameObject) +{ + return PyErr_SetExcFromWindowsErrWithFilenameObjects(exc, ierr, + filenameObject, NULL); +} + +PyObject *PyErr_SetExcFromWindowsErrWithFilenameObjects( + PyObject *exc, + int ierr, + PyObject *filenameObject, + PyObject *filenameObject2) { int len; WCHAR *s_buf = NULL; /* Free via LocalFree */ @@ -571,11 +617,15 @@ PyObject *PyErr_SetExcFromWindowsErrWithFilenameObject( return NULL; } - if (filenameObject == NULL) - filenameObject = Py_None; - /* This is the constructor signature for passing a Windows error code. + if (filenameObject == NULL) { + assert(filenameObject2 == NULL); + filenameObject = filenameObject2 = Py_None; + } + else if (filenameObject2 == NULL) + filenameObject2 = Py_None; + /* This is the constructor signature for OSError. The POSIX translation will be figured out by the constructor. */ - args = Py_BuildValue("(iOOi)", 0, message, filenameObject, err); + args = Py_BuildValue("(iOOiO)", 0, message, filenameObject, err, filenameObject2); Py_DECREF(message); if (args != NULL) { @@ -596,13 +646,31 @@ PyObject *PyErr_SetExcFromWindowsErrWithFilename( const char *filename) { PyObject *name = filename ? PyUnicode_DecodeFSDefault(filename) : NULL; - PyObject *ret = PyErr_SetExcFromWindowsErrWithFilenameObject(exc, + PyObject *ret = PyErr_SetExcFromWindowsErrWithFilenameObjects(exc, ierr, - name); + name, + NULL); Py_XDECREF(name); return ret; } +PyObject *PyErr_SetExcFromWindowsErrWithFilenames( + PyObject *exc, + int ierr, + const char *filename, + const char *filename2) +{ + PyObject *name = filename ? PyUnicode_DecodeFSDefault(filename) : NULL; + PyObject *name2 = filename2 ? PyUnicode_DecodeFSDefault(filename2) : NULL; + PyObject *ret = PyErr_SetExcFromWindowsErrWithFilenameObjects(exc, + ierr, + name, + name2); + Py_XDECREF(name); + Py_XDECREF(name2); + return ret; +} + PyObject *PyErr_SetExcFromWindowsErrWithUnicodeFilename( PyObject *exc, int ierr, @@ -611,31 +679,69 @@ PyObject *PyErr_SetExcFromWindowsErrWithUnicodeFilename( PyObject *name = filename ? PyUnicode_FromUnicode(filename, wcslen(filename)) : NULL; - PyObject *ret = PyErr_SetExcFromWindowsErrWithFilenameObject(exc, + PyObject *ret = PyErr_SetExcFromWindowsErrWithFilenameObjects(exc, ierr, - name); + name, + NULL); Py_XDECREF(name); return ret; } +PyObject *PyErr_SetExcFromWindowsErrWithUnicodeFilenames( + PyObject *exc, + int ierr, + const Py_UNICODE *filename, + const Py_UNICODE *filename2) +{ + PyObject *name = filename ? + PyUnicode_FromUnicode(filename, wcslen(filename)) : + NULL; + PyObject *name2 = filename2 ? + PyUnicode_FromUnicode(filename2, wcslen(filename2)) : + NULL; + PyObject *ret = PyErr_SetExcFromWindowsErrWithFilenameObjects(exc, + ierr, + name, + name2); + Py_XDECREF(name); + Py_XDECREF(name2); + return ret; +} + PyObject *PyErr_SetExcFromWindowsErr(PyObject *exc, int ierr) { - return PyErr_SetExcFromWindowsErrWithFilename(exc, ierr, NULL); + return PyErr_SetExcFromWindowsErrWithFilenames(exc, ierr, NULL, NULL); } PyObject *PyErr_SetFromWindowsErr(int ierr) { - return PyErr_SetExcFromWindowsErrWithFilename(PyExc_OSError, - ierr, NULL); + return PyErr_SetExcFromWindowsErrWithFilenames(PyExc_OSError, + ierr, NULL, NULL); } + +PyObject *PyErr_SetFromWindowsErrWithFilenames( + int ierr, + const char *filename, + const char *filename2) +{ + PyObject *name = filename ? PyUnicode_DecodeFSDefault(filename) : NULL; + PyObject *name2 = filename2 ? PyUnicode_DecodeFSDefault(filename2) : NULL; + PyObject *result = PyErr_SetExcFromWindowsErrWithFilenameObjects( + PyExc_OSError, + ierr, name, name2); + Py_XDECREF(name); + Py_XDECREF(name2); + return result; +} + PyObject *PyErr_SetFromWindowsErrWithFilename( int ierr, const char *filename) { PyObject *name = filename ? PyUnicode_DecodeFSDefault(filename) : NULL; - PyObject *result = PyErr_SetExcFromWindowsErrWithFilenameObject( + PyObject *result = PyErr_SetExcFromWindowsErrWithFilenameObjects( PyExc_OSError, - ierr, name); + ierr, name, NULL); Py_XDECREF(name); return result; } @@ -647,12 +753,31 @@ PyObject *PyErr_SetFromWindowsErrWithUnicodeFilename( PyObject *name = filename ? PyUnicode_FromUnicode(filename, wcslen(filename)) : NULL; - PyObject *result = PyErr_SetExcFromWindowsErrWithFilenameObject( + PyObject *result = PyErr_SetExcFromWindowsErrWithFilenameObjects( PyExc_OSError, - ierr, name); + ierr, name, NULL); Py_XDECREF(name); return result; } + +PyObject *PyErr_SetFromWindowsErrWithUnicodeFilenames( + int ierr, + const Py_UNICODE *filename, + const Py_UNICODE *filename2) +{ + PyObject *name = filename ? + PyUnicode_FromUnicode(filename, wcslen(filename)) : + NULL; + PyObject *name2 = filename2 ? + PyUnicode_FromUnicode(filename2, wcslen(filename2)) : + NULL; + PyObject *result = PyErr_SetExcFromWindowsErrWithFilenameObjects( + PyExc_OSError, + ierr, name, name2); + Py_XDECREF(name); + Py_XDECREF(name2); + return result; +} #endif /* MS_WINDOWS */ PyObject *