diff -r 083534cd7874 Lib/test/support.py --- a/Lib/test/support.py Thu Aug 02 17:50:06 2012 -0400 +++ b/Lib/test/support.py Fri Aug 03 14:19:56 2012 +0200 @@ -583,6 +583,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() diff -r 083534cd7874 Lib/test/test_os.py --- a/Lib/test/test_os.py Thu Aug 02 17:50:06 2012 -0400 +++ b/Lib/test/test_os.py Fri Aug 03 14:19:56 2012 +0200 @@ -2043,6 +2043,73 @@ class TermsizeTests(unittest.TestCase): self.assertEqual(expected, actual) +class OSErrorTests(unittest.TestCase): + def setUp(self): + self.filenames = [] + if support.TESTFN_UNENCODABLE is not None: + self.filenames.append(support.TESTFN_UNENCODABLE) + else: + self.filenames.append(support.TESTFN) + 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: + if isinstance(name, memoryview): + continue + 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( @@ -2071,6 +2138,7 @@ def test_main(): ExtendedAttributeTests, Win32DeprecatedBytesAPI, TermsizeTests, + OSErrorTests, ) if __name__ == "__main__": diff -r 083534cd7874 Modules/_io/fileio.c --- a/Modules/_io/fileio.c Thu Aug 02 17:50:06 2012 -0400 +++ b/Modules/_io/fileio.c Fri Aug 03 14:19:56 2012 +0200 @@ -391,12 +391,7 @@ fileio_init(PyObject *oself, PyObject *a 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; } } diff -r 083534cd7874 Modules/posixmodule.c --- a/Modules/posixmodule.c Thu Aug 02 17:50:06 2012 -0400 +++ b/Modules/posixmodule.c Fri Aug 03 14:19:56 2012 +0200 @@ -538,8 +538,8 @@ dir_fd_converter(PyObject *o, void *p) { * path_cleanup(). However it is safe to do so.) */ typedef struct { - char *function_name; - char *argument_name; + const char *function_name; + const char *argument_name; int nullable; int allow_fd; wchar_t *wide; @@ -1059,25 +1059,6 @@ posix_error(void) { return PyErr_SetFromErrno(PyExc_OSError); } -static PyObject * -posix_error_with_filename(char* name) -{ - return PyErr_SetFromErrnoWithFilename(PyExc_OSError, name); -} - - -static PyObject * -posix_error_with_allocated_filename(PyObject* name) -{ - PyObject *name_str, *rc; - name_str = PyUnicode_DecodeFSDefaultAndSize(PyBytes_AsString(name), - PyBytes_GET_SIZE(name)); - Py_DECREF(name); - rc = PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, - name_str); - Py_XDECREF(name_str); - return rc; -} #ifdef MS_WINDOWS static PyObject * @@ -1122,29 +1103,14 @@ win32_error_object(char* function, PyObj #endif /* MS_WINDOWS */ -/* - * Some functions return Win32 errors, others only ever use posix_error - * (this is for backwards compatibility with exceptions) - */ -static PyObject * -path_posix_error(char *function_name, path_t *path) -{ - if (path->narrow) - return posix_error_with_filename(path->narrow); - return posix_error(); -} - -static PyObject * -path_error(char *function_name, path_t *path) +static PyObject * +path_error(path_t *path) { #ifdef MS_WINDOWS - if (path->narrow) - return win32_error(function_name, path->narrow); - if (path->wide) - return win32_error_unicode(function_name, path->wide); - return win32_error(function_name, NULL); -#else - return path_posix_error(function_name, path); + return PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, + 0, path->object); +#else + return PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path->object); #endif } @@ -1247,21 +1213,25 @@ posix_fildes(PyObject *fdobj, int (*func } static PyObject * -posix_1str(PyObject *args, char *format, int (*func)(const char*)) -{ - PyObject *opath1 = NULL; - char *path1; +posix_1str(const char *func_name, PyObject *args, char *format, + int (*func)(const char*)) +{ + path_t path; int res; + memset(&path, 0, sizeof(path)); + path.function_name = func_name; if (!PyArg_ParseTuple(args, format, - PyUnicode_FSConverter, &opath1)) - return NULL; - path1 = PyBytes_AsString(opath1); + path_converter, &path)) + return NULL; Py_BEGIN_ALLOW_THREADS - res = (*func)(path1); + res = (*func)(path.narrow); Py_END_ALLOW_THREADS - if (res < 0) - return posix_error_with_allocated_filename(opath1); - Py_DECREF(opath1); + if (res < 0) { + path_error(&path); + path_cleanup(&path); + return NULL; + } + path_cleanup(&path); Py_INCREF(Py_None); return Py_None; } @@ -2277,8 +2247,9 @@ posix_do_stat(char *function_name, path_ result = STAT(path->narrow, &st); Py_END_ALLOW_THREADS - if (result != 0) - return path_error("stat", path); + if (result != 0) { + return path_error(path); + } return _pystat_fromstructstat(&st); } @@ -2309,6 +2280,7 @@ posix_stat(PyObject *self, PyObject *arg PyObject *return_value; memset(&path, 0, sizeof(path)); + path.function_name = "stat"; path.allow_fd = 1; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|$O&p:stat", keywords, path_converter, &path, @@ -2339,6 +2311,7 @@ posix_lstat(PyObject *self, PyObject *ar PyObject *return_value; memset(&path, 0, sizeof(path)); + path.function_name = "lstat"; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|$O&:lstat", keywords, path_converter, &path, #ifdef HAVE_FSTATAT @@ -2547,6 +2520,7 @@ posix_chdir(PyObject *self, PyObject *ar static char *keywords[] = {"path", NULL}; memset(&path, 0, sizeof(path)); + path.function_name = "chdir"; #ifdef HAVE_FCHDIR path.allow_fd = 1; #endif @@ -2575,7 +2549,7 @@ posix_chdir(PyObject *self, PyObject *ar Py_END_ALLOW_THREADS if (result) { - return_value = path_error("chdir", &path); + return_value = path_error(&path); goto exit; } @@ -2639,6 +2613,7 @@ posix_chmod(PyObject *self, PyObject *ar #endif memset(&path, 0, sizeof(path)); + path.function_name = "chmod"; #ifdef HAVE_FCHMOD path.allow_fd = 1; #endif @@ -2733,7 +2708,7 @@ posix_chmod(PyObject *self, PyObject *ar } else #endif - return_value = path_error("chmod", &path); + return_value = path_error(&path); goto exit; } #endif @@ -2777,20 +2752,23 @@ Equivalent to chmod(path, mode, follow_s static PyObject * posix_lchmod(PyObject *self, PyObject *args) { - PyObject *opath; - char *path; + path_t path; int i; int res; - if (!PyArg_ParseTuple(args, "O&i:lchmod", PyUnicode_FSConverter, - &opath, &i)) - return NULL; - path = PyBytes_AsString(opath); + memset(&path, 0, sizeof(path)); + path.function_name = "lchmod"; + if (!PyArg_ParseTuple(args, "O&i:lchmod", + path_converter, &path, &i)) + return NULL; Py_BEGIN_ALLOW_THREADS - res = lchmod(path, i); + res = lchmod(path.narrow, i); Py_END_ALLOW_THREADS - if (res < 0) - return posix_error_with_allocated_filename(opath); - Py_DECREF(opath); + if (res < 0) { + path_error(&path); + path_cleanup(&path); + return NULL; + } + path_cleanup(&path); Py_RETURN_NONE; } #endif /* HAVE_LCHMOD */ @@ -2818,6 +2796,7 @@ posix_chflags(PyObject *self, PyObject * static char *keywords[] = {"path", "flags", "follow_symlinks", NULL}; memset(&path, 0, sizeof(path)); + path.function_name = "chflags"; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&k|$i:chflags", keywords, path_converter, &path, &flags, &follow_symlinks)) @@ -2838,7 +2817,7 @@ posix_chflags(PyObject *self, PyObject * Py_END_ALLOW_THREADS if (result) { - return_value = path_posix_error("chflags", &path); + return_value = path_error(&path); goto exit; } @@ -2861,22 +2840,25 @@ Equivalent to chflags(path, flags, follo static PyObject * posix_lchflags(PyObject *self, PyObject *args) { - PyObject *opath; + path_t path; char *path; unsigned long flags; int res; + memset(&path, 0, sizeof(path)); + path.function_name = "lchflags"; if (!PyArg_ParseTuple(args, "O&k:lchflags", - PyUnicode_FSConverter, &opath, &flags)) - return NULL; - path = PyBytes_AsString(opath); + path_converter, &path, &flags)) + return NULL; Py_BEGIN_ALLOW_THREADS - res = lchflags(path, flags); + res = lchflags(path.narrow, flags); Py_END_ALLOW_THREADS - if (res < 0) - return posix_error_with_allocated_filename(opath); - Py_DECREF(opath); - Py_INCREF(Py_None); - return Py_None; + if (res < 0) { + path_error(&path); + path_cleanup(&path); + return NULL; + } + path_cleanup(&path); + Py_RETURN_NONE; } #endif /* HAVE_LCHFLAGS */ @@ -2888,7 +2870,7 @@ Change root directory to path."); static PyObject * posix_chroot(PyObject *self, PyObject *args) { - return posix_1str(args, "O&:chroot", chroot); + return posix_1str("chroot", args, "O&:chroot", chroot); } #endif @@ -2971,6 +2953,7 @@ posix_chown(PyObject *self, PyObject *ar "follow_symlinks", NULL}; memset(&path, 0, sizeof(path)); + path.function_name = "chown"; #ifdef HAVE_FCHOWN path.allow_fd = 1; #endif @@ -3029,7 +3012,7 @@ posix_chown(PyObject *self, PyObject *ar Py_END_ALLOW_THREADS if (result) { - return_value = path_posix_error("chown", &path); + return_value = path_error(&path); goto exit; } @@ -3075,21 +3058,24 @@ Equivalent to os.chown(path, uid, gid, f static PyObject * posix_lchown(PyObject *self, PyObject *args) { - PyObject *opath; - char *path; + path_t path; long uid, gid; int res; + memset(&path, 0, sizeof(path)); + path.function_name = "lchown"; if (!PyArg_ParseTuple(args, "O&ll:lchown", - PyUnicode_FSConverter, &opath, + path_converter, &path, &uid, &gid)) return NULL; - path = PyBytes_AsString(opath); Py_BEGIN_ALLOW_THREADS - res = lchown(path, (uid_t) uid, (gid_t) gid); + res = lchown(path.narrow, (uid_t) uid, (gid_t) gid); Py_END_ALLOW_THREADS - if (res < 0) - return posix_error_with_allocated_filename(opath); - Py_DECREF(opath); + if (res < 0) { + path_error(&path); + path_cleanup(&path); + return NULL; + } + path_cleanup(&path); Py_INCREF(Py_None); return Py_None; } @@ -3209,6 +3195,8 @@ posix_link(PyObject *self, PyObject *arg memset(&src, 0, sizeof(src)); memset(&dst, 0, sizeof(dst)); + src.function_name = "link"; + dst.function_name = "link"; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&|O&O&p:link", keywords, path_converter, &src, path_converter, &dst, @@ -3257,7 +3245,7 @@ posix_link(PyObject *self, PyObject *arg Py_END_ALLOW_THREADS if (result) { - return_value = path_error("link", &dst); + return_value = path_error(&src); goto exit; } #endif @@ -3325,6 +3313,7 @@ posix_listdir(PyObject *self, PyObject * #endif memset(&path, 0, sizeof(path)); + path.function_name = "listdir"; path.nullable = 1; #ifdef HAVE_FDOPENDIR path.allow_fd = 1; @@ -3505,7 +3494,7 @@ exit: if (rc != NO_ERROR) { errno = ENOENT; Py_DECREF(list); - list = posix_error_with_filename(path.narrow); + list = path_error(&path); goto exit; } @@ -3581,7 +3570,7 @@ exit: } if (dirp == NULL) { - list = path_error("listdir", &path); + list = path_error(&path); goto exit; } if ((list = PyList_New(0)) == NULL) { @@ -3597,7 +3586,7 @@ exit: break; } else { Py_DECREF(list); - list = path_error("listdir", &path); + list = path_error(&path); goto exit; } } @@ -3848,6 +3837,7 @@ posix_mkdir(PyObject *self, PyObject *ar int result; memset(&path, 0, sizeof(path)); + path.function_name = "mkdir"; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|i$O&:mkdir", keywords, path_converter, &path, &mode, #ifdef HAVE_MKDIRAT @@ -3884,7 +3874,7 @@ posix_mkdir(PyObject *self, PyObject *ar #endif Py_END_ALLOW_THREADS if (result < 0) { - return_value = path_error("mkdir", &path); + return_value = path_error(&path); goto exit; } #endif @@ -4002,6 +3992,8 @@ internal_rename(PyObject *args, PyObject memset(&src, 0, sizeof(src)); memset(&dst, 0, sizeof(dst)); + src.function_name = function_name; + dst.function_name = function_name; strcpy(format, "O&O&|$O&O&:"); strcat(format, function_name); if (!PyArg_ParseTupleAndKeywords(args, kwargs, format, keywords, @@ -4050,7 +4042,7 @@ internal_rename(PyObject *args, PyObject Py_END_ALLOW_THREADS if (result) { - return_value = path_error(function_name, &dst); + return_value = path_error(&src); goto exit; } #endif @@ -4114,6 +4106,7 @@ posix_rmdir(PyObject *self, PyObject *ar PyObject *return_value = NULL; memset(&path, 0, sizeof(path)); + path.function_name = "rmdir"; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|$O&:rmdir", keywords, path_converter, &path, #ifdef HAVE_UNLINKAT @@ -4142,7 +4135,7 @@ posix_rmdir(PyObject *self, PyObject *ar Py_END_ALLOW_THREADS if (result) { - return_value = path_error("rmdir", &path); + return_value = path_error(&path); goto exit; } @@ -4270,6 +4263,7 @@ posix_unlink(PyObject *self, PyObject *a PyObject *return_value = NULL; memset(&path, 0, sizeof(path)); + path.function_name = "unlink"; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|$O&:unlink", keywords, path_converter, &path, #ifdef HAVE_UNLINKAT @@ -4298,7 +4292,7 @@ posix_unlink(PyObject *self, PyObject *a Py_END_ALLOW_THREADS if (result) { - return_value = path_error("unlink", &path); + return_value = path_error(&path); goto exit; } @@ -4992,6 +4986,7 @@ posix_execve(PyObject *self, PyObject *a like posix.environ. */ memset(&path, 0, sizeof(path)); + path.function_name = "execve"; #ifdef HAVE_FEXECVE path.allow_fd = 1; #endif @@ -5031,7 +5026,7 @@ posix_execve(PyObject *self, PyObject *a /* If we get here it's definitely an error */ - path_posix_error("execve", &path); + path_error(&path); while (--envc >= 0) PyMem_DEL(envlist[envc]); @@ -7180,6 +7175,7 @@ posix_readlink(PyObject *self, PyObject static char *keywords[] = {"path", "dir_fd", NULL}; memset(&path, 0, sizeof(path)); + path.function_name = "readlink"; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|$O&:readlink", keywords, path_converter, &path, #ifdef HAVE_READLINKAT @@ -7200,7 +7196,7 @@ posix_readlink(PyObject *self, PyObject Py_END_ALLOW_THREADS if (length < 0) { - return_value = path_posix_error("readlink", &path); + return_value = path_error(&path); goto exit; } @@ -7270,8 +7266,10 @@ posix_symlink(PyObject *self, PyObject * #endif memset(&src, 0, sizeof(src)); + src.function_name = "symlink"; src.argument_name = "src"; memset(&dst, 0, sizeof(dst)); + dst.function_name = "symlink"; dst.argument_name = "dst"; #ifdef MS_WINDOWS @@ -7333,7 +7331,7 @@ posix_symlink(PyObject *self, PyObject * Py_END_ALLOW_THREADS if (result) { - return_value = path_error("symlink", &dst); + return_value = path_error(&dst); goto exit; } #endif @@ -7682,6 +7680,7 @@ posix_open(PyObject *self, PyObject *arg static char *keywords[] = {"path", "flags", "mode", "dir_fd", NULL}; memset(&path, 0, sizeof(path)); + path.function_name = "open"; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&i|i$O&:open", keywords, path_converter, &path, &flags, &mode, @@ -7714,7 +7713,7 @@ posix_open(PyObject *self, PyObject *arg return_value = posix_error(); else #endif - return_value = path_error("open", &path); + return_value = path_error(&path); goto exit; } @@ -8612,6 +8611,7 @@ posix_truncate(PyObject *self, PyObject static char *keywords[] = {"path", "length", NULL}; memset(&path, 0, sizeof(path)); + path.function_name = "truncate"; #ifdef HAVE_FTRUNCATE path.allow_fd = 1; #endif @@ -8629,7 +8629,7 @@ posix_truncate(PyObject *self, PyObject res = truncate(path.narrow, length); Py_END_ALLOW_THREADS if (res < 0) - result = path_posix_error("truncate", &path); + result = path_error(&path); else { Py_INCREF(Py_None); result = Py_None; @@ -9101,6 +9101,7 @@ posix_statvfs(PyObject *self, PyObject * struct statvfs st; memset(&path, 0, sizeof(path)); + path.function_name = "statvfs"; #ifdef HAVE_FSTATVFS path.allow_fd = 1; #endif @@ -9127,7 +9128,7 @@ posix_statvfs(PyObject *self, PyObject * Py_END_ALLOW_THREADS if (result) { - return_value = path_posix_error("statvfs", &path); + return_value = path_error(&path); goto exit; } @@ -9364,6 +9365,7 @@ posix_pathconf(PyObject *self, PyObject static char *keywords[] = {"path", "name", NULL}; memset(&path, 0, sizeof(path)); + path.function_name = "pathconf"; #ifdef HAVE_FPATHCONF path.allow_fd = 1; #endif @@ -9384,7 +9386,7 @@ posix_pathconf(PyObject *self, PyObject /* could be a path or name problem */ posix_error(); else - result = path_posix_error("pathconf", &path); + result = path_error(&path); } else result = PyLong_FromLong(limit); @@ -10444,6 +10446,8 @@ posix_getxattr(PyObject *self, PyObject memset(&path, 0, sizeof(path)); memset(&attribute, 0, sizeof(attribute)); + path.function_name = "getxattr"; + attribute.function_name = "getxattr"; path.allow_fd = 1; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&|$p:getxattr", keywords, path_converter, &path, @@ -10460,7 +10464,7 @@ posix_getxattr(PyObject *self, PyObject static Py_ssize_t buffer_sizes[] = {128, XATTR_SIZE_MAX, 0}; Py_ssize_t buffer_size = buffer_sizes[i]; if (!buffer_size) { - path_error("getxattr", &path); + path_error(&path); goto exit; } buffer = PyBytes_FromStringAndSize(NULL, buffer_size); @@ -10482,7 +10486,7 @@ posix_getxattr(PyObject *self, PyObject buffer = NULL; if (errno == ERANGE) continue; - path_error("getxattr", &path); + path_error(&path); goto exit; } @@ -10521,6 +10525,7 @@ posix_setxattr(PyObject *self, PyObject "flags", "follow_symlinks", NULL}; memset(&path, 0, sizeof(path)); + path.function_name = "setxattr"; path.allow_fd = 1; memset(&attribute, 0, sizeof(attribute)); memset(&value, 0, sizeof(value)); @@ -10548,7 +10553,7 @@ posix_setxattr(PyObject *self, PyObject Py_END_ALLOW_THREADS; if (result) { - return_value = path_error("setxattr", &path); + return_value = path_error(&path); goto exit; } @@ -10582,7 +10587,9 @@ posix_removexattr(PyObject *self, PyObje static char *keywords[] = {"path", "attribute", "follow_symlinks", NULL}; memset(&path, 0, sizeof(path)); + path.function_name = "removexattr"; memset(&attribute, 0, sizeof(attribute)); + attribute.function_name = "removexattr"; path.allow_fd = 1; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&|$p:removexattr", keywords, @@ -10604,7 +10611,7 @@ posix_removexattr(PyObject *self, PyObje Py_END_ALLOW_THREADS; if (result) { - return_value = path_error("removexattr", &path); + return_value = path_error(&path); goto exit; } @@ -10640,6 +10647,7 @@ posix_listxattr(PyObject *self, PyObject static char *keywords[] = {"path", "follow_symlinks", NULL}; memset(&path, 0, sizeof(path)); + path.function_name = "listxattr"; path.allow_fd = 1; path.fd = -1; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O&$p:listxattr", keywords, @@ -10658,7 +10666,7 @@ posix_listxattr(PyObject *self, PyObject Py_ssize_t buffer_size = buffer_sizes[i]; if (!buffer_size) { // ERANGE - path_error("listxattr", &path); + path_error(&path); break; } buffer = PyMem_MALLOC(buffer_size); @@ -10679,7 +10687,7 @@ posix_listxattr(PyObject *self, PyObject if (length < 0) { if (errno == ERANGE) continue; - path_error("listxattr", &path); + path_error(&path); break; }