Commit 6c6efb95 authored by Victor Stinner's avatar Victor Stinner

Issue #15478: Raising an OSError doesn't decode or encode the filename anymore

Pass the original filename argument to OSError constructor, instead of trying
to encode it to or decode it from the filesystem encoding. This change avoids
an additionnal UnicodeDecodeError on Windows if the filename cannot be decoded
from the filesystem encoding (ANSI code page).
parent dcb2a6b3
......@@ -246,6 +246,12 @@ The following exceptions are the exceptions that are usually raised.
:exc:`VMSError`, :exc:`socket.error`, :exc:`select.error` and
:exc:`mmap.error` have been merged into :exc:`OSError`.
.. versionchanged:: 3.4
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.
.. exception:: OverflowError
......
......@@ -647,6 +647,17 @@ elif sys.platform != 'darwin':
# the byte 0xff. Skip some unicode filename tests.
pass
# TESTFN_UNDECODABLE is a filename (bytes type) that should *not* be able to be
# decoded from the filesystem encoding (in strict mode). It can be None if we
# cannot generate such filename.
TESTFN_UNDECODABLE = None
for name in (b'abc\xff', b'\xe7w\xf0'):
try:
os.fsdecode(name)
except UnicodeDecodeErorr:
TESTFN_UNDECODABLE = name
break
# Save the initial cwd
SAVEDCWD = os.getcwd()
......
......@@ -2046,6 +2046,76 @@ class TermsizeTests(unittest.TestCase):
self.assertEqual(expected, actual)
class OSErrorTests(unittest.TestCase):
def setUp(self):
class Str(str):
pass
self.filenames = []
if support.TESTFN_UNENCODABLE is not None:
decoded = support.TESTFN_UNENCODABLE
else:
decoded = support.TESTFN
self.filenames.append(decoded)
self.filenames.append(Str(decoded))
if support.TESTFN_UNDECODABLE is not None:
encoded = support.TESTFN_UNDECODABLE
else:
encoded = os.fsencode(support.TESTFN)
self.filenames.append(encoded)
self.filenames.append(memoryview(encoded))
def test_oserror_filename(self):
funcs = [
(os.chdir,),
(os.chmod, 0o777),
(os.chown, 0, 0),
(os.lchown, 0, 0),
(os.listdir,),
(os.lstat,),
(os.open, os.O_RDONLY),
(os.rename, "dst"),
(os.replace, "dst"),
(os.rmdir,),
(os.stat,),
(os.truncate, 0),
(os.unlink,),
]
if sys.platform == "win32":
funcs.extend((
(os._getfullpathname,),
(os._isdir,),
))
if hasattr(os, "chflags"):
funcs.extend((
(os.chflags, 0),
(os.lchflags, 0),
))
if hasattr(os, "chroot"):
funcs.append((os.chroot,))
if hasattr(os, "link"):
funcs.append((os.link, "dst"))
if hasattr(os, "listxattr"):
funcs.extend((
(os.listxattr,),
(os.getxattr, "user.test"),
(os.setxattr, "user.test", b'user'),
(os.removexattr, "user.test"),
))
if hasattr(os, "lchmod"):
funcs.append((os.lchmod, 0o777))
if hasattr(os, "readlink"):
funcs.append((os.readlink,))
for func, *func_args in funcs:
for name in self.filenames:
try:
func(name, *func_args)
except FileNotFoundError as err:
self.assertIs(err.filename, name)
else:
self.fail("No exception thrown by {}".format(func))
@support.reap_threads
def test_main():
support.run_unittest(
......@@ -2074,6 +2144,7 @@ def test_main():
ExtendedAttributeTests,
Win32DeprecatedBytesAPI,
TermsizeTests,
OSErrorTests,
)
if __name__ == "__main__":
......
......@@ -391,12 +391,7 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
fd_is_own = 1;
if (self->fd < 0) {
#ifdef MS_WINDOWS
if (widename != NULL)
PyErr_SetFromErrnoWithFilenameObject(PyExc_IOError, nameobj);
else
#endif
PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj);
goto error;
}
}
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment