diff -r 6641e941ef1e -r ff1a9aa48cfd app/django/core/files/move.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/django/core/files/move.py Tue Oct 14 16:00:59 2008 +0000 @@ -0,0 +1,89 @@ +""" +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: + import stat + 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