cpython: 3b7230997425 (original) (raw)
--- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -1750,14 +1750,10 @@ Files and Directories The dir_fd argument. -.. function:: remove(path, *, dir_fd=None, rmdir=False) -
- Remove (delete) the file path. This function is identical to
- :func:
os.unlink
. - - Specify
rmdir=True
if path is a directory. Failing to do so - will raise an exception; likewise, specifying
rmdir=True
when - path is not a directory will also raise an exception. +.. function:: remove(path, *, dir_fd=None) +
- Remove (delete) the file path. If path is a directory, :exc:
OSError
- is raised. Use :func:
rmdir
to remove directories. If dir_fd is notNone
, it should be a file descriptor referring to a directory, and path should be relative; path will then be relative to @@ -1771,10 +1767,12 @@ Files and Directories be raised; on Unix, the directory entry is removed but the storage allocated to the file is not made available until the original file is no longer in use. - This function is identical to :func:
unlink
. + Availability: Unix, Windows. .. versionadded:: 3.3
The *dir_fd* and *rmdir* arguments.[](#l1.31)
The *dir_fd* argument.[](#l1.32)
.. function:: removedirs(path)
@@ -1872,14 +1870,25 @@ Files and Directories
.. versionadded:: 3.3
-.. function:: rmdir(path)
+.. function:: rmdir(path, *, dir_fd=None)
Remove (delete) the directory path. Only works when the directory is
empty, otherwise, :exc:OSError
is raised. In order to remove whole
directory trees, :func:shutil.rmtree
can be used.
- If dir_fd is not
None
, it should be a file descriptor referring to a - directory, and path should be relative; path will then be relative to
- that directory. (If path is absolute, dir_fd is ignored.)
- dir_fd may not be supported on your platform;
- you can check whether or not it is available using
- :data:
os.supports_dir_fd
. If it is unavailable, using it will raise - a :exc:
NotImplementedError
. + Availability: Unix, Windows. - .. versionadded:: 3.3
The *dir_fd* parameter.[](#l1.58)
+ .. data:: XATTR_SIZE_MAX @@ -2235,9 +2244,9 @@ Files and Directories .. versionadded:: 3.3 -.. function:: unlink(path, *, dir_fd=None, rmdir=False) -
- Remove (delete) the file path. This is the same function as +.. function:: unlink(path, *, dir_fd=None) +
- Remove (delete) the file path. This function is identical to
:func:
remove
; the :func:unlink
name is its traditional Unix name. Please see the documentation for :func:remove
for further information. @@ -2245,7 +2254,7 @@ Files and Directories Availability: Unix, Windows. .. versionadded:: 3.3
The *dir_fd* and *rmdir* parameters.[](#l1.80)
The *dir_fd* parameter.[](#l1.81)
.. function:: utime(path, times=None, *, ns=None, dir_fd=None, follow_symlinks=True)
--- a/Lib/os.py +++ b/Lib/os.py @@ -157,6 +157,7 @@ if _exists("_have_functions"): _add("HAVE_RENAMEAT", "rename") _add("HAVE_SYMLINKAT", "symlink") _add("HAVE_UNLINKAT", "unlink")
- _add("HAVE_UNLINKAT", "rmdir") _add("HAVE_UTIMENSAT", "utime") supports_dir_fd = _set @@ -214,10 +215,6 @@ if _exists("_have_functions"): _add("MS_WINDOWS", "stat") supports_follow_symlinks = _set
- del _set del _have_functions del _globals
--- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -785,7 +785,10 @@ class FwalkTests(WalkTests): os.unlink(name, dir_fd=rootfd) for name in dirs: st = os.stat(name, dir_fd=rootfd, follow_symlinks=False)
os.unlink(name, dir_fd=rootfd, rmdir=stat.S_ISDIR(st.st_mode))[](#l3.7)
if stat.S_ISDIR(st.st_mode):[](#l3.8)
os.rmdir(name, dir_fd=rootfd)[](#l3.9)
else:[](#l3.10)
os.unlink(name, dir_fd=rootfd)[](#l3.11) os.rmdir(support.TESTFN)[](#l3.12)
--- a/Misc/NEWS +++ b/Misc/NEWS @@ -47,6 +47,9 @@ Core and Builtins Library ------- +- Issue #15154: Add "dir_fd" parameter to os.rmdir, remove "rmdir"
- Issue #4489: Add a shutil.rmtree that isn't susceptible to symlink attacks. It is used automatically on platforms supporting the necessary os.openat() and os.unlinkat() functions. Main code by Martin von Löwis.
--- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -4084,17 +4084,62 @@ posix_replace(PyObject *self, PyObject * } PyDoc_STRVAR(posix_rmdir__doc__, -"rmdir(path)\n\n[](#l5.7) -Remove a directory."); - -static PyObject * -posix_rmdir(PyObject *self, PyObject *args) -{ +"rmdir(path, *, dir_fd=None)\n\n[](#l5.13) +Remove a directory.\n[](#l5.14) +\n[](#l5.15) +If dir_fd is not None, it should be a file descriptor open to a directory,\n[](#l5.16)
- and path should be relative; path will then be relative to that directory.\n[](#l5.17) +dir_fd may not be implemented on your platform.\n[](#l5.18)
- If it is unavailable, using it will raise a NotImplementedError."); + +static PyObject * +posix_rmdir(PyObject *self, PyObject *args, PyObject *kwargs) +{
- path_t path;
- int dir_fd = DEFAULT_DIR_FD;
- static char *keywords[] = {"path", "dir_fd", NULL};
- int result;
- PyObject *return_value = NULL;
- memset(&path, 0, sizeof(path));
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|$O&:rmdir", keywords,
path_converter, &path,[](#l5.32)
dir_fd_converter, &dir_fd[](#l5.34)
dir_fd_unavailable, &dir_fd[](#l5.36)
))[](#l5.38)
return NULL;[](#l5.39)
- if (path.wide)
result = RemoveDirectoryW(path.wide);[](#l5.48)
- else
result = RemoveDirectoryA(path.narrow);[](#l5.50)
- result = !result; /* Windows, success=1, UNIX, success=0 */
result = rmdir(path.narrow);[](#l5.58)
} @@ -4186,68 +4231,54 @@ BOOL WINAPI Py_DeleteFileW(LPCWSTR lpFil #endif /* MS_WINDOWS */ PyDoc_STRVAR(posix_unlink__doc__, -"unlink(path, *, dir_fd=None, rmdir=False)\n\n[](#l5.80) +"unlink(path, *, dir_fd=None)\n\n[](#l5.81) Remove a file (same as remove()).\n[](#l5.82) \n[](#l5.83) If dir_fd is not None, it should be a file descriptor open to a directory,\n[](#l5.84) and path should be relative; path will then be relative to that directory.\n[](#l5.85) dir_fd may not be implemented on your platform.\n[](#l5.86)
- If it is unavailable, using it will raise a NotImplementedError.\n[](#l5.87) -If rmdir is True, unlink will behave like os.rmdir().");
- If it is unavailable, using it will raise a NotImplementedError."); PyDoc_STRVAR(posix_remove__doc__, -"remove(path, *, dir_fd=None, rmdir=False)\n\n[](#l5.92) +"remove(path, *, dir_fd=None)\n\n[](#l5.93) Remove a file (same as unlink()).\n[](#l5.94) \n[](#l5.95) If dir_fd is not None, it should be a file descriptor open to a directory,\n[](#l5.96) and path should be relative; path will then be relative to that directory.\n[](#l5.97) dir_fd may not be implemented on your platform.\n[](#l5.98)
- If it is unavailable, using it will raise a NotImplementedError.\n[](#l5.99) -If rmdir is True, remove will behave like os.rmdir().");
- If it is unavailable, using it will raise a NotImplementedError."); static PyObject * posix_unlink(PyObject *self, PyObject *args, PyObject *kwargs) { path_t path; int dir_fd = DEFAULT_DIR_FD;
- static char *keywords[] = {"path", "dir_fd", NULL}; int result; PyObject *return_value = NULL; memset(&path, 0, sizeof(path));
dir_fd_converter, &dir_fd,[](#l5.119)
dir_fd_unavailable, &dir_fd,[](#l5.121)
&remove_dir))[](#l5.123)
dir_fd_converter, &dir_fd[](#l5.124)
dir_fd_unavailable, &dir_fd[](#l5.126)
))[](#l5.128) return NULL;[](#l5.129)
Py_BEGIN_ALLOW_THREADS #ifdef MS_WINDOWS
- if (remove_dir) {
if (path.wide)[](#l5.134)
result = RemoveDirectoryW(path.wide);[](#l5.135)
else[](#l5.136)
result = RemoveDirectoryA(path.narrow);[](#l5.137)
- }
- else {
if (path.wide)[](#l5.140)
result = Py_DeleteFileW(path.wide);[](#l5.141)
else[](#l5.142)
result = DeleteFileA(path.narrow);[](#l5.143)
- }
- if (path.wide)
result = Py_DeleteFileW(path.wide);[](#l5.146)
- else
result = !result; /* Windows, success=1, UNIX, success=0 */result = DeleteFileA(path.narrow);[](#l5.148)
#ifdef HAVE_UNLINKAT if (dir_fd != DEFAULT_DIR_FD)
result = unlinkat(dir_fd, path.narrow, remove_dir ? AT_REMOVEDIR : 0);[](#l5.156)
#endif /* HAVE_UNLINKAT */ result = unlink(path.narrow); @@ -10806,7 +10837,9 @@ static PyMethodDef posix_methods[] = { {"replace", (PyCFunction)posix_replace, METH_VARARGS | METH_KEYWORDS, posix_replace__doc__},