]> granicus.if.org Git - python/commitdiff
Issue #20517: Functions in the os module that accept two filenames
authorLarry Hastings <larry@hastings.org>
Mon, 10 Feb 2014 06:05:19 +0000 (22:05 -0800)
committerLarry Hastings <larry@hastings.org>
Mon, 10 Feb 2014 06:05:19 +0000 (22:05 -0800)
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
Doc/data/refcounts.dat
Doc/library/exceptions.rst
Include/pyerrors.h
Lib/test/test_exceptions.py
Lib/test/test_posix.py
Misc/NEWS
Modules/posixmodule.c
Objects/exceptions.c
Python/errors.c

index d4065e08c9038a1b099c86eba7ad5f3b30d35bd8..070dd1782b24e4219821b7bd8c9bea234e52a36f 100644 (file)
@@ -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
index 814c3b043793b7b8e35889b8d95c6692435dbf63..2585a48620f816529ea851069625088b14628789 100644 (file)
@@ -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:
index 704abbbf2b823f28b6b91ac0809d7925711be91b..c2017cc1d3095757467573456672497d2407c902 100644 (file)
@@ -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
index 3e92bb68b05534e95050fabb3487889e5796394c..f9959699dc20bf194dbbb07a464600e8898e2d3a 100644 (file)
@@ -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 */
index 63e1da33a553a5ad5aa6f8b4deda82086c4e926e..62a2e324b1ca545a1374d1f7ae4288f6433dbed7 100644 (file)
@@ -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',
index 4e50cbef946a464738017bb6fa09a5e90b13d0f8..009f29da32e874d501865bd824bb8dace27822bb 100644 (file)
@@ -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):
index 8bd0dd39da5f7c45f8c9af7327d51662bb849048..b5bc34fa533d73030dde072826b28c815370fbfc 100644 (file)
--- 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
 -------------
 
index 60d0099910dd1b57cf21f69a2e3945a1cef59984..71abc9871d2143d3a0b6eb35179aec49ae1d35cd 100644 (file)
@@ -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
index 2531ead50fbda1294e623f88ad071d17c2fbea62..11dcaec2775e612b745ee52e696f77a00ff67125 100644 (file)
@@ -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")},
index 90dc729fd1d1f8cb9b95d9f353905a28939bf54b..0057e5eb36ff39a9ace1243fd60ef6194662a9c9 100644 (file)
@@ -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,
+                                                                 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);
+                                                                 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,10 +753,29 @@ PyObject *PyErr_SetFromWindowsErrWithUnicodeFilename(
     PyObject *name = filename ?
                      PyUnicode_FromUnicode(filename, wcslen(filename)) :
              NULL;
-    PyObject *result = PyErr_SetExcFromWindowsErrWithFilenameObject(
+    PyObject *result = PyErr_SetExcFromWindowsErrWithFilenameObjects(
+                                                  PyExc_OSError,
+                                                  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);
+                                                  ierr, name, name2);
     Py_XDECREF(name);
+    Py_XDECREF(name2);
     return result;
 }
 #endif /* MS_WINDOWS */