diff options
author | Malcolm Tredinnick <malcolm.tredinnick@gmail.com> | 2008-08-23 17:56:02 +0000 |
---|---|---|
committer | Malcolm Tredinnick <malcolm.tredinnick@gmail.com> | 2008-08-23 17:56:02 +0000 |
commit | a9f0ae706a8dc035543ac64a1e87e9621a35fa00 (patch) | |
tree | 8b2d416e9dbe5c37e46eeaaaa7b2abc00f3f7f98 /django/core/files/move.py | |
parent | eaa64cd889587f4d459ab0bb62c96c04d362d0eb (diff) | |
download | django-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.py | 70 |
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 |