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

Skip to content

Commit b082731

Browse files
committed
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.
1 parent dc62b7e commit b082731

10 files changed

Lines changed: 380 additions & 73 deletions

File tree

Doc/c-api/exceptions.rst

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -241,13 +241,30 @@ in various ways. There is a separate error indicator for each thread.
241241
exception instance.
242242
243243
244+
.. c:function:: PyObject* PyErr_SetFromErrnoWithFilenameObjects(PyObject *type, PyObject *filenameObject, PyObject *filenameObject2)
245+
246+
Similar to :c:func:`PyErr_SetFromErrnoWithFilenameObject`, but takes a second
247+
filename object, for raising errors when a function that takes two filenames
248+
fails.
249+
250+
.. versionadded:: 3.4
251+
252+
244253
.. c:function:: PyObject* PyErr_SetFromErrnoWithFilename(PyObject *type, const char *filename)
245254
246255
Similar to :c:func:`PyErr_SetFromErrnoWithFilenameObject`, but the filename
247256
is given as a C string. *filename* is decoded from the filesystem encoding
248257
(:func:`os.fsdecode`).
249258
250259
260+
.. c:function:: PyObject* PyErr_SetFromErrnoWithFilenames(PyObject *type, const char *filename, const char *filename2)
261+
262+
Similar to :c:func:`PyErr_SetFromErrnoWithFilename`, but accepts a
263+
second filename.
264+
265+
.. versionadded:: 3.4
266+
267+
251268
.. c:function:: PyObject* PyErr_SetFromWindowsErr(int ierr)
252269
253270
This is a convenience function to raise :exc:`WindowsError`. If called with
@@ -266,33 +283,52 @@ in various ways. There is a separate error indicator for each thread.
266283
specifying the exception type to be raised. Availability: Windows.
267284
268285
269-
.. c:function:: PyObject* PyErr_SetFromWindowsErrWithFilenameObject(int ierr, PyObject *filenameObject)
270-
271-
Similar to :c:func:`PyErr_SetFromWindowsErr`, with the additional behavior
272-
that if *filenameObject* is not *NULL*, it is passed to the constructor of
273-
:exc:`WindowsError` as a third parameter. Availability: Windows.
274-
275-
276286
.. c:function:: PyObject* PyErr_SetFromWindowsErrWithFilename(int ierr, const char *filename)
277287
278288
Similar to :c:func:`PyErr_SetFromWindowsErrWithFilenameObject`, but the
279289
filename is given as a C string. *filename* is decoded from the filesystem
280290
encoding (:func:`os.fsdecode`). Availability: Windows.
281291
282292
293+
.. c:function:: PyObject* PyErr_SetFromWindowsErrWithFilenames(int ierr, const char *filename, const char *filename2)
294+
295+
Similar to :c:func:`PyErr_SetFromWindowsErrWithFilename`, but accepts
296+
a second filename. Availability: Windows.
297+
298+
.. versionadded:: 3.4
299+
300+
283301
.. c:function:: PyObject* PyErr_SetExcFromWindowsErrWithFilenameObject(PyObject *type, int ierr, PyObject *filename)
284302
285303
Similar to :c:func:`PyErr_SetFromWindowsErrWithFilenameObject`, with an
286304
additional parameter specifying the exception type to be raised.
287305
Availability: Windows.
288306
289307
308+
.. c:function:: PyObject* PyErr_SetExcFromWindowsErrWithFilenameObjects(PyObject *type, int ierr, PyObject *filename, PyObject *filename2)
309+
310+
Similar to :c:func:`PyErr_SetExcFromWindowsErrWithFilenameObject`,
311+
but accepts a second filename object.
312+
Availability: Windows.
313+
314+
.. versionadded:: 3.4
315+
316+
290317
.. c:function:: PyObject* PyErr_SetExcFromWindowsErrWithFilename(PyObject *type, int ierr, const char *filename)
291318
292319
Similar to :c:func:`PyErr_SetFromWindowsErrWithFilename`, with an additional
293320
parameter specifying the exception type to be raised. Availability: Windows.
294321
295322
323+
.. c:function:: PyObject* PyErr_SetExcFromWindowsErrWithFilenames(PyObject *type, int ierr, const char *filename, const char *filename2)
324+
325+
Similar to :c:func:`PyErr_SetExcFromWindowsErrWithFilename`,
326+
but accepts a second filename object.
327+
Availability: Windows.
328+
329+
.. versionadded:: 3.4
330+
331+
296332
.. c:function:: PyObject* PyErr_SetImportError(PyObject *msg, PyObject *name, PyObject *path)
297333
298334
This is a convenience function to raise :exc:`ImportError`. *msg* will be

