Should this end up being used in importlib through _os?<br><br><div class="gmail_quote">On Mon, Jan 30, 2012 at 16:11, antoine.pitrou <span dir="ltr"><<a href="mailto:python-checkins@python.org">python-checkins@python.org</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><a href="http://hg.python.org/cpython/rev/80ddbd822227" target="_blank">http://hg.python.org/cpython/rev/80ddbd822227</a><br>
changeset: 74689:80ddbd822227<br>
user: Antoine Pitrou <<a href="mailto:solipsis@pitrou.net">solipsis@pitrou.net</a>><br>
date: Mon Jan 30 22:08:52 2012 +0100<br>
summary:<br>
Issue #8828: Add new function os.replace(), for cross-platform renaming with overwriting.<br>
<br>
files:<br>
Doc/library/os.rst | 18 +++++++++-<br>
Lib/test/test_os.py | 12 ++++++<br>
Misc/NEWS | 3 +<br>
Modules/posixmodule.c | 55 +++++++++++++++++++++---------<br>
4 files changed, 69 insertions(+), 19 deletions(-)<br>
<br>
<br>
diff --git a/Doc/library/os.rst b/Doc/library/os.rst<br>
--- a/Doc/library/os.rst<br>
+++ b/Doc/library/os.rst<br>
@@ -1889,8 +1889,9 @@<br>
Unix flavors if *src* and *dst* are on different filesystems. If successful,<br>
the renaming will be an atomic operation (this is a POSIX requirement). On<br>
Windows, if *dst* already exists, :exc:`OSError` will be raised even if it is a<br>
- file; there may be no way to implement an atomic rename when *dst* names an<br>
- existing file.<br>
+ file.<br>
+<br>
+ If you want cross-platform overwriting of the destination, use :func:`replace`.<br>
<br>
Availability: Unix, Windows.<br>
<br>
@@ -1908,6 +1909,19 @@<br>
permissions needed to remove the leaf directory or file.<br>
<br>
<br>
+.. function:: replace(src, dst)<br>
+<br>
+ Rename the file or directory *src* to *dst*. If *dst* is a directory,<br>
+ :exc:`OSError` will be raised. If *dst* exists and is a file, it will<br>
+ be replaced silently if the user has permission. The operation may fail<br>
+ if *src* and *dst* are on different filesystems. If successful,<br>
+ the renaming will be an atomic operation (this is a POSIX requirement).<br>
+<br>
+ Availability: Unix, Windows<br>
+<br>
+ .. versionadded:: 3.3<br>
+<br>
+<br>
.. function:: rmdir(path)<br>
<br>
Remove (delete) the directory *path*. Only works when the directory is<br>
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py<br>
--- a/Lib/test/test_os.py<br>
+++ b/Lib/test/test_os.py<br>
@@ -129,6 +129,18 @@<br>
self.fdopen_helper('r')<br>
self.fdopen_helper('r', 100)<br>
<br>
+ def test_replace(self):<br>
+ TESTFN2 = support.TESTFN + ".2"<br>
+ with open(support.TESTFN, 'w') as f:<br>
+ f.write("1")<br>
+ with open(TESTFN2, 'w') as f:<br>
+ f.write("2")<br>
+ self.addCleanup(os.unlink, TESTFN2)<br>
+ os.replace(support.TESTFN, TESTFN2)<br>
+ self.assertRaises(FileNotFoundError, os.stat, support.TESTFN)<br>
+ with open(TESTFN2, 'r') as f:<br>
+ self.assertEqual(f.read(), "1")<br>
+<br>
<br>
# Test attributes on return values from os.*stat* family.<br>
class StatAttributeTests(unittest.TestCase):<br>
diff --git a/Misc/NEWS b/Misc/NEWS<br>
--- a/Misc/NEWS<br>
+++ b/Misc/NEWS<br>
@@ -463,6 +463,9 @@<br>
Library<br>
-------<br>
<br>
+- Issue #8828: Add new function os.replace(), for cross-platform renaming<br>
+ with overwriting.<br>
+<br>
- Issue #13848: open() and the FileIO constructor now check for NUL<br>
characters in the file name. Patch by Hynek Schlawack.<br>
<br>
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c<br>
--- a/Modules/posixmodule.c<br>
+++ b/Modules/posixmodule.c<br>
@@ -3280,17 +3280,16 @@<br>
#endif /* HAVE_SETPRIORITY */<br>
<br>
<br>
-PyDoc_STRVAR(posix_rename__doc__,<br>
-"rename(old, new)\n\n\<br>
-Rename a file or directory.");<br>
-<br>
-static PyObject *<br>
-posix_rename(PyObject *self, PyObject *args)<br>
+static PyObject *<br>
+internal_rename(PyObject *self, PyObject *args, int is_replace)<br>
{<br>
#ifdef MS_WINDOWS<br>
PyObject *src, *dst;<br>
BOOL result;<br>
- if (PyArg_ParseTuple(args, "UU:rename", &src, &dst))<br>
+ int flags = is_replace ? MOVEFILE_REPLACE_EXISTING : 0;<br>
+ if (PyArg_ParseTuple(args,<br>
+ is_replace ? "UU:replace" : "UU:rename",<br>
+ &src, &dst))<br>
{<br>
wchar_t *wsrc, *wdst;<br>
<br>
@@ -3301,16 +3300,17 @@<br>
if (wdst == NULL)<br>
return NULL;<br>
Py_BEGIN_ALLOW_THREADS<br>
- result = MoveFileW(wsrc, wdst);<br>
+ result = MoveFileExW(wsrc, wdst, flags);<br>
Py_END_ALLOW_THREADS<br>
if (!result)<br>
- return win32_error("rename", NULL);<br>
+ return win32_error(is_replace ? "replace" : "rename", NULL);<br>
Py_INCREF(Py_None);<br>
return Py_None;<br>
}<br>
else {<br>
PyErr_Clear();<br>
- if (!PyArg_ParseTuple(args, "O&O&:rename",<br>
+ if (!PyArg_ParseTuple(args,<br>
+ is_replace ? "O&O&:replace" : "O&O&:rename",<br>
PyUnicode_FSConverter, &src,<br>
PyUnicode_FSConverter, &dst))<br>
return NULL;<br>
@@ -3319,15 +3319,15 @@<br>
goto error;<br>
<br>
Py_BEGIN_ALLOW_THREADS<br>
- result = MoveFileA(PyBytes_AS_STRING(src),<br>
- PyBytes_AS_STRING(dst));<br>
+ result = MoveFileExA(PyBytes_AS_STRING(src),<br>
+ PyBytes_AS_STRING(dst), flags);<br>
Py_END_ALLOW_THREADS<br>
<br>
Py_XDECREF(src);<br>
Py_XDECREF(dst);<br>
<br>
if (!result)<br>
- return win32_error("rename", NULL);<br>
+ return win32_error(is_replace ? "replace" : "rename", NULL);<br>
Py_INCREF(Py_None);<br>
return Py_None;<br>
<br>
@@ -3337,10 +3337,30 @@<br>
return NULL;<br>
}<br>
#else<br>
- return posix_2str(args, "O&O&:rename", rename);<br>
-#endif<br>
-}<br>
-<br>
+ return posix_2str(args,<br>
+ is_replace ? "O&O&:replace" : "O&O&:rename", rename);<br>
+#endif<br>
+}<br>
+<br>
+PyDoc_STRVAR(posix_rename__doc__,<br>
+"rename(old, new)\n\n\<br>
+Rename a file or directory.");<br>
+<br>
+static PyObject *<br>
+posix_rename(PyObject *self, PyObject *args)<br>
+{<br>
+ return internal_rename(self, args, 0);<br>
+}<br>
+<br>
+PyDoc_STRVAR(posix_replace__doc__,<br>
+"replace(old, new)\n\n\<br>
+Rename a file or directory, overwriting the destination.");<br>
+<br>
+static PyObject *<br>
+posix_replace(PyObject *self, PyObject *args)<br>
+{<br>
+ return internal_rename(self, args, 1);<br>
+}<br>
<br>
PyDoc_STRVAR(posix_rmdir__doc__,<br>
"rmdir(path)\n\n\<br>
@@ -10555,6 +10575,7 @@<br>
{"readlink", win_readlink, METH_VARARGS, win_readlink__doc__},<br>
#endif /* !defined(HAVE_READLINK) && defined(MS_WINDOWS) */<br>
{"rename", posix_rename, METH_VARARGS, posix_rename__doc__},<br>
+ {"replace", posix_replace, METH_VARARGS, posix_replace__doc__},<br>
{"rmdir", posix_rmdir, METH_VARARGS, posix_rmdir__doc__},<br>
{"stat", posix_stat, METH_VARARGS, posix_stat__doc__},<br>
{"stat_float_times", stat_float_times, METH_VARARGS, stat_float_times__doc__},<br>
<span class="HOEnZb"><font color="#888888"><br>
--<br>
Repository URL: <a href="http://hg.python.org/cpython" target="_blank">http://hg.python.org/cpython</a><br>
</font></span><br>_______________________________________________<br>
Python-checkins mailing list<br>
<a href="mailto:Python-checkins@python.org">Python-checkins@python.org</a><br>
<a href="http://mail.python.org/mailman/listinfo/python-checkins" target="_blank">http://mail.python.org/mailman/listinfo/python-checkins</a><br>
<br></blockquote></div><br>