8000 Issue #15478: Raising an OSError doesn't decode or encode the filenam… · python/cpython@292c835 · GitHub
[go: up one dir, main page]

Skip to content

Commit 292c835

Browse files
committed
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).
1 parent 76df43d commit 292c835

File tree

5 files changed

+199
-108
lines changed

5 files changed

+199
-108
lines changed

Doc/library/exceptions.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,12 @@ The following exceptions are the exceptions that are usually raised.
246246
:exc:`VMSError`, :exc:`socket.error`, :exc:`select.error` and
247247
:exc:`mmap.error` have been merged into :exc:`OSError`.
248248

249+
.. versionchanged:: 3.4
250+
251+
The :attr:`filename` attribute is now the original file name passed to
252+
the function, instead of the name encoded to or decoded from the
253+
filesystem encoding.
254+
249255

250256
.. exception:: OverflowError
251257

Lib/test/support.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,17 @@ def _is_ipv6_enabled():
647647
# the byte 0xff. Skip some unicode filename tests.
648648
pass
649649

650+
# TESTFN_UNDECODABLE is a filename (bytes type) that should *not* be able to be
651+
# decoded from the filesystem encoding (in strict mode). It can be None if we
652+
# cannot generate such filename.
653+
TESTFN_UNDECODABLE = None
654+
for name in (b'abc\xff', b'\xe7w\xf0'):
655+
try:
656+
os.fsdecode(name)
657+
except UnicodeDecodeErorr:
658+
TESTFN_UNDECODABLE = name
659+
break
660+
650661
# Save the initial cwd
651662
SAVEDCWD = os.getcwd()
652663

Lib/test/test_os.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2046,6 +2046,76 @@ def test_stty_match(self):
20462046
self.assertEqual(expected, actual)
20472047

20482048

2049+
class OSErrorTests(unittest.TestCase):
2050+
def setUp(self):
2051+
class Str(str):
2052+
pass
2053+
2054+
self.filenames = []
2055+
if support.TESTFN_UNENCODABLE is not None:
2056+
decoded = support.TESTFN_UNENCODABLE
2057+
else:
2058+
decoded = support.TESTFN
2059+
self.filenames.append(decoded)
2060+
self.filenames.append(Str(decoded))
2061+
if support.TESTFN_UNDECODABLE is not None:
2062+
encoded = support.TESTFN_UNDECODABLE
2063+
else:
2064+
encoded = os.fsencode(support.TESTFN)
2065+
self.filenames.append(encoded)
2066+
self.filenames.append(memoryview(encoded))
2067+
2068+
def test_oserror_filename(self):
2069+
funcs = [
2070+
(os.chdir,),
2071+
(os.chmod, 0o777),
2072+
(os.chown, 0, 0),
2073+
(os.lchown, 0, 0),
2074+
(os.listdir,),
2075+
(os.lstat,),
2076+
(os.open, os.O_RDONLY),
2077+
(os.rename, "dst"),
2078+
(os.replace, "dst"),
2079+
(os.rmdir,),
2080+
(os.stat,),
2081+
(os.truncate, 0),
2082+
(os.unlink,),
2083+
]
2084+
if sys.platform == "win32":
2085+
funcs.extend((
2086+
(os._getfullpathname,),
2087+
(os._isdir,),
2088+
))
2089+
if hasattr(os, "chflags"):
2090+
funcs.extend((
2091+
(os.chflags, 0),
2092+
(os.lchflags, 0),
2093+
))
2094+
if hasattr(os, "chroot"):
2095+
funcs.append((os.chroot,))
2096+
if hasattr(os, "link"):
2097+
funcs.append((os.link, "dst"))
2098+
if hasattr(os, "listxattr"):
2099+
funcs.extend((
2100+
(os.listxattr,),
2101+
(os 6D40 .getxattr, "user.test"),
2102+
(os.setxattr, "user.test", b'user'),
2103+
(os.removexattr, "user.test"),
2104+
))
2105+
if hasattr(os, "lchmod"):
2106+
funcs.append((os.lchmod, 0o777))
2107+
if hasattr(os, "readlink"):
2108+
funcs.append((os.readlink,))
2109+
2110+
for func, *func_args in funcs:
2111+
for name in self.filenames:
2112+
try:
2113+
func(name, *func_args)
2114+
except FileNotFoundError as err:
2115+
self.assertIs(err.filename, name)
2116+
else:
2117+
self.fail("No exception thrown by {}".format(func))
2118+
20492119
@support.reap_threads
20502120
def test_main():
20512121
support.run_unittest(
@@ -2074,6 +2144,7 @@ def test_main():
20742144
ExtendedAttributeTests,
20752145
Win32DeprecatedBytesAPI,
20762146
TermsizeTests,
2147+
OSErrorTests,
20772148
)
20782149

20792150
if __name__ == "__main__":

Modules/_io/fileio.c

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -391,12 +391,7 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
391391

392392
fd_is_own = 1;
393393
if (self->fd < 0) {
394-
#ifdef MS_WINDOWS
395-
if (widename != NULL)
396-
PyErr_SetFromErrnoWithFilenameObject(PyExc_IOError, nameobj);
397-
else
398-
#endif
399-
PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
394+
PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj);
400395
goto error;
401396
}
402397
}

0 commit comments

Comments
 (0)
0