summaryrefslogtreecommitdiff
path: root/django/core/files/move.py
diff options
context:
space:
mode:
authorMalcolm Tredinnick <malcolm.tredinnick@gmail.com>2008-08-23 17:56:02 +0000
committerMalcolm Tredinnick <malcolm.tredinnick@gmail.com>2008-08-23 17:56:02 +0000
commita9f0ae706a8dc035543ac64a1e87e9621a35fa00 (patch)
tree8b2d416e9dbe5c37e46eeaaaa7b2abc00f3f7f98 /django/core/files/move.py
parenteaa64cd889587f4d459ab0bb62c96c04d362d0eb (diff)
downloaddjango-a9f0ae706a8dc035543ac64a1e87e9621a35fa00.tar.gz
Fixed #8203 -- Fixed temporary file deleation on Windows and a couple of edge
cases on Unix-like systems. Patch from snaury. Testing and verification on Windows, Mac and Linux from cgrady and ramikassab. git-svn-id: http://code.djangoproject.com/svn/django/trunk@8493 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/core/files/move.py')
-rw-r--r--django/core/files/move.py70
1 files changed, 49 insertions, 21 deletions
diff --git a/django/core/files/move.py b/django/core/files/move.py
index 5ae776c3dd..58a0ab125d 100644
--- a/django/core/files/move.py
+++ b/django/core/files/move.py
@@ -8,13 +8,31 @@ Move a file in the safest way possible::
import os
from django.core.files import locks
-__all__ = ['file_move_safe']
-
try:
- import shutil
- file_move = shutil.move
+ from shutil import copystat
except ImportError:
- file_move = os.rename
+ def copystat(src, dst):
+ """Copy all stat info (mode bits, atime and mtime) from src to dst"""
+ st = os.stat(src)
+ mode = stat.S_IMODE(st.st_mode)
+ if hasattr(os, 'utime'):
+ os.utime(dst, (st.st_atime, st.st_mtime))
+ if hasattr(os, 'chmod'):
+ os.chmod(dst, mode)
+
+__all__ = ['file_move_safe']
+
+def _samefile(src, dst):
+ # Macintosh, Unix.
+ if hasattr(os.path,'samefile'):
+ try:
+ return os.path.samefile(src, dst)
+ except OSError:
+ return False
+
+ # All other platforms: check for same pathname.
+ return (os.path.normcase(os.path.abspath(src)) ==
+ os.path.normcase(os.path.abspath(dst)))
def file_move_safe(old_file_name, new_file_name, chunk_size = 1024*64, allow_overwrite=False):
"""
@@ -30,31 +48,41 @@ def file_move_safe(old_file_name, new_file_name, chunk_size = 1024*64, allow_ove
"""
# There's no reason to move if we don't have to.
- if old_file_name == new_file_name:
+ if _samefile(old_file_name, new_file_name):
return
- if not allow_overwrite and os.path.exists(new_file_name):
- raise IOError("Cannot overwrite existing file '%s'." % new_file_name)
-
try:
- file_move(old_file_name, new_file_name)
+ os.rename(old_file_name, new_file_name)
return
except OSError:
# This will happen with os.rename if moving to another filesystem
+ # or when moving opened files on certain operating systems
pass
- # If the built-in didn't work, do it the hard way.
- fd = os.open(new_file_name, os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, 'O_BINARY', 0))
+ # first open the old file, so that it won't go away
+ old_file = open(old_file_name, 'rb')
try:
- locks.lock(fd, locks.LOCK_EX)
- old_file = open(old_file_name, 'rb')
- current_chunk = None
- while current_chunk != '':
- current_chunk = old_file.read(chunk_size)
- os.write(fd, current_chunk)
+ # now open the new file, not forgetting allow_overwrite
+ fd = os.open(new_file_name, os.O_WRONLY | os.O_CREAT | getattr(os, 'O_BINARY', 0) |
+ (not allow_overwrite and os.O_EXCL or 0))
+ try:
+ locks.lock(fd, locks.LOCK_EX)
+ current_chunk = None
+ while current_chunk != '':
+ current_chunk = old_file.read(chunk_size)
+ os.write(fd, current_chunk)
+ finally:
+ locks.unlock(fd)
+ os.close(fd)
finally:
- locks.unlock(fd)
- os.close(fd)
old_file.close()
+ copystat(old_file_name, new_file_name)
- os.remove(old_file_name)
+ try:
+ os.remove(old_file_name)
+ except OSError, e:
+ # Certain operating systems (Cygwin and Windows)
+ # fail when deleting opened files, ignore it
+ if getattr(e, 'winerror', 0) != 32:
+ # FIXME: should we also ignore errno 13?
+ raise