Doc/data/refcounts.dat

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
# reference to the item argument!
3030

3131
# The parameter names are as they appear in the API manual, not the source
32-
# code.
32+
# code.
3333

3434
PyBool_FromLong:PyObject*::+1:
3535
PyBool_FromLong:long:v:0:
@@ -317,20 +317,36 @@ PyErr_SetExcFromWindowsErrWithFilename:PyObject*:type:0:
317317
PyErr_SetExcFromWindowsErrWithFilename:int:ierr::
318318
PyErr_SetExcFromWindowsErrWithFilename:const char*:filename::
319319

320+
PyErr_SetExcFromWindowsErrWithFilenames:PyObject*::null:
321+
PyErr_SetExcFromWindowsErrWithFilenames:PyObject*:type:0:
322+
PyErr_SetExcFromWindowsErrWithFilenames:int:ierr::
323+
PyErr_SetExcFromWindowsErrWithFilenames:const char*:filename::
324+
PyErr_SetExcFromWindowsErrWithFilenames:const char*:filename2::
325+
320326
PyErr_SetFromErrno:PyObject*::null:
321327
PyErr_SetFromErrno:PyObject*:type:0:
322328

323329
PyErr_SetFromErrnoWithFilename:PyObject*::null:
324330
PyErr_SetFromErrnoWithFilename:PyObject*:type:0:
325331
PyErr_SetFromErrnoWithFilename:const char*:filename::
326332

333+
PyErr_SetFromErrnoWithFilenames:PyObject*::null:
334+
PyErr_SetFromErrnoWithFilenames:PyObject*:type:0:
335+
PyErr_SetFromErrnoWithFilenames:const char*:filename::
336+
PyErr_SetFromErrnoWithFilenames:const char*:filename2::
337+
327338
PyErr_SetFromWindowsErr:PyObject*::null:
328339
PyErr_SetFromWindowsErr:int:ierr::
329340

330341
PyErr_SetFromWindowsErrWithFilename:PyObject*::null:
331342
PyErr_SetFromWindowsErrWithFilename:int:ierr::
332343
PyErr_SetFromWindowsErrWithFilename:const char*:filename::
333344

345+
PyErr_SetFromWindowsErrWithFilenames:PyObject*::null:
346+
PyErr_SetFromWindowsErrWithFilenames:int:ierr::
347+
PyErr_SetFromWindowsErrWithFilenames:const char*:filename::
348+
PyErr_SetFromWindowsErrWithFilenames:const char*:filename2::
349+
334350
PyErr_SetInterrupt:void:::
335351

336352
PyErr_SetNone:void:::
@@ -907,7 +923,7 @@ PyNumber_Xor:PyObject*::+1:
907923
PyNumber_Xor:PyObject*:o1:0:
908924
PyNumber_Xor:PyObject*:o2:0:
909925

910-
PyObject_AsFileDescriptor:int:::
926+
PyObject_AsFileDescriptor:int:::
911927
PyObject_AsFileDescriptor:PyObject*:o:0:
912928

913929
PyObject_Call:PyObject*::+1:

Doc/library/exceptions.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,11 @@ The following exceptions are the exceptions that are usually raised.
253253
For exceptions that involve a file system path (such as :func:`open` or
254254
:func:`os.unlink`), the exception instance will contain an additional
255255
attribute, :attr:`filename`, which is the file name passed to the function.
256+
For functions that involve two file system paths (such as
257+
:func:`os.rename`), the exception instance will contain a second
258+
:attr:`filename2` attribute corresponding to the second file name passed
259+
to the function.
260+
256261

257262
.. versionchanged:: 3.3
258263
:exc:`EnvironmentError`, :exc:`IOError`, :exc:`WindowsError`,
@@ -263,7 +268,7 @@ The following exceptions are the exceptions that are usually raised.
263268

264269
The :attr:`filename` attribute is now the original file name passed to
265270
the function, instead of the name encoded to or decoded from the
266-
filesystem encoding.
271+
filesystem encoding. Also, the :attr:`filename2` attribute was added.
267272

268273

269274
.. exception:: OverflowError

