Ticket #15252: 15252.2.2.diff
File 15252.2.2.diff, 11.8 KB (added by , 13 years ago) |
---|
-
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 6038cbc..5c9fd4b 100644
a b Type 'yes' to continue, or 'no' to cancel: """ 110 110 path = os.path.join(storage.prefix, path) 111 111 handler(path, path, storage) 112 112 113 actual_count = len(self.copied_files) + len(self.symlinked_files) 113 modified_files = self.copied_files + self.symlinked_files 114 actual_count = len(modified_files) 115 116 # Here we check if the storage backend has a post_process method 117 # and pass it the list of modified files, if possible. 118 if hasattr(self.storage, 'post_process'): 119 self.storage.post_process(modified_files) 120 114 121 unmodified_count = len(self.unmodified_files) 115 122 if self.verbosity >= 1: 116 123 self.stdout.write(smart_str(u"\n%s static file%s %s %s%s.\n" -
django/contrib/staticfiles/storage.py
diff --git a/django/contrib/staticfiles/storage.py b/django/contrib/staticfiles/storage.py index a69ae23..1942385 100644
a b 1 import hashlib 1 2 import os 3 import re 4 2 5 from django.conf import settings 3 from django.core.exceptions import ImproperlyConfigured 4 from django.core.files.storage import FileSystemStorage 6 from django.core.cache import get_cache, InvalidCacheBackendError, cache as default_cache 7 from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation 8 from django.core.files.storage import FileSystemStorage, get_storage_class 9 from django.core.files.base import ContentFile 10 from django.template import Template, Context 5 11 from django.utils.importlib import import_module 12 from django.utils.encoding import force_unicode 13 from django.utils.functional import LazyObject 14 15 from django.contrib.staticfiles.utils import check_settings 16 6 17 7 from django.contrib.staticfiles import utils 18 urltag_re = re.compile(r""" 19 url\( 20 (\s*) # allow whitespace wrapping (and capture) 21 ( # capture actual url 22 [^\)\\\r\n]*? # don't allow newlines, closing paran, escape chars (1) 23 (?:\\. # process all escapes here instead 24 [^\)\\\r\n]*? # proceed, with previous restrictions (1) 25 )* # repeat until end 26 ) 27 (\s*) # whitespace again (and capture) 28 \) 29 """, re.VERBOSE) 8 30 9 31 10 32 class StaticFilesStorage(FileSystemStorage): … … class StaticFilesStorage(FileSystemStorage): 26 48 if base_url is None: 27 49 raise ImproperlyConfigured("You're using the staticfiles app " 28 50 "without having set the STATIC_URL setting.") 29 utils.check_settings()51 check_settings() 30 52 super(StaticFilesStorage, self).__init__(location, base_url, *args, **kwargs) 31 53 32 54 55 class CacheBustingMixin(object): 56 57 def __init__(self, *args, **kwargs): 58 super(CacheBustingMixin, self).__init__(*args, **kwargs) 59 self.processed_files = [] 60 try: 61 self.cache = get_cache('staticfiles') 62 except InvalidCacheBackendError: 63 # Use the default backend 64 self.cache = default_cache 65 66 def hashed_filename(self, name, content=None): 67 if content is None: 68 if not self.exists(name): 69 raise SuspiciousOperation("Attempted access to '%s' denied." % self.path(name)) 70 content = self.open(self.path(name)) 71 path, filename = os.path.split(name) 72 root, ext = os.path.splitext(filename) 73 # Get the MD5 hash of the file 74 md5 = hashlib.md5() 75 for chunk in content.chunks(): 76 md5.update(chunk) 77 md5sum = md5.hexdigest()[:12] 78 return os.path.join(path, u"%s.%s%s" % (root, md5sum, ext)) 79 80 def cache_key(self, name): 81 return 'staticfiles:cache:%s' % name 82 83 def url(self, name): 84 cache_key = self.cache_key(name) 85 hashed_name = self.cache.get(cache_key, self.hashed_filename(name)) 86 return super(CacheBustingMixin, self).url(hashed_name) 87 88 def save(self, name, content): 89 original_name = super(CacheBustingMixin, self).save(name, content) 90 hashed_name = self.hashed_filename(original_name, content) 91 # Return the name if the file is already there 92 if os.path.exists(hashed_name): 93 return hashed_name 94 # Save the file 95 rendered_content = Template(content.read()).render(Context({})) 96 hashed_name = self._save(hashed_name, ContentFile(rendered_content)) 97 # Use filenames with forward slashes, even on Windows 98 hashed_name = force_unicode(hashed_name.replace('\\', '/')) 99 self.cache.set(self.cache_key(name), hashed_name) 100 self.processed_files.append((name, hashed_name)) 101 return hashed_name 102 103 def post_process(self, modified_files): 104 """ 105 Post process method called by the collectstatic management command. 106 """ 107 cached_files = [self.cache_key(path) for path in modified_files] 108 self.cache.delete_many(cached_files) 109 110 def path_level((name, hashed_name)): 111 return len(name.split(os.sep)) 112 113 for name, hashed_name in sorted( 114 self.processed_files, key=path_level, reverse=True): 115 116 def url_converter(matchobj): 117 url = matchobj.groups()[1] 118 # normalize the url we got 119 if url[:1] in '"\'': 120 url = url[1:] 121 if url[-1:] in '"\'': 122 url = url[:-1] 123 rel_level = url.count(os.pardir) 124 if rel_level: 125 url_parts = (name.split('/')[:-rel_level-1] + 126 url.split('/')[rel_level:]) 127 url = self.url('/'.join(url_parts)) 128 return "url('%s')" % url 129 130 original = self.open(name) 131 converted = urltag_re.sub(url_converter, original.read()) 132 hashed = self.path(hashed_name) 133 with open(hashed, 'w') as hashed_file: 134 hashed_file.write(converted) 135 136 class CachedStaticFilesStorage(CacheBustingMixin, StaticFilesStorage): 137 pass 138 139 33 140 class AppStaticStorage(FileSystemStorage): 34 141 """ 35 142 A file system storage backend that takes an app module and works … … class AppStaticStorage(FileSystemStorage): 47 154 mod_path = os.path.dirname(mod.__file__) 48 155 location = os.path.join(mod_path, self.source_dir) 49 156 super(AppStaticStorage, self).__init__(location, *args, **kwargs) 157 158 159 160 class ConfiguredStorage(LazyObject): 161 def _setup(self): 162 self._wrapped = get_storage_class(settings.STATICFILES_STORAGE)() 163 164 configured_storage = ConfiguredStorage() -
new file django/contrib/staticfiles/templatetags/staticfiles.py
diff --git a/django/contrib/staticfiles/templatetags/__init__.py b/django/contrib/staticfiles/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/django/contrib/staticfiles/templatetags/staticfiles.py b/django/contrib/staticfiles/templatetags/staticfiles.py new file mode 100644 index 0000000..42b4f6b
- + 1 from django import template 2 from django.contrib.staticfiles import storage 3 4 register = template.Library() 5 6 7 @register.simple_tag 8 def static(path): 9 """ 10 A template tag that returns the URL to a file 11 using staticfiles' storage backend 12 """ 13 return storage.configured_storage.url(path) -
django/core/cache/__init__.py
diff --git a/django/core/cache/__init__.py b/django/core/cache/__init__.py index b76ddcf..b97c746 100644
a b def parse_backend_conf(backend, **kwargs): 126 126 location = args.pop('LOCATION', '') 127 127 return backend, location, args 128 128 else: 129 # Trying to import the given backend, in case it's a dotted path130 mod_path, cls_name = backend.rsplit('.', 1)131 129 try: 130 # Trying to import the given backend, in case it's a dotted path 131 mod_path, cls_name = backend.rsplit('.', 1) 132 132 mod = importlib.import_module(mod_path) 133 133 backend_cls = getattr(mod, cls_name) 134 except (AttributeError, ImportError ):134 except (AttributeError, ImportError, ValueError): 135 135 raise InvalidCacheBackendError("Could not find backend '%s'" % backend) 136 136 location = kwargs.pop('LOCATION', '') 137 137 return backend, location, kwargs -
new file tests/regressiontests/staticfiles_tests/project/site_media/static/testfile.txt
diff --git a/tests/regressiontests/staticfiles_tests/project/site_media/static/testfile.txt b/tests/regressiontests/staticfiles_tests/project/site_media/static/testfile.txt new file mode 100644 index 0000000..4d92dbe
- + 1 Test! 2 No newline at end of file -
tests/regressiontests/staticfiles_tests/tests.py
diff --git a/tests/regressiontests/staticfiles_tests/tests.py b/tests/regressiontests/staticfiles_tests/tests.py index 8a60126..6cba626 100644
a b import sys 8 8 import tempfile 9 9 from StringIO import StringIO 10 10 11 from django.template import loader, Context 11 12 from django.conf import settings 12 from django.core.exceptions import ImproperlyConfigured 13 from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation 13 14 from django.core.files.storage import default_storage 14 15 from django.core.management import call_command 15 16 from django.test import TestCase … … class StaticFilesTestCase(TestCase): 48 49 def assertFileNotFound(self, filepath): 49 50 self.assertRaises(IOError, self._get_file, filepath) 50 51 52 def assertTemplateRenders(self, template, result, **kwargs): 53 if isinstance(template, basestring): 54 template = loader.get_template_from_string(template) 55 self.assertEqual(template.render(Context(kwargs)), result) 56 57 def assertTemplateRaises(self, exc, template, result, **kwargs): 58 self.assertRaises(exc, self.assertTemplateRenders, template, result, **kwargs) 59 60 51 61 StaticFilesTestCase = override_settings( 52 62 DEBUG = True, 53 63 MEDIA_URL = '/media/', … … TestBuildStaticNonLocalStorage = override_settings( 253 263 )(TestBuildStaticNonLocalStorage) 254 264 255 265 266 class TestBuildStaticCachedStorage(BuildStaticTestCase, TestDefaults): 267 """ 268 Tests for the Cache busting storage 269 """ 270 @classmethod 271 def tearDownClass(cls): 272 """ 273 Resetting the global storage for staticfiles 274 """ 275 storage.configured_storage = storage.ConfiguredStorage() 276 277 def test_template_tag(self): 278 self.assertTemplateRaises(SuspiciousOperation, """{% load static from staticfiles %}{% static "does/not/exist.png" %}""", "/static/does/not/exist.png") 279 self.assertTemplateRenders("""{% load static from staticfiles %}{% static "test/file.txt" %}""", "/static/test/file.dad0999e4f8f.txt") 280 281 282 TestBuildStaticCachedStorage = override_settings( 283 STATICFILES_STORAGE='django.contrib.staticfiles.storage.CachedStaticFilesStorage' 284 )(TestBuildStaticCachedStorage) 285 286 256 287 if sys.platform != 'win32': 257 288 class TestBuildStaticLinks(BuildStaticTestCase, TestDefaults): 258 289 """ … … class TestStaticfilesDirsType(TestCase): 414 445 TestStaticfilesDirsType = override_settings( 415 446 STATICFILES_DIRS = 'a string', 416 447 )(TestStaticfilesDirsType) 448 449 450 class TestTemplateTag(StaticFilesTestCase): 451 452 def test_template_tag(self): 453 self.assertTemplateRenders("""{% load static from staticfiles %}{% static "does/not/exist.png" %}""", "/static/does/not/exist.png") 454 self.assertTemplateRenders("""{% load static from staticfiles %}{% static "testfile.txt" %}""", "/static/testfile.txt")