Ticket #15190: 15190.1.diff

File 15190.1.diff, 10.8 KB (added by Jannis Leidel, 13 years ago)

refactored collectstatic command

  • django/contrib/staticfiles/management/commands/collectstatic.py

    diff --git a/django/contrib/staticfiles/management/commands/collectstatic.py b/django/contrib/staticfiles/management/commands/collectstatic.py
    index 25f683f..10a1441 100644
    a b class Command(NoArgsCommand):  
    3434
    3535    def __init__(self, *args, **kwargs):
    3636        super(NoArgsCommand, self).__init__(*args, **kwargs)
    37         self.copied_files = set()
    38         self.symlinked_files = set()
    39         self.unmodified_files = set()
    40         self.destination_storage = get_storage_class(settings.STATICFILES_STORAGE)()
     37        self.copied_files = []
     38        self.symlinked_files = []
     39        self.unmodified_files = []
     40        self.storage = get_storage_class(settings.STATICFILES_STORAGE)()
    4141        try:
    42             self.destination_storage.path('')
     42            self.storage.path('')
    4343        except NotImplementedError:
    44             self.destination_local = False
     44            self.local = False
    4545        else:
    46             self.destination_local = True
     46            self.local = True
    4747        # Use ints for file times (ticket #14665)
    4848        os.stat_float_times(False)
    4949
    class Command(NoArgsCommand):  
    5959            if sys.platform == 'win32':
    6060                raise CommandError("Symlinking is not supported by this "
    6161                                   "platform (%s)." % sys.platform)
    62             if not self.destination_local:
     62            if not self.local:
    6363                raise CommandError("Can't symlink to a remote destination.")
    6464
    6565        # Warn before doing anything more.
    6666        if options.get('interactive'):
    6767            confirm = raw_input("""
    68 You have requested to collate static files and collect them at the destination
    69 location as specified in your settings file.
     68You have requested to collect static files at the destination
     69location as specified in your settings file ('%s').
    7070
    7171This will overwrite existing files.
    7272Are you sure you want to do this?
    7373
    74 Type 'yes' to continue, or 'no' to cancel: """)
     74Type 'yes' to continue, or 'no' to cancel: """ % settings.STATIC_ROOT)
    7575            if confirm != 'yes':
    7676                raise CommandError("Collecting static files cancelled.")
    7777
    7878        for finder in finders.get_finders():
    79             for source, storage in finder.list(ignore_patterns):
    80                 self.copy_file(source, storage, **options)
     79            for path, storage in finder.list(ignore_patterns):
     80                # Prefix the relative path if the source storage contains it
     81                if getattr(storage, 'prefix', None):
     82                    prefixed_path = os.path.join(storage.prefix, path)
     83                else:
     84                    prefixed_path = path
     85                if symlink:
     86                    self.link_file(path, prefixed_path, storage, **options)
     87                else:
     88                    self.copy_file(path, prefixed_path, storage, **options)
    8189
    8290        actual_count = len(self.copied_files) + len(self.symlinked_files)
    8391        unmodified_count = len(self.unmodified_files)
    Type 'yes' to continue, or 'no' to cancel: """)  
    98106        if self.verbosity >= level:
    99107            self.stdout.write(msg)
    100108
    101     def copy_file(self, source, source_storage, **options):
    102         """
    103         Attempt to copy (or symlink) ``source`` to ``destination``,
    104         returning True if successful.
    105         """
    106         source_path = source_storage.path(source)
    107         try:
    108             source_last_modified = source_storage.modified_time(source)
    109         except (OSError, NotImplementedError):
    110             source_last_modified = None
    111         if getattr(source_storage, 'prefix', None):
    112             destination = os.path.join(source_storage.prefix, source)
    113         else:
    114             destination = source
     109    def delete_file(self, path, prefixed_path, source_storage, **options):
     110        # Whether we are in symlink mode
    115111        symlink = options['link']
    116         dry_run = options['dry_run']
    117 
    118         if destination in self.copied_files:
    119             self.log("Skipping '%s' (already copied earlier)" % destination)
    120             return False
    121 
    122         if destination in self.symlinked_files:
    123             self.log("Skipping '%s' (already linked earlier)" % destination)
    124             return False
    125 
    126         if self.destination_storage.exists(destination):
     112        # Checks if the target file should be deleted if it already exists
     113        if self.storage.exists(prefixed_path):
    127114            try:
    128                 destination_last_modified = \
    129                     self.destination_storage.modified_time(destination)
     115                # When was the target file modified last time?
     116                target_last_modified = self.storage.modified_time(prefixed_path)
    130117            except (OSError, NotImplementedError):
    131                 # storage doesn't support ``modified_time`` or failed.
     118                # The storage doesn't support ``modified_time`` or failed
    132119                pass
    133120            else:
    134                 destination_is_link = (self.destination_local and
    135                     os.path.islink(self.destination_storage.path(destination)))
    136                 if destination_last_modified >= source_last_modified:
    137                     if (not symlink and not destination_is_link):
    138                         self.log("Skipping '%s' (not modified)" % destination)
    139                         self.unmodified_files.add(destination)
    140                         return False
    141             if dry_run:
    142                 self.log("Pretending to delete '%s'" % destination)
     121                try:
     122                    # When was the source file modified last time?
     123                    source_last_modified = source_storage.modified_time(path)
     124                except (OSError, NotImplementedError):
     125                    pass
     126                else:
     127                    # The full path of the target file
     128                    full_path = self.storage.path(prefixed_path)
     129                    # Skip the file if the source file is younger
     130                    if target_last_modified >= source_last_modified:
     131                        if not ((symlink and not os.path.islink(full_path)) or
     132                                (not symlink and os.path.islink(full_path))):
     133                            if prefixed_path not in self.unmodified_files:
     134                                self.unmodified_files.append(prefixed_path)
     135                            self.log("Skipping '%s' (not modified)" % path)
     136                            return False
     137            # Then delete the existing file if really needed
     138            if options['dry_run']:
     139                self.log("Pretending to delete '%s'" % path)
    143140            else:
    144                 self.log("Deleting '%s'" % destination)
    145                 self.destination_storage.delete(destination)
     141                self.log("Deleting '%s'" % path)
     142                self.storage.delete(prefixed_path)
     143        return True
    146144
    147         if symlink:
    148             destination_path = self.destination_storage.path(destination)
    149             if dry_run:
    150                 self.log("Pretending to link '%s' to '%s'" %
    151                          (source_path, destination_path), level=1)
    152             else:
    153                 self.log("Linking '%s' to '%s'" %
    154                          (source_path, destination_path), level=1)
     145    def link_file(self, path, prefixed_path, source_storage, **options):
     146        """
     147        Attempt to link ``path``
     148        """
     149        # Skip this file if it was already copied earlier
     150        if prefixed_path in self.symlinked_files:
     151            return self.log("Skipping '%s' (already linked earlier)" % path)
     152        # Delete the target file if needed or break
     153        if not self.delete_file(path, prefixed_path, source_storage, **options):
     154            return
     155        # The full path of the source file
     156        source_path = source_storage.path(path)
     157        # Finally link the file
     158        if options['dry_run']:
     159            self.log("Pretending to link '%s'" % source_path, level=1)
     160        else:
     161            self.log("Linking '%s'" % source_path, level=1)
     162            full_path = self.storage.path(prefixed_path)
     163            try:
     164                os.makedirs(os.path.dirname(full_path))
     165            except OSError:
     166                pass
     167            os.symlink(source_path, full_path)
     168        if prefixed_path not in self.symlinked_files:
     169            self.symlinked_files.append(prefixed_path)
     170
     171    def copy_file(self, path, prefixed_path, source_storage, **options):
     172        """
     173        Attempt to copy ``path`` with storage
     174        """
     175        # Skip this file if it was already copied earlier
     176        if prefixed_path in self.copied_files:
     177            return self.log("Skipping '%s' (already copied earlier)" % path)
     178        # Delete the target file if needed or break
     179        if not self.delete_file(path, prefixed_path, source_storage, **options):
     180            return
     181        # The full path of the source file
     182        source_path = source_storage.path(path)
     183        # Finally start copying
     184        if options['dry_run']:
     185            self.log("Pretending to copy '%s'" % source_path, level=1)
     186        else:
     187            self.log("Copying '%s'" % source_path, level=1)
     188            if self.local:
     189                full_path = self.storage.path(prefixed_path)
    155190                try:
    156                     os.makedirs(os.path.dirname(destination_path))
     191                    os.makedirs(os.path.dirname(full_path))
    157192                except OSError:
    158193                    pass
    159                 os.symlink(source_path, destination_path)
    160             self.symlinked_files.add(destination)
    161         else:
    162             if dry_run:
    163                 self.log("Pretending to copy '%s' to '%s'" %
    164                          (source_path, destination), level=1)
     194                shutil.copy2(source_path, full_path)
    165195            else:
    166                 if self.destination_local:
    167                     destination_path = self.destination_storage.path(destination)
    168                     try:
    169                         os.makedirs(os.path.dirname(destination_path))
    170                     except OSError:
    171                         pass
    172                     shutil.copy2(source_path, destination_path)
    173                     self.log("Copying '%s' to '%s'" %
    174                              (source_path, destination_path), level=1)
    175                 else:
    176                     source_file = source_storage.open(source)
    177                     self.destination_storage.save(destination, source_file)
    178                     self.log("Copying %s to %s" %
    179                              (source_path, destination), level=1)
    180             self.copied_files.add(destination)
    181         return True
     196                source_file = source_storage.open(path)
     197                self.storage.save(prefixed_path, source_file)
     198        if not prefixed_path in self.copied_files:
     199            self.copied_files.append(prefixed_path)
Back to Top