Ticket #8203: django-win32-8203+samefile+copystat+test.patch

File django-win32-8203+samefile+copystat+test.patch, 6.5 KB (added by snaury, 7 years ago)

A more thorough implementation that has some necessary bits copied from shutil (plus removed forgotten import sys in django.core.files.temp)

  • django/core/files/move.py

    === modified file 'django/core/files/move.py'
     
    77
    88import os
    99from django.core.files import locks
    10 
    11 __all__ = ['file_move_safe']
    12 
    1310try:
    14     import shutil
    15     file_move = shutil.move
     11    from shutil import copystat
    1612except ImportError:
    17     file_move = os.rename
     13    def copystat(src, dst):
     14        """Copy all stat info (mode bits, atime and mtime) from src to dst"""
     15        st = os.stat(src)
     16        mode = stat.S_IMODE(st.st_mode)
     17        if hasattr(os, 'utime'):
     18            os.utime(dst, (st.st_atime, st.st_mtime))
     19        if hasattr(os, 'chmod'):
     20            os.chmod(dst, mode)
     21
     22__all__ = ['file_move_safe']
     23
     24def _samefile(src, dst):
     25    # Macintosh, Unix.
     26    if hasattr(os.path,'samefile'):
     27        try:
     28            return os.path.samefile(src, dst)
     29        except OSError:
     30            return False
     31
     32    # All other platforms: check for same pathname.
     33    return (os.path.normcase(os.path.abspath(src)) ==
     34            os.path.normcase(os.path.abspath(dst)))
    1835
    1936def file_move_safe(old_file_name, new_file_name, chunk_size = 1024*64, allow_overwrite=False):
    2037    """
     
    3047    """
    3148
    3249    # There's no reason to move if we don't have to.
    33     if old_file_name == new_file_name:
     50    if _samefile(old_file_name, new_file_name):
    3451        return
    3552
    36     if not allow_overwrite and os.path.exists(new_file_name):
    37         raise IOError("Cannot overwrite existing file '%s'." % new_file_name)
    38 
    3953    try:
    40         file_move(old_file_name, new_file_name)
     54        os.rename(old_file_name, new_file_name)
    4155        return
    4256    except OSError:
    4357        # This will happen with os.rename if moving to another filesystem
     58        # or when moving opened files on certain operating systems
    4459        pass
    4560
    46     # If the built-in didn't work, do it the hard way.
    47     fd = os.open(new_file_name, os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, 'O_BINARY', 0))
     61    # first open the old file, so that it won't go away
     62    old_file = open(old_file_name, 'rb')
    4863    try:
    49         locks.lock(fd, locks.LOCK_EX)
    50         old_file = open(old_file_name, 'rb')
    51         current_chunk = None
    52         while current_chunk != '':
    53             current_chunk = old_file.read(chunk_size)
    54             os.write(fd, current_chunk)
     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)
    5576    finally:
    56         locks.unlock(fd)
    57         os.close(fd)
    5877        old_file.close()
     78    copystat(old_file_name, new_file_name)
    5979
    60     os.remove(old_file_name)
     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
     85        if getattr(e, 'winerror', 0) != 32:
     86            # FIXME: should we also ignore errno 13?
     87            raise
  • django/core/files/temp.py

    === modified file 'django/core/files/temp.py'
     
    2525            fd, name = tempfile.mkstemp(suffix=suffix, prefix=prefix,
    2626                                          dir=dir)
    2727            self.name = name
    28             self._file = os.fdopen(fd, mode, bufsize)
     28            self.file = os.fdopen(fd, mode, bufsize)
     29            self.close_called = False
     30
     31        # Because close can be called during shutdown
     32        # we need to cache os.unlink and access it
     33        # as self.unlink only
     34        unlink = os.unlink
     35
     36        def close(self):
     37            if not self.close_called:
     38                self.close_called = True
     39                try:
     40                    self.file.close()
     41                except (OSError, IOError):
     42                    pass
     43                try:
     44                    self.unlink(self.name)
     45                except (OSError):
     46                    pass
    2947
    3048        def __del__(self):
    31             try:
    32                 self._file.close()
    33             except (OSError, IOError):
    34                 pass
    35             try:
    36                 os.unlink(self.name)
    37             except (OSError):
    38                 pass
    39 
    40             try:
    41                 super(TemporaryFile, self).__del__()
    42             except AttributeError:
    43                 pass
    44 
    45 
    46         def read(self, *args):          return self._file.read(*args)
    47         def seek(self, offset):         return self._file.seek(offset)
    48         def write(self, s):             return self._file.write(s)
    49         def close(self):                return self._file.close()
    50         def __iter__(self):             return iter(self._file)
    51         def readlines(self, size=None): return self._file.readlines(size)
    52         def xreadlines(self):           return self._file.xreadlines()
     49            self.close()
     50
     51        def read(self, *args):          return self.file.read(*args)
     52        def seek(self, offset):         return self.file.seek(offset)
     53        def write(self, s):             return self.file.write(s)
     54        def __iter__(self):             return iter(self.file)
     55        def readlines(self, size=None): return self.file.readlines(size)
     56        def xreadlines(self):           return self.file.xreadlines()
    5357
    5458    NamedTemporaryFile = TemporaryFile
    5559else:
  • tests/regressiontests/file_uploads/views.py

    === modified file 'tests/regressiontests/file_uploads/views.py'
     
    22from django.core.files.uploadedfile import UploadedFile
    33from django.http import HttpResponse, HttpResponseServerError
    44from django.utils import simplejson
     5from models import FileModel
    56from uploadhandler import QuotaUploadHandler
    67from django.utils.hashcompat import sha_constructor
    78
     
    4546        if new_hash != submitted_hash:
    4647            return HttpResponseServerError()
    4748
     49    # Adding large file to the database should succeed
     50    largefile = request.FILES['file_field2']
     51    obj = FileModel()
     52    obj.testfile.save(largefile.name, largefile)
     53
    4854    return HttpResponse('')
    4955
    5056def file_upload_echo(request):
Back to Top