Include/pyerrors.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ typedef struct {
5353
PyObject *myerrno;
5454
PyObject *strerror;
5555
PyObject *filename;
56+
PyObject *filename2;
5657
#ifdef MS_WINDOWS
5758
PyObject *winerror;
5859
#endif
@@ -225,13 +226,23 @@ PyAPI_FUNC(PyObject *) PyErr_NoMemory(void);
225226
PyAPI_FUNC(PyObject *) PyErr_SetFromErrno(PyObject *);
226227
PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithFilenameObject(
227228
PyObject *, PyObject *);
229+
PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithFilenameObjects(
230+
PyObject *, PyObject *, PyObject *);
228231
PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithFilename(
229232
PyObject *exc,
230233
const char *filename /* decoded from the filesystem encoding */
231234
);
235+
PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithFilenames(
236+
PyObject *exc,
237+
/* decoded from the filesystem encoding */
238+
const char *filename,
239+
const char *filename2
240+
);
232241
#if defined(MS_WINDOWS) && !defined(Py_LIMITED_API)
233242
PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithUnicodeFilename(
234243
PyObject *, const Py_UNICODE *);
244+
PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithUnicodeFilenames(
245+
PyObject *, const Py_UNICODE *, const Py_UNICODE *);
235246
#endif /* MS_WINDOWS */
236247

237248
PyAPI_FUNC(PyObject *) PyErr_Format(
@@ -245,22 +256,41 @@ PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithFilename(
245256
int ierr,
246257
const char *filename /* decoded from the filesystem encoding */
247258
);
259+
PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithFilenames(
260+
int ierr,
261+
/* decoded from the filesystem encoding */
262+
const char *filename,
263+
const char *filename2
264+
);
248265
#ifndef Py_LIMITED_API
249266
/* XXX redeclare to use WSTRING */
250267
PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithUnicodeFilename(
251268
int, const Py_UNICODE *);
269+
PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithUnicodeFilenames(
270+
int, const Py_UNICODE *, const Py_UNICODE *);
252271
#endif
253272
PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErr(int);
254273
PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithFilenameObject(
255274
PyObject *,int, PyObject *);
275+
PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithFilenameObjects(
276+
PyObject *,int, PyObject *, PyObject *);
256277
PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithFilename(
257278
PyObject *exc,
258279
int ierr,
259280
const char *filename /* decoded from the filesystem encoding */
260281
);
282+
PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithFilenames(
283+
PyObject *exc,
284+
int ierr,
285+
/* decoded from the filesystem encoding */
286+
const char *filename,
287+
const char *filename2
288+
);
261289
#ifndef Py_LIMITED_API
262290
PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithUnicodeFilename(
263291
PyObject *,int, const Py_UNICODE *);
292+
PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithUnicodeFilenames(
293+
PyObject *,int, const Py_UNICODE *, const Py_UNICODE *);
264294
#endif
265295
PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErr(PyObject *, int);
266296
#endif /* MS_WINDOWS */

Lib/test/test_exceptions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,8 @@ def testAttributes(self):
266266
(OSError, ('foo', 'bar', 'baz'),
267267
{'args' : ('foo', 'bar'), 'filename' : 'baz',
268268
'errno' : 'foo', 'strerror' : 'bar'}),
269-
(OSError, ('foo', 'bar', 'baz', 'quux'),
270-
{'args' : ('foo', 'bar', 'baz', 'quux')}),
269+
(OSError, ('foo', 'bar', 'baz', None, 'quux'),
270+
{'args' : ('foo', 'bar'), 'filename' : 'baz', 'filename2': 'quux'}),
271271
(OSError, ('errnoStr', 'strErrorStr', 'filenameStr'),
272272
{'args' : ('errnoStr', 'strErrorStr'),
273273
'strerror' : 'strErrorStr', 'errno' : 'errnoStr',

Lib/test/test_posix.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,6 +1121,23 @@ def test_fs_holes(self):
11211121
# http://lists.freebsd.org/pipermail/freebsd-amd64/2012-January/014332.html
11221122
raise unittest.SkipTest("OSError raised!")
11231123

1124+
def test_path_error2(self):
1125+
"""
1126+
Test functions that call path_error2(), providing two filenames in their exceptions.
1127+
"""
1128+
for name in ("rename", "replace", "link", "symlink"):
1129+
function = getattr(os, name, None)
1130+
1131+
if function:
1132+
for dst in ("noodly2", support.TESTFN):
1133+
try:
1134+
function('doesnotexistfilename', dst)
1135+
except OSError as e:
1136+
self.assertIn("'doesnotexistfilename' -> '{}'".format(dst), str(e))
1137+
break
1138+
else:
1139+
self.fail("No valid path_error2() test for os." + name)
1140+
11241141
class PosixGroupsTester(unittest.TestCase):
11251142

11261143
def setUp(self):

Misc/NEWS

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ Core and Builtins
3232
Library
3333
-------
3434

35+
- Issue #20517: Functions in the os module that accept two filenames
36+
now register both filenames in the exception on failure.
37+
3538
- Issue #20563: The ipaddress module API is now considered stable.
3639

3740
- Issue #14983: email.generator now always adds a line end after each MIME
@@ -236,6 +239,15 @@ Build
236239

237240
- Issue #20465: Update SQLite shipped with OS X installer to 3.8.3.
238241

242+
C-API
243+
-----
244+
245+
- Issue #20517: Added new functions allowing OSError exceptions to reference
246+
two filenames instead of one: PyErr_SetFromErrnoWithFilenameObjects(),
247+
PyErr_SetFromErrnoWithFilenames(), PyErr_SetFromWindowsErrWithFilenames(),
248+
PyErr_SetExcFromWindowsErrWithFilenameObjects(), and
249+
PyErr_SetExcFromWindowsErrWithFilenames().
250+
239251
Documentation
240252
-------------
241253

Modules/posixmodule.c

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,6 +1313,19 @@ path_error(path_t *path)
13131313
}
13141314

13151315

1316+
static PyObject *
1317+
path_error2(path_t *path, path_t *path2)
1318+
{
1319+
#ifdef MS_WINDOWS
1320+
return PyErr_SetExcFromWindowsErrWithFilenameObjects(PyExc_OSError,
1321+
0, path->object, path2->object);
1322+
#else
1323+
return PyErr_SetFromErrnoWithFilenameObjects(PyExc_OSError,
1324+
path->object, path2->object);
1325+
#endif
1326+
}
1327+
1328+
13161329
/* POSIX generic methods */
13171330

13181331
static PyObject *
@@ -3518,7 +3531,7 @@ posix_link(PyObject *self, PyObject *args, PyObject *kwargs)
35183531
Py_END_ALLOW_THREADS
35193532

35203533
if (!result) {
3521-
return_value = path_error(&src);
3534+
return_value = path_error2(&src, &dst);
35223535
goto exit;
35233536
}
35243537
#else
@@ -3536,7 +3549,7 @@ posix_link(PyObject *self, PyObject *args, PyObject *kwargs)
35363549
Py_END_ALLOW_THREADS
35373550

35383551
if (result) {
3539-
return_value = path_error(&src);
3552+
return_value = path_error2(&src, &dst);
35403553
goto exit;
35413554
}
35423555
#endif
@@ -4284,7 +4297,7 @@ internal_rename(PyObject *args, PyObject *kwargs, int is_replace)
42844297
Py_END_ALLOW_THREADS
42854298

42864299
if (!result) {
4287-
return_value = path_error(&src);
4300+
return_value = path_error2(&src, &dst);
42884301
goto exit;
42894302
}
42904303

@@ -4299,7 +4312,7 @@ internal_rename(PyObject *args, PyObject *kwargs, int is_replace)
42994312
Py_END_ALLOW_THREADS
43004313

43014314
if (result) {
4302-
return_value = path_error(&src);
4315+
return_value = path_error2(&src, &dst);
43034316
goto exit;
43044317
}
43054318
#endif
@@ -7345,7 +7358,7 @@ posix_symlink(PyObject *self, PyObject *args, PyObject *kwargs)
73457358
Py_END_ALLOW_THREADS
73467359

73477360
if (!result) {
7348-
return_value = path_error(&src);
7361+
return_value = path_error2(&src, &dst);
73497362
goto exit;
73507363
}
73517364

@@ -7361,7 +7374,7 @@ posix_symlink(PyObject *self, PyObject *args, PyObject *kwargs)
73617374
Py_END_ALLOW_THREADS
73627375

73637376
if (result) {
7364-
return_value = path_error(&src);
7377+
return_value = path_error2(&src, &dst);
73657378
goto exit;
73667379
}
73677380
#endif

0 commit comments

Comments
 (0)