web/lib/django/core/files/move.py
changeset 0 0d40e90630ef
equal deleted inserted replaced
-1:000000000000 0:0d40e90630ef
       
     1 """
       
     2 Move a file in the safest way possible::
       
     3 
       
     4     >>> from django.core.files.move import file_move_safe
       
     5     >>> file_move_safe("/tmp/old_file", "/tmp/new_file")
       
     6 """
       
     7 
       
     8 import os
       
     9 from django.core.files import locks
       
    10 
       
    11 try:
       
    12     from shutil import copystat
       
    13 except ImportError:
       
    14     import stat
       
    15     def copystat(src, dst):
       
    16         """Copy all stat info (mode bits, atime and mtime) from src to dst"""
       
    17         st = os.stat(src)
       
    18         mode = stat.S_IMODE(st.st_mode)
       
    19         if hasattr(os, 'utime'):
       
    20             os.utime(dst, (st.st_atime, st.st_mtime))
       
    21         if hasattr(os, 'chmod'):
       
    22             os.chmod(dst, mode)
       
    23 
       
    24 __all__ = ['file_move_safe']
       
    25 
       
    26 def _samefile(src, dst):
       
    27     # Macintosh, Unix.
       
    28     if hasattr(os.path,'samefile'):
       
    29         try:
       
    30             return os.path.samefile(src, dst)
       
    31         except OSError:
       
    32             return False
       
    33 
       
    34     # All other platforms: check for same pathname.
       
    35     return (os.path.normcase(os.path.abspath(src)) ==
       
    36             os.path.normcase(os.path.abspath(dst)))
       
    37 
       
    38 def file_move_safe(old_file_name, new_file_name, chunk_size = 1024*64, allow_overwrite=False):
       
    39     """
       
    40     Moves a file from one location to another in the safest way possible.
       
    41 
       
    42     First, tries ``os.rename``, which is simple but will break across filesystems.
       
    43     If that fails, streams manually from one file to another in pure Python.
       
    44 
       
    45     If the destination file exists and ``allow_overwrite`` is ``False``, this
       
    46     function will throw an ``IOError``.
       
    47     """
       
    48 
       
    49     # There's no reason to move if we don't have to.
       
    50     if _samefile(old_file_name, new_file_name):
       
    51         return
       
    52 
       
    53     try:
       
    54         os.rename(old_file_name, new_file_name)
       
    55         return
       
    56     except OSError:
       
    57         # This will happen with os.rename if moving to another filesystem
       
    58         # or when moving opened files on certain operating systems
       
    59         pass
       
    60 
       
    61     # first open the old file, so that it won't go away
       
    62     old_file = open(old_file_name, 'rb')
       
    63     try:
       
    64         # now open the new file, not forgetting allow_overwrite
       
    65         fd = os.open(new_file_name, os.O_WRONLY | os.O_CREAT | getattr(os, 'O_BINARY', 0) |
       
    66                                     (not allow_overwrite and os.O_EXCL or 0))
       
    67         try:
       
    68             locks.lock(fd, locks.LOCK_EX)
       
    69             current_chunk = None
       
    70             while current_chunk != '':
       
    71                 current_chunk = old_file.read(chunk_size)
       
    72                 os.write(fd, current_chunk)
       
    73         finally:
       
    74             locks.unlock(fd)
       
    75             os.close(fd)
       
    76     finally:
       
    77         old_file.close()
       
    78     copystat(old_file_name, new_file_name)
       
    79 
       
    80     try:
       
    81         os.remove(old_file_name)
       
    82     except OSError, e:
       
    83         # Certain operating systems (Cygwin and Windows) 
       
    84         # fail when deleting opened files, ignore it.  (For the 
       
    85         # systems where this happens, temporary files will be auto-deleted
       
    86         # on close anyway.)
       
    87         if getattr(e, 'winerror', 0) != 32 and getattr(e, 'errno', 0) != 13:
       
    88             raise