Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #15035 -- Fixed collectstatic management command to work with n…

…on-local storage backends correctly. Also refactored a bit code smell.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@15154 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 19ab52f77fef0edb1e4101e7f8acaa093d3ca16c 1 parent a389494
Jannis Leidel authored January 08, 2011
82  django/contrib/staticfiles/management/commands/collectstatic.py
@@ -32,23 +32,27 @@ class Command(NoArgsCommand):
32 32
     )
33 33
     help = "Collect static files from apps and other locations in a single location."
34 34
 
35  
-    def handle_noargs(self, **options):
36  
-        symlink = options['link']
37  
-        ignore_patterns = options['ignore_patterns']
38  
-        if options['use_default_ignore_patterns']:
39  
-            ignore_patterns += ['CVS', '.*', '*~']
40  
-        ignore_patterns = list(set(ignore_patterns))
  35
+    def __init__(self, *args, **kwargs):
41 36
         self.copied_files = set()
42 37
         self.symlinked_files = set()
43 38
         self.unmodified_files = set()
44 39
         self.destination_storage = get_storage_class(settings.STATICFILES_STORAGE)()
45  
-
46 40
         try:
47 41
             self.destination_storage.path('')
48 42
         except NotImplementedError:
49 43
             self.destination_local = False
50 44
         else:
51 45
             self.destination_local = True
  46
+        # Use ints for file times (ticket #14665)
  47
+        os.stat_float_times(False)
  48
+
  49
+    def handle_noargs(self, **options):
  50
+        symlink = options['link']
  51
+        ignore_patterns = options['ignore_patterns']
  52
+        if options['use_default_ignore_patterns']:
  53
+            ignore_patterns += ['CVS', '.*', '*~']
  54
+        ignore_patterns = list(set(ignore_patterns))
  55
+        self.verbosity = int(options.get('verbosity', 1))
52 56
 
53 57
         if symlink:
54 58
             if sys.platform == 'win32':
@@ -70,17 +74,13 @@ def handle_noargs(self, **options):
70 74
             if confirm != 'yes':
71 75
                 raise CommandError("Collecting static files cancelled.")
72 76
 
73  
-        # Use ints for file times (ticket #14665)
74  
-        os.stat_float_times(False)
75  
-
76 77
         for finder in finders.get_finders():
77 78
             for source, prefix, storage in finder.list(ignore_patterns):
78 79
                 self.copy_file(source, prefix, storage, **options)
79 80
 
80  
-        verbosity = int(options.get('verbosity', 1))
81 81
         actual_count = len(self.copied_files) + len(self.symlinked_files)
82 82
         unmodified_count = len(self.unmodified_files)
83  
-        if verbosity >= 1:
  83
+        if self.verbosity >= 1:
84 84
             self.stdout.write("\n%s static file%s %s to '%s'%s.\n"
85 85
                               % (actual_count, actual_count != 1 and 's' or '',
86 86
                                  symlink and 'symlinked' or 'copied',
@@ -88,6 +88,15 @@ def handle_noargs(self, **options):
88 88
                                  unmodified_count and ' (%s unmodified)'
89 89
                                  % unmodified_count or ''))
90 90
 
  91
+    def log(self, msg, level=2):
  92
+        """
  93
+        Small log helper
  94
+        """
  95
+        if not msg.endswith("\n"):
  96
+            msg += "\n"
  97
+        if self.verbosity >= level:
  98
+            self.stdout.write(msg)
  99
+
91 100
     def copy_file(self, source, prefix, source_storage, **options):
