changeset 4948:068c47fc2c3a

merged moin/1.8
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Sat, 15 Aug 2009 23:42:41 +0200
parents dce251f8cfc3 (current diff) 3695ad9d93f2 (diff)
children b1bfe9bc5375 6638f02fc4b0 09e577676ba2 10965bc1ee3c
files MoinMoin/converter/text_html_text_moin_wiki.py MoinMoin/util/filesys.py
diffstat 3 files changed, 131 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/converter/text_html_text_moin_wiki.py	Sun Aug 09 01:30:45 2009 +0200
+++ b/MoinMoin/converter/text_html_text_moin_wiki.py	Sat Aug 15 23:42:41 2009 +0200
@@ -682,6 +682,12 @@
         found = False
         need_indent = False
         pending = []
+
+        # If this is a empty list item, we just terminate the line
+        if node.childNodes.length == 0:
+            self.text.append(self.new_line)
+            return
+
         for i in node.childNodes:
             name = i.localName
 
--- a/MoinMoin/util/_tests/test_filesys.py	Sun Aug 09 01:30:45 2009 +0200
+++ b/MoinMoin/util/_tests/test_filesys.py	Sat Aug 15 23:42:41 2009 +0200
@@ -79,4 +79,60 @@
         assert uid2 != uid1  # should be considered stale if platform has no inode support
 
 
+class TestRename:
+    """ test filesys.rename* """
+
+    def setup_method(self, method):
+        self.test_dir = tempfile.mkdtemp('', 'rename_')
+        self.src = os.path.join(self.test_dir, "rename-src")
+        self.dst = os.path.join(self.test_dir, "rename-dst")
+
+    def teardown_method(self, method):
+        shutil.rmtree(self.test_dir)
+
+    def makefile(self, fname, content):
+        f = open(fname, "w")
+        f.write(content)
+        f.close()
+
+    def test_posix_rename_exists(self):
+        self.makefile(self.src, "src")
+        self.makefile(self.dst, "dst")
+        # posix rename overwrites an existing destination
+        # (on win32, we emulate this behaviour)
+        filesys.rename(self.src, self.dst)
+        dst_contents = open(self.dst).read()
+        assert dst_contents == "src"
+
+    def test_win32_rename_exists(self):
+        self.makefile(self.src, "src")
+        self.makefile(self.dst, "dst")
+        # win32-like rename does not overwrite an existing destination
+        # (on posix, we emulate this behaviour)
+        py.test.raises(OSError, filesys.rename_no_overwrite, self.src, self.dst)
+
+    def test_special_rename_exists(self):
+        self.makefile(self.src, "src")
+        self.makefile(self.dst, "dst")
+        py.test.raises(OSError, filesys.rename_no_overwrite, self.src, self.dst, delete_old=True)
+        assert not os.path.exists(self.src)
+
+    def test_posix_rename_notexists(self):
+        self.makefile(self.src, "src")
+        filesys.rename(self.src, self.dst)
+        dst_contents = open(self.dst).read()
+        assert dst_contents == "src"
+
+    def test_win32_rename_notexists(self):
+        self.makefile(self.src, "src")
+        filesys.rename_no_overwrite(self.src, self.dst)
+        dst_contents = open(self.dst).read()
+        assert dst_contents == "src"
+
+    def test_special_rename_notexists(self):
+        self.makefile(self.src, "src")
+        filesys.rename_no_overwrite(self.src, self.dst, delete_old=True)
+        assert not os.path.exists(self.src)
+
+
 coverage_modules = ['MoinMoin.util.filesys']
--- a/MoinMoin/util/filesys.py	Sun Aug 09 01:30:45 2009 +0200
+++ b/MoinMoin/util/filesys.py	Sat Aug 15 23:42:41 2009 +0200
@@ -7,7 +7,7 @@
     @license: GNU GPL, see COPYING for details.
 """
 
-import sys, os, shutil, time, errno
+import sys, os, shutil, time, errno, random
 from stat import S_ISDIR, ST_MODE, S_IMODE
 
 from MoinMoin import log
@@ -42,12 +42,75 @@
     rename, then unlock when finished.
     """
     if os.name == 'nt':
-        if os.path.isfile(newname):
+        # Windows "rename" taken from Mercurial's util.py. Thanks!
+        try:
+            os.rename(oldname, newname)
+        except OSError, err:
+            # On windows, rename to existing file is not allowed, so we
+            # must delete destination first. But if a file is open, unlink
+            # schedules it for delete but does not delete it. Rename
+            # happens immediately even for open files, so we rename
+            # destination to a temporary name, then delete that. Then
+            # rename is safe to do.
+            # The temporary name is chosen at random to avoid the situation
+            # where a file is left lying around from a previous aborted run.
+            # The usual race condition this introduces can't be avoided as
+            # we need the name to rename into, and not the file itself. Due
+            # to the nature of the operation however, any races will at worst
+            # lead to the rename failing and the current operation aborting.
+
+            if err.errno != errno.EEXIST:
+                raise
+
+            def tempname(prefix):
+                for tries in xrange(10):
+                    temp = '%s-%08x' % (prefix, random.randint(0, 0xffffffff))
+                    if not os.path.exists(temp):
+                        return temp
+                raise IOError, (errno.EEXIST, "No usable temporary filename found")
+
+            temp = tempname(newname)
+            os.rename(newname, temp)
+            os.unlink(temp)
+            os.rename(oldname, newname)
+    else:
+        # POSIX: just do it :)
+        os.rename(oldname, newname)
+
+rename_overwrite = rename
+
+def rename_no_overwrite(oldname, newname, delete_old=False):
+    """ Multiplatform rename
+
+    This kind of rename is doing things differently: it fails if newname
+    already exists. This is the usual thing on win32, but not on posix.
+
+    If delete_old is True, oldname is removed in any case (even if the
+    rename did not succeed).
+    """
+    if os.name == 'nt':
+        try:
             try:
-                os.remove(newname)
-            except OSError:
-                pass # let os.rename give us the error (if any)
-    os.rename(oldname, newname)
+                os.rename(oldname, newname)
+                success = True
+            except:
+                success = False
+                raise
+        finally:
+            if not success and delete_old:
+                os.unlink(oldname)
+    else:
+        try:
+            try:
+                os.link(oldname, newname)
+                success = True
+            except:
+                success = False
+                raise
+        finally:
+            if success or delete_old:
+                os.unlink(oldname)
+
 
 def touch(name):
     if sys.platform == 'win32':