summaryrefslogtreecommitdiffstats
path: root/webapp/django/core/files/move.py
diff options
context:
space:
mode:
Diffstat (limited to 'webapp/django/core/files/move.py')
-rw-r--r--webapp/django/core/files/move.py88
1 files changed, 88 insertions, 0 deletions
diff --git a/webapp/django/core/files/move.py b/webapp/django/core/files/move.py
new file mode 100644
index 0000000000..58a0ab125d
--- /dev/null
+++ b/webapp/django/core/files/move.py
@@ -0,0 +1,88 @@
+"""
+Move a file in the safest way possible::
+
+ >>> from django.core.files.move import file_move_save
+ >>> file_move_save("/tmp/old_file", "/tmp/new_file")
+"""
+
+import os
+from django.core.files import locks
+
+try:
+ from shutil import copystat
+except ImportError:
+ 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):
+ """
+ Moves a file from one location to another in the safest way possible.
+
+ First, try using ``shutils.move``, which is OS-dependent but doesn't break
+ if moving across filesystems. Then, try ``os.rename``, which will break
+ across filesystems. Finally, streams manually from one file to another in
+ pure Python.
+
+ If the destination file exists and ``allow_overwrite`` is ``False``, this
+ function will throw an ``IOError``.
+ """
+
+ # There's no reason to move if we don't have to.
+ if _samefile(old_file_name, new_file_name):
+ return
+
+ try:
+ 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
+
+ # first open the old file, so that it won't go away
+ old_file = open(old_file_name, 'rb')
+ try:
+ # 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:
+ old_file.close()
+ copystat(old_file_name, new_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