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">&lt;<a href="mailto:python-checkins@python.org">python-checkins@python.org</a>&gt;</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 &lt;<a href="mailto:solipsis@pitrou.net">solipsis@pitrou.net</a>&gt;<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(&#39;r&#39;)<br>
         self.fdopen_helper(&#39;r&#39;, 100)<br>
<br>
+    def test_replace(self):<br>
+        TESTFN2 = support.TESTFN + &quot;.2&quot;<br>
+        with open(support.TESTFN, &#39;w&#39;) as f:<br>
+            f.write(&quot;1&quot;)<br>
+        with open(TESTFN2, &#39;w&#39;) as f:<br>
+            f.write(&quot;2&quot;)<br>
+        self.addCleanup(os.unlink, TESTFN2)<br>
+        os.replace(support.TESTFN, TESTFN2)<br>
+        self.assertRaises(FileNotFoundError, os.stat, support.TESTFN)<br>
+        with open(TESTFN2, &#39;r&#39;) as f:<br>
+            self.assertEqual(f.read(), &quot;1&quot;)<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>
-&quot;rename(old, new)\n\n\<br>
-Rename a file or directory.&quot;);<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, &quot;UU:rename&quot;, &amp;src, &amp;dst))<br>
+    int flags = is_replace ? MOVEFILE_REPLACE_EXISTING : 0;<br>
+    if (PyArg_ParseTuple(args,<br>
+                         is_replace ? &quot;UU:replace&quot; : &quot;UU:rename&quot;,<br>
+                         &amp;src, &amp;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(&quot;rename&quot;, NULL);<br>
+            return win32_error(is_replace ? &quot;replace&quot; : &quot;rename&quot;, NULL);<br>
         Py_INCREF(Py_None);<br>
         return Py_None;<br>
     }<br>
     else {<br>
         PyErr_Clear();<br>
-        if (!PyArg_ParseTuple(args, &quot;O&amp;O&amp;:rename&quot;,<br>
+        if (!PyArg_ParseTuple(args,<br>
+                              is_replace ? &quot;O&amp;O&amp;:replace&quot; : &quot;O&amp;O&amp;:rename&quot;,<br>
                               PyUnicode_FSConverter, &amp;src,<br>
                               PyUnicode_FSConverter, &amp;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(&quot;rename&quot;, NULL);<br>
+            return win32_error(is_replace ? &quot;replace&quot; : &quot;rename&quot;, 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, &quot;O&amp;O&amp;:rename&quot;, rename);<br>
-#endif<br>
-}<br>
-<br>
+    return posix_2str(args,<br>
+                      is_replace ? &quot;O&amp;O&amp;:replace&quot; : &quot;O&amp;O&amp;:rename&quot;, rename);<br>
+#endif<br>
+}<br>
+<br>
+PyDoc_STRVAR(posix_rename__doc__,<br>
+&quot;rename(old, new)\n\n\<br>
+Rename a file or directory.&quot;);<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>
+&quot;replace(old, new)\n\n\<br>
+Rename a file or directory, overwriting the destination.&quot;);<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>
 &quot;rmdir(path)\n\n\<br>
@@ -10555,6 +10575,7 @@<br>
     {&quot;readlink&quot;,        win_readlink, METH_VARARGS, win_readlink__doc__},<br>
 #endif /* !defined(HAVE_READLINK) &amp;&amp; defined(MS_WINDOWS) */<br>
     {&quot;rename&quot;,          posix_rename, METH_VARARGS, posix_rename__doc__},<br>
+    {&quot;replace&quot;,         posix_replace, METH_VARARGS, posix_replace__doc__},<br>
     {&quot;rmdir&quot;,           posix_rmdir, METH_VARARGS, posix_rmdir__doc__},<br>
     {&quot;stat&quot;,            posix_stat, METH_VARARGS, posix_stat__doc__},<br>
     {&quot;stat_float_times&quot;, 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>