92 101
         """
93 102
         Attempt to copy (or symlink) ``source`` to ``destination``,
@@ -104,18 +113,13 @@ def copy_file(self, source, prefix, source_storage, **options):
104 113
             destination = source
105 114
         symlink = options['link']
106 115
         dry_run = options['dry_run']
107  
-        verbosity = int(options.get('verbosity', 1))
108 116
 
109 117
         if destination in self.copied_files:
110  
-            if verbosity >= 2:
111  
-                self.stdout.write("Skipping '%s' (already copied earlier)\n"
112  
-                                  % destination)
  118
+            self.log("Skipping '%s' (already copied earlier)" % destination)
113 119
             return False
114 120
 
115 121
         if destination in self.symlinked_files:
116  
-            if verbosity >= 2:
117  
-                self.stdout.write("Skipping '%s' (already linked earlier)\n"
118  
-                                  % destination)
  122
+            self.log("Skipping '%s' (already linked earlier)" % destination)
119 123
             return False
120 124
 
121 125
         if self.destination_storage.exists(destination):
@@ -126,34 +130,27 @@ def copy_file(self, source, prefix, source_storage, **options):
126 130
                 # storage doesn't support ``modified_time`` or failed.
127 131
                 pass
128 132
             else:
129  
-                destination_is_link = os.path.islink(
130  
-                    self.destination_storage.path(destination))
  133
+                destination_is_link = (self.destination_local and
  134
+                    os.path.islink(self.destination_storage.path(destination)))
131 135
                 if destination_last_modified >= source_last_modified:
132 136
                     if (not symlink and not destination_is_link):
133  
-                        if verbosity >= 2:
134  
-                            self.stdout.write("Skipping '%s' (not modified)\n"
135  
-                                              % destination)
  137
+                        self.log("Skipping '%s' (not modified)" % destination)
136 138
                         self.unmodified_files.add(destination)
137 139
                         return False
138 140
             if dry_run:
139  
-                if verbosity >= 2:
140  
-                    self.stdout.write("Pretending to delete '%s'\n"
141  
-                                      % destination)
  141
+                self.log("Pretending to delete '%s'" % destination)
142 142
             else:
143  
-                if verbosity >= 2:
144  
-                    self.stdout.write("Deleting '%s'\n" % destination)
  143
+                self.log("Deleting '%s'" % destination)
145 144
                 self.destination_storage.delete(destination)
146 145
 
147 146
         if symlink:
148 147
             destination_path = self.destination_storage.path(destination)
149 148
             if dry_run:
150  
-                if verbosity >= 1:
151  
-                    self.stdout.write("Pretending to symlink '%s' to '%s'\n"
152  
-                                      % (source_path, destination_path))
  149
+                self.log("Pretending to link '%s' to '%s'" %
  150
+                         (source_path, destination_path), level=1)
153 151
             else:
154  
-                if verbosity >= 1:
155  
-                    self.stdout.write("Symlinking '%s' to '%s'\n"
156  
-                                      % (source_path, destination_path))
  152
+                self.log("Linking '%s' to '%s'" %
  153
+                         (source_path, destination_path), level=1)
157 154
                 try:
158 155
                     os.makedirs(os.path.dirname(destination_path))
159 156
                 except OSError:
@@ -162,9 +159,8 @@ def copy_file(self, source, prefix, source_storage, **options):
162 159
             self.symlinked_files.add(destination)
163 160
         else:
164 161
             if dry_run:
165  
-                if verbosity >= 1:
166  
-                    self.stdout.write("Pretending to copy '%s' to '%s'\n"
167  
-                                      % (source_path, destination))
  162
+                self.log("Pretending to copy '%s' to '%s'" %
  163
+                         (source_path, destination), level=1)
168 164
             else:
169 165
                 if self.destination_local:
170 166
                     destination_path = self.destination_storage.path(destination)
@@ -173,14 +169,12 @@ def copy_file(self, source, prefix, source_storage, **options):
173 169
                     except OSError:
174 170
                         pass
175 171
                     shutil.copy2(source_path, destination_path)
176  
-                    if verbosity >= 1:
177  
-                        self.stdout.write("Copying '%s' to '%s'\n"
178  
-                                          % (source_path, destination_path))
  172
+                    self.log("Copying '%s' to '%s'" %
  173
+                             (source_path, destination_path), level=1)
179 174
                 else:
180 175
                     source_file = source_storage.open(source)
181 176
                     self.destination_storage.save(destination, source_file)
182  
-                    if verbosity >= 1:
183  
-                        self.stdout.write("Copying %s to %s\n"
184  
-                                          % (source_path, destination))
  177
+                    self.log("Copying %s to %s" %
  178
+                             (source_path, destination), level=1)
185 179
             self.copied_files.add(destination)
186 180
         return True
19  tests/regressiontests/staticfiles_tests/storage.py
... ...
@@ -0,0 +1,19 @@
  1
+from datetime import datetime
  2
+from django.core.files import storage
  3
+
  4
+class DummyStorage(storage.Storage):
  5
+    """
  6
