mirror of
https://github.com/python/cpython.git
synced 2025-08-02 16:13:13 +00:00
Issue #8828: Add new function os.replace(), for cross-platform renaming with overwriting.
This commit is contained in:
parent
8a8945085f
commit
f3b2d88b67
4 changed files with 65 additions and 15 deletions
|
@ -1889,8 +1889,9 @@ Files and Directories
|
||||||
Unix flavors if *src* and *dst* are on different filesystems. If successful,
|
Unix flavors if *src* and *dst* are on different filesystems. If successful,
|
||||||
the renaming will be an atomic operation (this is a POSIX requirement). On
|
the renaming will be an atomic operation (this is a POSIX requirement). On
|
||||||
Windows, if *dst* already exists, :exc:`OSError` will be raised even if it is a
|
Windows, if *dst* already exists, :exc:`OSError` will be raised even if it is a
|
||||||
file; there may be no way to implement an atomic rename when *dst* names an
|
file.
|
||||||
existing file.
|
|
||||||
|
If you want cross-platform overwriting of the destination, use :func:`replace`.
|
||||||
|
|
||||||
Availability: Unix, Windows.
|
Availability: Unix, Windows.
|
||||||
|
|
||||||
|
@ -1908,6 +1909,19 @@ Files and Directories
|
||||||
permissions needed to remove the leaf directory or file.
|
permissions needed to remove the leaf directory or file.
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: replace(src, dst)
|
||||||
|
|
||||||
|
Rename the file or directory *src* to *dst*. If *dst* is a directory,
|
||||||
|
:exc:`OSError` will be raised. If *dst* exists and is a file, it will
|
||||||
|
be replaced silently if the user has permission. The operation may fail
|
||||||
|
if *src* and *dst* are on different filesystems. If successful,
|
||||||
|
the renaming will be an atomic operation (this is a POSIX requirement).
|
||||||
|
|
||||||
|
Availability: Unix, Windows
|
||||||
|
|
||||||
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
|
||||||
.. function:: rmdir(path)
|
.. function:: rmdir(path)
|
||||||
|
|
||||||
Remove (delete) the directory *path*. Only works when the directory is
|
Remove (delete) the directory *path*. Only works when the directory is
|
||||||
|
|
|
@ -129,6 +129,18 @@ class FileTests(unittest.TestCase):
|
||||||
self.fdopen_helper('r')
|
self.fdopen_helper('r')
|
||||||
self.fdopen_helper('r', 100)
|
self.fdopen_helper('r', 100)
|
||||||
|
|
||||||
|
def test_replace(self):
|
||||||
|
TESTFN2 = support.TESTFN + ".2"
|
||||||
|
with open(support.TESTFN, 'w') as f:
|
||||||
|
f.write("1")
|
||||||
|
with open(TESTFN2, 'w') as f:
|
||||||
|
f.write("2")
|
||||||
|
self.addCleanup(os.unlink, TESTFN2)
|
||||||
|
os.replace(support.TESTFN, TESTFN2)
|
||||||
|
self.assertRaises(FileNotFoundError, os.stat, support.TESTFN)
|
||||||
|
with open(TESTFN2, 'r') as f:
|
||||||
|
self.assertEqual(f.read(), "1")
|
||||||
|
|
||||||
|
|
||||||
# Test attributes on return values from os.*stat* family.
|
# Test attributes on return values from os.*stat* family.
|
||||||
class StatAttributeTests(unittest.TestCase):
|
class StatAttributeTests(unittest.TestCase):
|
||||||
|
|
|
@ -463,6 +463,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #8828: Add new function os.replace(), for cross-platform renaming
|
||||||
|
with overwriting.
|
||||||
|
|
||||||
- Issue #13848: open() and the FileIO constructor now check for NUL
|
- Issue #13848: open() and the FileIO constructor now check for NUL
|
||||||
characters in the file name. Patch by Hynek Schlawack.
|
characters in the file name. Patch by Hynek Schlawack.
|
||||||
|
|
||||||
|
|
|
@ -3280,17 +3280,16 @@ posix_setpriority(PyObject *self, PyObject *args)
|
||||||
#endif /* HAVE_SETPRIORITY */
|
#endif /* HAVE_SETPRIORITY */
|
||||||
|
|
||||||
|
|
||||||
PyDoc_STRVAR(posix_rename__doc__,
|
|
||||||
"rename(old, new)\n\n\
|
|
||||||
Rename a file or directory.");
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
posix_rename(PyObject *self, PyObject *args)
|
internal_rename(PyObject *self, PyObject *args, int is_replace)
|
||||||
{
|
{
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
PyObject *src, *dst;
|
PyObject *src, *dst;
|
||||||
BOOL result;
|
BOOL result;
|
||||||
if (PyArg_ParseTuple(args, "UU:rename", &src, &dst))
|
int flags = is_replace ? MOVEFILE_REPLACE_EXISTING : 0;
|
||||||
|
if (PyArg_ParseTuple(args,
|
||||||
|
is_replace ? "UU:replace" : "UU:rename",
|
||||||
|
&src, &dst))
|
||||||
{
|
{
|
||||||
wchar_t *wsrc, *wdst;
|
wchar_t *wsrc, *wdst;
|
||||||
|
|
||||||
|
@ -3301,16 +3300,17 @@ posix_rename(PyObject *self, PyObject *args)
|
||||||
if (wdst == NULL)
|
if (wdst == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
result = MoveFileW(wsrc, wdst);
|
result = MoveFileExW(wsrc, wdst, flags);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
if (!result)
|
if (!result)
|
||||||
return win32_error("rename", NULL);
|
return win32_error(is_replace ? "replace" : "rename", NULL);
|
||||||
Py_INCREF(Py_None);
|
Py_INCREF(Py_None);
|
||||||
return Py_None;
|
return Py_None;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
if (!PyArg_ParseTuple(args, "O&O&:rename",
|
if (!PyArg_ParseTuple(args,
|
||||||
|
is_replace ? "O&O&:replace" : "O&O&:rename",
|
||||||
PyUnicode_FSConverter, &src,
|
PyUnicode_FSConverter, &src,
|
||||||
PyUnicode_FSConverter, &dst))
|
PyUnicode_FSConverter, &dst))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -3319,15 +3319,15 @@ posix_rename(PyObject *self, PyObject *args)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
result = MoveFileA(PyBytes_AS_STRING(src),
|
result = MoveFileExA(PyBytes_AS_STRING(src),
|
||||||
PyBytes_AS_STRING(dst));
|
PyBytes_AS_STRING(dst), flags);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
|
|
||||||
Py_XDECREF(src);
|
Py_XDECREF(src);
|
||||||
Py_XDECREF(dst);
|
Py_XDECREF(dst);
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
return win32_error("rename", NULL);
|
return win32_error(is_replace ? "replace" : "rename", NULL);
|
||||||
Py_INCREF(Py_None);
|
Py_INCREF(Py_None);
|
||||||
return Py_None;
|
return Py_None;
|
||||||
|
|
||||||
|
@ -3337,10 +3337,30 @@ error:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
return posix_2str(args, "O&O&:rename", rename);
|
return posix_2str(args,
|
||||||
|
is_replace ? "O&O&:replace" : "O&O&:rename", rename);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(posix_rename__doc__,
|
||||||
|
"rename(old, new)\n\n\
|
||||||
|
Rename a file or directory.");
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
posix_rename(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
return internal_rename(self, args, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(posix_replace__doc__,
|
||||||
|
"replace(old, new)\n\n\
|
||||||
|
Rename a file or directory, overwriting the destination.");
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
posix_replace(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
return internal_rename(self, args, 1);
|
||||||
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(posix_rmdir__doc__,
|
PyDoc_STRVAR(posix_rmdir__doc__,
|
||||||
"rmdir(path)\n\n\
|
"rmdir(path)\n\n\
|
||||||
|
@ -10555,6 +10575,7 @@ static PyMethodDef posix_methods[] = {
|
||||||
{"readlink", win_readlink, METH_VARARGS, win_readlink__doc__},
|
{"readlink", win_readlink, METH_VARARGS, win_readlink__doc__},
|
||||||
#endif /* !defined(HAVE_READLINK) && defined(MS_WINDOWS) */
|
#endif /* !defined(HAVE_READLINK) && defined(MS_WINDOWS) */
|
||||||
{"rename", posix_rename, METH_VARARGS, posix_rename__doc__},
|
{"rename", posix_rename, METH_VARARGS, posix_rename__doc__},
|
||||||
|
{"replace", posix_replace, METH_VARARGS, posix_replace__doc__},
|
||||||
{"rmdir", posix_rmdir, METH_VARARGS, posix_rmdir__doc__},
|
{"rmdir", posix_rmdir, METH_VARARGS, posix_rmdir__doc__},
|
||||||
{"stat", posix_stat, METH_VARARGS, posix_stat__doc__},
|
{"stat", posix_stat, METH_VARARGS, posix_stat__doc__},
|
||||||
{"stat_float_times", stat_float_times, METH_VARARGS, stat_float_times__doc__},
|
{"stat_float_times", stat_float_times, METH_VARARGS, stat_float_times__doc__},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue