Added option into collectstatic command to ignore post-processing errors #17

Open
wants to merge 11 commits into
from
View
@@ -50,6 +50,12 @@ Some commonly used options are:
method of the configured
:attr:`~django.conf.settings.STATICFILES_STORAGE` storage backend.
+``--ignore-errors``
+
+ .. versionadded:: 1.2
+
+ Ignore post-processing error raised on missing file.
+
For a full list of options, refer to the collectstatic management command help
by running::
View
@@ -33,7 +33,7 @@ static
.. versionadded:: 1.1
-Uses the configued :attr:`~django.conf.settings.STATICFILES_STORAGE` storage
+Uses the configured :attr:`~django.conf.settings.STATICFILES_STORAGE` storage
to create the full URL for the given relative path, e.g.::
{% load staticfiles %}
View
@@ -5,9 +5,11 @@
from distutils.util import convert_path
from setuptools import setup, find_packages
+
def read(*parts):
return open(os.path.join(os.path.dirname(__file__), *parts)).read()
+
def find_version(*file_paths):
version_file = read(*file_paths)
version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
@@ -22,6 +24,7 @@ def find_version(*file_paths):
standard_exclude_directories = ('.*', 'CVS', '_darcs', './build',
'./dist', 'EGG-INFO', '*.egg-info')
+
# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
# Note: you may want to copy this into your setup.py file verbatim, as
@@ -102,11 +105,10 @@ def find_package_data(
break
if bad_name:
continue
- out.setdefault(package, []).append(prefix+name)
+ out.setdefault(package, []).append(prefix + name)
return out
-
setup(
name="django-staticfiles",
version=find_version("staticfiles", "__init__.py"),
@@ -43,6 +43,9 @@ class Command(NoArgsCommand):
dest='use_default_ignore_patterns', default=True,
help="Don't ignore the common private glob-style patterns 'CVS', "
"'.*' and '*~'."),
+ make_option('--ignore-errors', action='store_true',
+ dest='fail_silently', default=False,
+ help="Ignore post-processing error raised on missing file."),
)
help = "Collect static files in a single location."
requires_model_validation = False
@@ -79,6 +82,7 @@ def set_options(self, **options):
ignore_patterns += ['CVS', '.*', '*~']
self.ignore_patterns = list(set(ignore_patterns))
self.post_process = options['post_process']
+ self.fail_silently = options['fail_silently']
def collect(self):
"""
@@ -109,14 +113,16 @@ def collect(self):
prefixed_path = os.path.join(storage.prefix, path)
else:
prefixed_path = path
- found_files[prefixed_path] = (storage, path)
- handler(path, prefixed_path, storage)
+ # Process only not already processed files.
+ if prefixed_path not in found_files:
+ found_files[prefixed_path] = (storage, path)
+ handler(path, prefixed_path, storage)
# Here we check if the storage backend has a post_process
# method and pass it the list of modified files.
if self.post_process and hasattr(self.storage, 'post_process'):
processor = self.storage.post_process(found_files,
- dry_run=self.dry_run)
+ dry_run=self.dry_run, fail_silently=self.fail_silently)
for original_path, processed_path, processed in processor:
if processed:
self.log(u"Post-processed '%s' as '%s" %
View
@@ -177,7 +177,7 @@ def url(self, name, force=False):
return unquote(final_url)
- def url_converter(self, name):
+ def url_converter(self, name, fail_silently=False):
"""
Returns the custom URL converter for the given file name.
"""
@@ -210,13 +210,17 @@ def converter(matchobj):
else:
start, end = 1, sub_level - 1
joined_result = '/'.join(name_parts[:-start] + url_parts[end:])
- hashed_url = self.url(unquote(joined_result), force=True)
-
+ try:
+ hashed_url = self.url(unquote(joined_result), force=True)
+ except ValueError:
+ if not fail_silently:
+ raise
+ hashed_url = url
# Return the hashed and normalized version to the file
return 'url("%s")' % unquote(hashed_url)
return converter
- def post_process(self, paths, dry_run=False, **options):
+ def post_process(self, paths, dry_run=False, fail_silently=False):
"""
Post process the given list of files (called from collectstatic).
@@ -264,7 +268,7 @@ def post_process(self, paths, dry_run=False, **options):
# ..to apply each replacement pattern to the content
if name in adjustable_paths:
content = original_file.read()
- converter = self.url_converter(name)
+ converter = self.url_converter(name, fail_silently)
for patterns in self._patterns.values():
for pattern in patterns:
content = pattern.sub(converter, content)
@@ -0,0 +1 @@
+@import url("img/does_not_exists.png");
View
@@ -137,7 +137,7 @@ def run_collectstatic(self, **kwargs):
'*.ignoreme', os.path.join('test', '*.ignoreme2'), os.path.join(
settings.TEST_ROOT, 'apps', 'test', 'static', 'test', '*.ignoreme3')]
call_command('collectstatic', interactive=False, verbosity='0',
- ignore_patterns=ignore_patterns, **kwargs)
+ ignore_patterns=ignore_patterns, fail_silently=True, **kwargs)
def _get_file(self, filepath):
assert filepath, 'filepath is empty.'
@@ -268,7 +268,7 @@ def test_staticfiles_ignore_patterns(self):
class TestCollectionClear(CollectionTestCase):
"""
- Test the ``--clear`` option of the ``collectstatic`` managemenet command.
+ Test the ``--clear`` option of the ``collectstatic`` management command.
"""
def run_collectstatic(self, **kwargs):
clear_filepath = os.path.join(settings.STATIC_ROOT, 'cleared.txt')
@@ -361,7 +361,7 @@ def test_template_tag_return(self):
"does/not/exist.png",
"/static/does/not/exist.png")
self.assertStaticRenders("test/file.txt",
- "/static/test/file.ea5bccaf16d5.txt")
+ "/static/test/file.dad0999e4f8f.txt")
self.assertStaticRenders("cached/styles.css",
"/static/cached/styles.93b1147e8552.css")
@@ -459,6 +459,22 @@ def test_cache_invalidation(self):
cached_name = storage.staticfiles_storage.cache.get(cache_key)
self.assertEqual(cached_name, hashed_name)
+ def test_ignored_file(self):
+ relpath = self.cached_file_path("cached/faulty.css")
+ self.assertEqual(relpath, "cached/faulty.c376691faf10.css")
+ with storage.staticfiles_storage.open(relpath) as relfile:
+ content = relfile.read()
+ self.assertIn('@import url("img/does_not_exists.png");', content)
+
+ def test_path_with_precedence(self):
+ relpath = self.cached_file_path("test/file.txt")
+ self.assertEqual(relpath, "test/file.dad0999e4f8f.txt")
+ with storage.staticfiles_storage.open(
+ "test/file.dad0999e4f8f.txt") as relfile:
+ content = relfile.read()
+ self.assertNotIn("In app media directory.", content)
+ self.assertIn("In STATICFILES_DIRS directory.", content)
+
def test_post_processing(self):
"""Test that post_processing behaves correctly.
@@ -476,7 +492,8 @@ def test_post_processing(self):
'dry_run': False,
'post_process': True,
'use_default_ignore_patterns': True,
- 'ignore_patterns': ['*.ignoreme']
+ 'ignore_patterns': ['*.ignoreme'],
+ 'fail_silently': True,
}
collectstatic_cmd = CollectstaticCommand()
@@ -485,6 +502,28 @@ def test_post_processing(self):
self.assertTrue(u'cached/css/window.css' in stats['post_processed'])
self.assertTrue(u'cached/css/img/window.png' in stats['unmodified'])
+ def test_post_processing_fail(self):
+ """Test that post_processing behaves correctly.
+
+ Missing files raise a ValueError on post-processing when errors aren't
+ explicitely silenced.
+ """
+ collectstatic_args = {
+ 'interactive': False,
+ 'verbosity': '0',
+ 'link': False,
+ 'clear': False,
+ 'dry_run': False,
+ 'post_process': True,
+ 'use_default_ignore_patterns': True,
+ 'ignore_patterns': ['*.ignoreme'],
+ 'fail_silently': False,
+ }
+
+ collectstatic_cmd = CollectstaticCommand()
+ collectstatic_cmd.set_options(**collectstatic_args)
+ self.assertRaises(ValueError, collectstatic_cmd.collect)
+
if sys.platform != 'win32':
class TestCollectionLinks(CollectionTestCase, TestDefaults):