+    A storage class that does implement modified_time() but raises
  7
+    NotImplementedError when calling 
  8
+    """
  9
+    def _save(self, name, content):
  10
+        return 'dummy'
  11
+
  12
+    def delete(self, name):
  13
+        pass
  14
+
  15
+    def exists(self, name):
  16
+        pass
  17
+
  18
+    def modified_time(self, name):
  19
+        return datetime.date(1970, 1, 1)
30  tests/regressiontests/staticfiles_tests/tests.py
@@ -92,7 +92,6 @@ class BuildStaticTestCase(StaticFilesTestCase):
92 92
     """
93 93
     def setUp(self):
94 94
         super(BuildStaticTestCase, self).setUp()
95  
-        self.old_staticfiles_storage = settings.STATICFILES_STORAGE
96 95
         self.old_root = settings.STATIC_ROOT
97 96
         settings.STATIC_ROOT = tempfile.mkdtemp()
98 97
         self.run_collectstatic()
@@ -220,18 +219,35 @@ def test_no_common_ignore_patterns(self):
220 219
         self.assertFileContains('test/CVS', 'should be ignored')
221 220
 
222 221
 
223  
-class TestBuildStaticDryRun(BuildStaticTestCase):
  222
+class TestNoFilesCreated(object):
  223
+
  224
+    def test_no_files_created(self):
  225
+        """
  226
+        Make sure no files were create in the destination directory.
  227
+        """
  228
+        self.assertEquals(os.listdir(settings.STATIC_ROOT), [])
  229
+
  230
+
  231
+class TestBuildStaticDryRun(BuildStaticTestCase, TestNoFilesCreated):
224 232
     """
225 233
     Test ``--dry-run`` option for ``collectstatic`` management command.
226 234
     """
227 235
     def run_collectstatic(self):
228 236
         super(TestBuildStaticDryRun, self).run_collectstatic(dry_run=True)
229 237
 
230  
-    def test_no_files_created(self):
231  
-        """
232  
-        With --dry-run, no files created in destination dir.
233  
-        """
234  
-        self.assertEquals(os.listdir(settings.STATIC_ROOT), [])
  238
+
  239
+class TestBuildStaticNonLocalStorage(BuildStaticTestCase, TestNoFilesCreated):
  240
+    """
  241
+    Tests for #15035
  242
+    """
  243
+    def setUp(self):
  244
+        self.old_staticfiles_storage = settings.STATICFILES_STORAGE
  245
+        settings.STATICFILES_STORAGE = 'regressiontests.staticfiles_tests.storage.DummyStorage'
  246
+        super(TestBuildStaticNonLocalStorage, self).setUp()
  247
+
  248
+    def tearDown(self):
  249
+        super(TestBuildStaticNonLocalStorage, self).tearDown()
  250
+        settings.STATICFILES_STORAGE = self.old_staticfiles_storage
235 251
 
236 252
 
237 253
 if sys.platform != 'win32':

0 notes on commit 19ab52f

Please sign in to comment.
Something went wrong with that request. Please try again.