Merge lp:~thedac/ubuntu/wily/python-django-compressor/debian-merge into lp:ubuntu/wily/python-django-compressor
- Wily (15.10)
- debian-merge
- Merge into wily
Proposed by
David Ames
Status: | Merged |
---|---|
Merge reported by: | Martin Pitt |
Merged at revision: | not available |
Proposed branch: | lp:~thedac/ubuntu/wily/python-django-compressor/debian-merge |
Merge into: | lp:ubuntu/wily/python-django-compressor |
Diff against target: |
2565 lines (+680/-930) 53 files modified
.pc/.quilt_patches (+0/-1) .pc/.quilt_series (+0/-1) .pc/.version (+0/-1) .pc/applied-patches (+0/-2) .pc/disable-coffin-tests.patch/compressor/test_settings.py (+0/-40) .pc/disable-coffin-tests.patch/compressor/tests/test_offline.py (+0/-499) .pc/fix-test_settings.py-for-django-1.7.patch/compressor/test_settings.py (+0/-40) .travis.yml (+15/-10) AUTHORS (+1/-0) README.rst (+10/-7) compressor/__init__.py (+1/-1) compressor/base.py (+8/-2) compressor/cache.py (+13/-3) compressor/conf.py (+2/-0) compressor/css.py (+1/-1) compressor/filters/base.py (+27/-3) compressor/filters/cleancss.py (+10/-0) compressor/filters/css_default.py (+3/-9) compressor/js.py (+32/-4) compressor/management/commands/compress.py (+25/-18) compressor/offline/django.py (+27/-16) compressor/parser/__init__.py (+5/-2) compressor/templates/compressor/js_file.html (+1/-1) compressor/test_settings.py (+19/-2) compressor/tests/precompiler.py (+4/-4) compressor/tests/static/css/filename with spaces.css (+1/-0) compressor/tests/static/css/utf-8_with-BOM.css (+1/-0) compressor/tests/static/js/three.js (+1/-0) compressor/tests/static/js/two.js (+1/-0) compressor/tests/test_base.py (+66/-3) compressor/tests/test_filters.py (+135/-59) compressor/tests/test_jinja2ext.py (+1/-2) compressor/tests/test_offline.py (+20/-19) compressor/tests/test_templates/test_block_super_base_compressed/test_compressor_offline.html (+5/-0) compressor/utils/staticfiles.py (+4/-14) debian/changelog (+20/-0) debian/control (+44/-12) debian/gbp.conf (+1/-1) debian/patches/disable-coffin-tests.patch (+6/-6) debian/patches/fix-test_settings.py-for-django-1.7.patch (+0/-16) debian/patches/remove-failed-test.patch (+43/-0) debian/patches/series (+1/-1) debian/rules (+18/-14) debian/watch (+2/-1) docs/changelog.txt (+38/-4) docs/contributing.txt (+5/-4) docs/django-sekizai.txt (+4/-4) docs/jinja2.txt (+5/-6) docs/quickstart.txt (+3/-5) docs/remote-storages.txt (+5/-7) docs/settings.txt (+22/-6) docs/usage.txt (+1/-1) tox.ini (+23/-78) |
To merge this branch: | bzr merge lp:~thedac/ubuntu/wily/python-django-compressor/debian-merge |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Corey Bryant | Approve | ||
Ubuntu branches | Pending | ||
Review via email: mp+268621@code.launchpad.net |
Commit message
Description of the change
Debian Merge
To post a comment you must log in.
Revision history for this message
Corey Bryant (corey.bryant) wrote : | # |
- 8. By David Ames
-
Sync up d/control
- 9. By David Ames
-
Fix d/watch
Revision history for this message
Corey Bryant (corey.bryant) : | # |
review:
Approve
Revision history for this message
Martin Pitt (pitti) wrote : | # |
This was already merged: https:/
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === removed directory '.pc' |
2 | === removed file '.pc/.quilt_patches' |
3 | --- .pc/.quilt_patches 2012-10-14 10:51:47 +0000 |
4 | +++ .pc/.quilt_patches 1970-01-01 00:00:00 +0000 |
5 | @@ -1,1 +0,0 @@ |
6 | -debian/patches |
7 | |
8 | === removed file '.pc/.quilt_series' |
9 | --- .pc/.quilt_series 2012-10-14 10:51:47 +0000 |
10 | +++ .pc/.quilt_series 1970-01-01 00:00:00 +0000 |
11 | @@ -1,1 +0,0 @@ |
12 | -series |
13 | |
14 | === removed file '.pc/.version' |
15 | --- .pc/.version 2012-10-14 10:51:47 +0000 |
16 | +++ .pc/.version 1970-01-01 00:00:00 +0000 |
17 | @@ -1,1 +0,0 @@ |
18 | -2 |
19 | |
20 | === removed file '.pc/applied-patches' |
21 | --- .pc/applied-patches 2015-01-06 12:34:51 +0000 |
22 | +++ .pc/applied-patches 1970-01-01 00:00:00 +0000 |
23 | @@ -1,2 +0,0 @@ |
24 | -fix-test_settings.py-for-django-1.7.patch |
25 | -disable-coffin-tests.patch |
26 | |
27 | === removed directory '.pc/disable-coffin-tests.patch' |
28 | === removed directory '.pc/disable-coffin-tests.patch/compressor' |
29 | === removed file '.pc/disable-coffin-tests.patch/compressor/test_settings.py' |
30 | --- .pc/disable-coffin-tests.patch/compressor/test_settings.py 2015-01-06 12:34:51 +0000 |
31 | +++ .pc/disable-coffin-tests.patch/compressor/test_settings.py 1970-01-01 00:00:00 +0000 |
32 | @@ -1,40 +0,0 @@ |
33 | -import os |
34 | -import django |
35 | - |
36 | -TEST_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'tests') |
37 | - |
38 | -COMPRESS_CACHE_BACKEND = 'django.core.cache.backends.locmem.CacheClass' |
39 | - |
40 | -DATABASES = { |
41 | - 'default': { |
42 | - 'ENGINE': 'django.db.backends.sqlite3', |
43 | - 'NAME': ':memory:', |
44 | - } |
45 | -} |
46 | - |
47 | -INSTALLED_APPS = [ |
48 | - 'compressor', |
49 | - 'coffin', |
50 | - 'jingo', |
51 | -] |
52 | - |
53 | -STATIC_URL = '/static/' |
54 | - |
55 | - |
56 | -STATIC_ROOT = os.path.join(TEST_DIR, 'static') |
57 | - |
58 | -TEMPLATE_DIRS = ( |
59 | - # Specifically choose a name that will not be considered |
60 | - # by app_directories loader, to make sure each test uses |
61 | - # a specific template without considering the others. |
62 | - os.path.join(TEST_DIR, 'test_templates'), |
63 | -) |
64 | - |
65 | -if django.VERSION[:2] < (1, 6): |
66 | - TEST_RUNNER = 'discover_runner.DiscoverRunner' |
67 | - |
68 | -SECRET_KEY = "iufoj=mibkpdz*%bob952x(%49rqgv8gg45k36kjcg76&-y5=!" |
69 | - |
70 | -PASSWORD_HASHERS = ( |
71 | - 'django.contrib.auth.hashers.UnsaltedMD5PasswordHasher', |
72 | -) |
73 | |
74 | === removed directory '.pc/disable-coffin-tests.patch/compressor/tests' |
75 | === removed file '.pc/disable-coffin-tests.patch/compressor/tests/test_offline.py' |
76 | --- .pc/disable-coffin-tests.patch/compressor/tests/test_offline.py 2015-01-06 12:34:51 +0000 |
77 | +++ .pc/disable-coffin-tests.patch/compressor/tests/test_offline.py 1970-01-01 00:00:00 +0000 |
78 | @@ -1,499 +0,0 @@ |
79 | -from __future__ import with_statement, unicode_literals |
80 | -import io |
81 | -import os |
82 | -import sys |
83 | - |
84 | -from django.core.management.base import CommandError |
85 | -from django.template import Template, Context |
86 | -from django.test import TestCase |
87 | -from django.utils import six, unittest |
88 | - |
89 | -from compressor.cache import flush_offline_manifest, get_offline_manifest |
90 | -from compressor.conf import settings |
91 | -from compressor.exceptions import OfflineGenerationError |
92 | -from compressor.management.commands.compress import Command as CompressCommand |
93 | -from compressor.storage import default_storage |
94 | - |
95 | -if six.PY3: |
96 | - # there is an 'io' module in python 2.6+, but io.StringIO does not |
97 | - # accept regular strings, just unicode objects |
98 | - from io import StringIO |
99 | -else: |
100 | - try: |
101 | - from cStringIO import StringIO |
102 | - except ImportError: |
103 | - from StringIO import StringIO |
104 | - |
105 | -# The Jinja2 tests fail on Python 3.2 due to the following: |
106 | -# The line in compressor/management/commands/compress.py: |
107 | -# compressor_nodes.setdefault(template, []).extend(nodes) |
108 | -# causes the error "unhashable type: 'Template'" |
109 | -_TEST_JINJA2 = not(sys.version_info[0] == 3 and sys.version_info[1] == 2) |
110 | - |
111 | - |
112 | -class OfflineTestCaseMixin(object): |
113 | - template_name = "test_compressor_offline.html" |
114 | - verbosity = 0 |
115 | - # Change this for each test class |
116 | - templates_dir = "" |
117 | - expected_hash = "" |
118 | - # Engines to test |
119 | - if _TEST_JINJA2: |
120 | - engines = ("django", "jinja2") |
121 | - else: |
122 | - engines = ("django",) |
123 | - |
124 | - def setUp(self): |
125 | - self._old_compress = settings.COMPRESS_ENABLED |
126 | - self._old_compress_offline = settings.COMPRESS_OFFLINE |
127 | - self._old_template_dirs = settings.TEMPLATE_DIRS |
128 | - self._old_offline_context = settings.COMPRESS_OFFLINE_CONTEXT |
129 | - self.log = StringIO() |
130 | - |
131 | - # Reset template dirs, because it enables us to force compress to |
132 | - # consider only a specific directory (helps us make true, |
133 | - # independant unit tests). |
134 | - # Specify both Jinja2 and Django template locations. When the wrong engine |
135 | - # is used to parse a template, the TemplateSyntaxError will cause the |
136 | - # template to be skipped over. |
137 | - django_template_dir = os.path.join(settings.TEST_DIR, 'test_templates', self.templates_dir) |
138 | - jinja2_template_dir = os.path.join(settings.TEST_DIR, 'test_templates_jinja2', self.templates_dir) |
139 | - settings.TEMPLATE_DIRS = (django_template_dir, jinja2_template_dir) |
140 | - |
141 | - # Enable offline compress |
142 | - settings.COMPRESS_ENABLED = True |
143 | - settings.COMPRESS_OFFLINE = True |
144 | - |
145 | - if "django" in self.engines: |
146 | - self.template_path = os.path.join(django_template_dir, self.template_name) |
147 | - |
148 | - with io.open(self.template_path, encoding=settings.FILE_CHARSET) as file: |
149 | - self.template = Template(file.read()) |
150 | - |
151 | - self._old_jinja2_get_environment = settings.COMPRESS_JINJA2_GET_ENVIRONMENT |
152 | - |
153 | - if "jinja2" in self.engines: |
154 | - # Setup Jinja2 settings. |
155 | - settings.COMPRESS_JINJA2_GET_ENVIRONMENT = lambda: self._get_jinja2_env() |
156 | - jinja2_env = settings.COMPRESS_JINJA2_GET_ENVIRONMENT() |
157 | - self.template_path_jinja2 = os.path.join(jinja2_template_dir, self.template_name) |
158 | - |
159 | - with io.open(self.template_path_jinja2, encoding=settings.FILE_CHARSET) as file: |
160 | - self.template_jinja2 = jinja2_env.from_string(file.read()) |
161 | - |
162 | - def tearDown(self): |
163 | - settings.COMPRESS_JINJA2_GET_ENVIRONMENT = self._old_jinja2_get_environment |
164 | - settings.COMPRESS_ENABLED = self._old_compress |
165 | - settings.COMPRESS_OFFLINE = self._old_compress_offline |
166 | - settings.TEMPLATE_DIRS = self._old_template_dirs |
167 | - manifest_path = os.path.join('CACHE', 'manifest.json') |
168 | - if default_storage.exists(manifest_path): |
169 | - default_storage.delete(manifest_path) |
170 | - |
171 | - def _render_template(self, engine): |
172 | - if engine == "django": |
173 | - return self.template.render(Context(settings.COMPRESS_OFFLINE_CONTEXT)) |
174 | - elif engine == "jinja2": |
175 | - return self.template_jinja2.render(settings.COMPRESS_OFFLINE_CONTEXT) + "\n" |
176 | - else: |
177 | - return None |
178 | - |
179 | - def _test_offline(self, engine): |
180 | - count, result = CompressCommand().compress(log=self.log, verbosity=self.verbosity, engine=engine) |
181 | - self.assertEqual(1, count) |
182 | - self.assertEqual([ |
183 | - '<script type="text/javascript" src="/static/CACHE/js/%s.js"></script>' % (self.expected_hash, ), |
184 | - ], result) |
185 | - rendered_template = self._render_template(engine) |
186 | - self.assertEqual(rendered_template, "".join(result) + "\n") |
187 | - |
188 | - def test_offline(self): |
189 | - for engine in self.engines: |
190 | - self._test_offline(engine=engine) |
191 | - |
192 | - def _get_jinja2_env(self): |
193 | - import jinja2 |
194 | - import jinja2.ext |
195 | - from compressor.offline.jinja2 import url_for, SpacelessExtension |
196 | - from compressor.contrib.jinja2ext import CompressorExtension |
197 | - |
198 | - # Extensions needed for the test cases only. |
199 | - extensions = [ |
200 | - CompressorExtension, |
201 | - SpacelessExtension, |
202 | - jinja2.ext.with_, |
203 | - jinja2.ext.do, |
204 | - ] |
205 | - loader = self._get_jinja2_loader() |
206 | - env = jinja2.Environment(extensions=extensions, loader=loader) |
207 | - env.globals['url_for'] = url_for |
208 | - |
209 | - return env |
210 | - |
211 | - def _get_jinja2_loader(self): |
212 | - import jinja2 |
213 | - |
214 | - loader = jinja2.FileSystemLoader(settings.TEMPLATE_DIRS, encoding=settings.FILE_CHARSET) |
215 | - return loader |
216 | - |
217 | - |
218 | -class OfflineGenerationSkipDuplicatesTestCase(OfflineTestCaseMixin, TestCase): |
219 | - templates_dir = "test_duplicate" |
220 | - |
221 | - # We don't need to test multiples engines here. |
222 | - engines = ("django",) |
223 | - |
224 | - def _test_offline(self, engine): |
225 | - count, result = CompressCommand().compress(log=self.log, verbosity=self.verbosity, engine=engine) |
226 | - # Only one block compressed, the second identical one was skipped. |
227 | - self.assertEqual(1, count) |
228 | - # Only 1 <script> block in returned result as well. |
229 | - self.assertEqual([ |
230 | - '<script type="text/javascript" src="/static/CACHE/js/f5e179b8eca4.js"></script>', |
231 | - ], result) |
232 | - rendered_template = self._render_template(engine) |
233 | - # But rendering the template returns both (identical) scripts. |
234 | - self.assertEqual(rendered_template, "".join(result * 2) + "\n") |
235 | - |
236 | - |
237 | -class OfflineGenerationBlockSuperTestCase(OfflineTestCaseMixin, TestCase): |
238 | - templates_dir = "test_block_super" |
239 | - expected_hash = "7c02d201f69d" |
240 | - # Block.super not supported for Jinja2 yet. |
241 | - engines = ("django",) |
242 | - |
243 | - |
244 | -class OfflineGenerationBlockSuperMultipleTestCase(OfflineTestCaseMixin, TestCase): |
245 | - templates_dir = "test_block_super_multiple" |
246 | - expected_hash = "f8891c416981" |
247 | - # Block.super not supported for Jinja2 yet. |
248 | - engines = ("django",) |
249 | - |
250 | - |
251 | -class OfflineGenerationBlockSuperMultipleWithCachedLoaderTestCase(OfflineTestCaseMixin, TestCase): |
252 | - templates_dir = "test_block_super_multiple_cached" |
253 | - expected_hash = "2f6ef61c488e" |
254 | - # Block.super not supported for Jinja2 yet. |
255 | - engines = ("django",) |
256 | - |
257 | - def setUp(self): |
258 | - self._old_template_loaders = settings.TEMPLATE_LOADERS |
259 | - settings.TEMPLATE_LOADERS = ( |
260 | - ('django.template.loaders.cached.Loader', ( |
261 | - 'django.template.loaders.filesystem.Loader', |
262 | - 'django.template.loaders.app_directories.Loader', |
263 | - )), |
264 | - ) |
265 | - super(OfflineGenerationBlockSuperMultipleWithCachedLoaderTestCase, self).setUp() |
266 | - |
267 | - def tearDown(self): |
268 | - super(OfflineGenerationBlockSuperMultipleWithCachedLoaderTestCase, self).tearDown() |
269 | - settings.TEMPLATE_LOADERS = self._old_template_loaders |
270 | - |
271 | - |
272 | -class OfflineGenerationBlockSuperTestCaseWithExtraContent(OfflineTestCaseMixin, TestCase): |
273 | - templates_dir = "test_block_super_extra" |
274 | - # Block.super not supported for Jinja2 yet. |
275 | - engines = ("django",) |
276 | - |
277 | - def _test_offline(self, engine): |
278 | - count, result = CompressCommand().compress(log=self.log, verbosity=self.verbosity, engine=engine) |
279 | - self.assertEqual(2, count) |
280 | - self.assertEqual([ |
281 | - '<script type="text/javascript" src="/static/CACHE/js/ced14aec5856.js"></script>', |
282 | - '<script type="text/javascript" src="/static/CACHE/js/7c02d201f69d.js"></script>' |
283 | - ], result) |
284 | - rendered_template = self._render_template(engine) |
285 | - self.assertEqual(rendered_template, "".join(result) + "\n") |
286 | - |
287 | - |
288 | -class OfflineGenerationConditionTestCase(OfflineTestCaseMixin, TestCase): |
289 | - templates_dir = "test_condition" |
290 | - expected_hash = "4e3758d50224" |
291 | - |
292 | - def setUp(self): |
293 | - self.old_offline_context = settings.COMPRESS_OFFLINE_CONTEXT |
294 | - settings.COMPRESS_OFFLINE_CONTEXT = { |
295 | - 'condition': 'red', |
296 | - } |
297 | - super(OfflineGenerationConditionTestCase, self).setUp() |
298 | - |
299 | - def tearDown(self): |
300 | - self.COMPRESS_OFFLINE_CONTEXT = self.old_offline_context |
301 | - super(OfflineGenerationConditionTestCase, self).tearDown() |
302 | - |
303 | - |
304 | -class OfflineGenerationTemplateTagTestCase(OfflineTestCaseMixin, TestCase): |
305 | - templates_dir = "test_templatetag" |
306 | - expected_hash = "a27e1d3a619a" |
307 | - |
308 | - |
309 | -class OfflineGenerationStaticTemplateTagTestCase(OfflineTestCaseMixin, TestCase): |
310 | - templates_dir = "test_static_templatetag" |
311 | - expected_hash = "dfa2bb387fa8" |
312 | - |
313 | - |
314 | -class OfflineGenerationTestCaseWithContext(OfflineTestCaseMixin, TestCase): |
315 | - templates_dir = "test_with_context" |
316 | - expected_hash = "5838e2fd66af" |
317 | - |
318 | - def setUp(self): |
319 | - self.old_offline_context = settings.COMPRESS_OFFLINE_CONTEXT |
320 | - settings.COMPRESS_OFFLINE_CONTEXT = { |
321 | - 'content': 'OK!', |
322 | - } |
323 | - super(OfflineGenerationTestCaseWithContext, self).setUp() |
324 | - |
325 | - def tearDown(self): |
326 | - settings.COMPRESS_OFFLINE_CONTEXT = self.old_offline_context |
327 | - super(OfflineGenerationTestCaseWithContext, self).tearDown() |
328 | - |
329 | - |
330 | -class OfflineGenerationTestCaseErrors(OfflineTestCaseMixin, TestCase): |
331 | - templates_dir = "test_error_handling" |
332 | - |
333 | - def _test_offline(self, engine): |
334 | - count, result = CompressCommand().compress(log=self.log, verbosity=self.verbosity, engine=engine) |
335 | - |
336 | - if engine == "django": |
337 | - self.assertEqual(2, count) |
338 | - else: |
339 | - # Because we use env.parse in Jinja2Parser, the engine does not |
340 | - # actually load the "extends" and "includes" templates, and so |
341 | - # it is unable to detect that they are missing. So all the "compress" |
342 | - # nodes are processed correctly. |
343 | - self.assertEqual(4, count) |
344 | - self.assertEqual(engine, "jinja2") |
345 | - self.assertIn('<link rel="stylesheet" href="/static/CACHE/css/78bd7a762e2d.css" type="text/css" />', result) |
346 | - self.assertIn('<link rel="stylesheet" href="/static/CACHE/css/e31030430724.css" type="text/css" />', result) |
347 | - |
348 | - self.assertIn('<script type="text/javascript" src="/static/CACHE/js/3872c9ae3f42.js"></script>', result) |
349 | - self.assertIn('<script type="text/javascript" src="/static/CACHE/js/cd8870829421.js"></script>', result) |
350 | - |
351 | - |
352 | -class OfflineGenerationTestCaseWithError(OfflineTestCaseMixin, TestCase): |
353 | - templates_dir = 'test_error_handling' |
354 | - |
355 | - def setUp(self): |
356 | - self._old_compress_precompilers = settings.COMPRESS_PRECOMPILERS |
357 | - settings.COMPRESS_PRECOMPILERS = (('text/coffeescript', 'non-existing-binary'),) |
358 | - super(OfflineGenerationTestCaseWithError, self).setUp() |
359 | - |
360 | - def _test_offline(self, engine): |
361 | - """ |
362 | - Test that a CommandError is raised with DEBUG being False as well as |
363 | - True, as otherwise errors in configuration will never show in |
364 | - production. |
365 | - """ |
366 | - self._old_debug = settings.DEBUG |
367 | - |
368 | - try: |
369 | - settings.DEBUG = True |
370 | - self.assertRaises(CommandError, CompressCommand().compress, engine=engine) |
371 | - |
372 | - settings.DEBUG = False |
373 | - self.assertRaises(CommandError, CompressCommand().compress, engine=engine) |
374 | - |
375 | - finally: |
376 | - settings.DEBUG = self._old_debug |
377 | - |
378 | - def tearDown(self): |
379 | - settings.COMPRESS_PRECOMPILERS = self._old_compress_precompilers |
380 | - super(OfflineGenerationTestCaseWithError, self).tearDown() |
381 | - |
382 | - |
383 | -class OfflineGenerationTestCase(OfflineTestCaseMixin, TestCase): |
384 | - templates_dir = "basic" |
385 | - expected_hash = "f5e179b8eca4" |
386 | - |
387 | - def test_rendering_without_manifest_raises_exception(self): |
388 | - # flush cached manifest |
389 | - flush_offline_manifest() |
390 | - self.assertRaises(OfflineGenerationError, |
391 | - self.template.render, Context({})) |
392 | - |
393 | - @unittest.skipIf(not _TEST_JINJA2, "No Jinja2 testing") |
394 | - def test_rendering_without_manifest_raises_exception_jinja2(self): |
395 | - # flush cached manifest |
396 | - flush_offline_manifest() |
397 | - self.assertRaises(OfflineGenerationError, |
398 | - self.template_jinja2.render, {}) |
399 | - |
400 | - def _test_deleting_manifest_does_not_affect_rendering(self, engine): |
401 | - count, result = CompressCommand().compress(log=self.log, verbosity=self.verbosity, engine=engine) |
402 | - get_offline_manifest() |
403 | - manifest_path = os.path.join('CACHE', 'manifest.json') |
404 | - if default_storage.exists(manifest_path): |
405 | - default_storage.delete(manifest_path) |
406 | - self.assertEqual(1, count) |
407 | - self.assertEqual([ |
408 | - '<script type="text/javascript" src="/static/CACHE/js/%s.js"></script>' % (self.expected_hash, ), |
409 | - ], result) |
410 | - rendered_template = self._render_template(engine) |
411 | - self.assertEqual(rendered_template, "".join(result) + "\n") |
412 | - |
413 | - def test_deleting_manifest_does_not_affect_rendering(self): |
414 | - for engine in self.engines: |
415 | - self._test_deleting_manifest_does_not_affect_rendering(engine) |
416 | - |
417 | - def test_requires_model_validation(self): |
418 | - self.assertFalse(CompressCommand.requires_model_validation) |
419 | - |
420 | - def test_get_loaders(self): |
421 | - old_loaders = settings.TEMPLATE_LOADERS |
422 | - settings.TEMPLATE_LOADERS = ( |
423 | - ('django.template.loaders.cached.Loader', ( |
424 | - 'django.template.loaders.filesystem.Loader', |
425 | - 'django.template.loaders.app_directories.Loader', |
426 | - )), |
427 | - ) |
428 | - try: |
429 | - from django.template.loaders.filesystem import Loader as FileSystemLoader |
430 | - from django.template.loaders.app_directories import Loader as AppDirectoriesLoader |
431 | - except ImportError: |
432 | - pass |
433 | - else: |
434 | - loaders = CompressCommand().get_loaders() |
435 | - self.assertTrue(isinstance(loaders[0], FileSystemLoader)) |
436 | - self.assertTrue(isinstance(loaders[1], AppDirectoriesLoader)) |
437 | - finally: |
438 | - settings.TEMPLATE_LOADERS = old_loaders |
439 | - |
440 | - |
441 | -class OfflineGenerationBlockSuperBaseCompressed(OfflineTestCaseMixin, TestCase): |
442 | - template_names = ["base.html", "base2.html", "test_compressor_offline.html"] |
443 | - templates_dir = 'test_block_super_base_compressed' |
444 | - expected_hash = ['028c3fc42232', '2e9d3f5545a6', 'f8891c416981'] |
445 | - # Block.super not supported for Jinja2 yet. |
446 | - engines = ("django",) |
447 | - |
448 | - def setUp(self): |
449 | - super(OfflineGenerationBlockSuperBaseCompressed, self).setUp() |
450 | - |
451 | - self.template_paths = [] |
452 | - self.templates = [] |
453 | - for template_name in self.template_names: |
454 | - template_path = os.path.join(settings.TEMPLATE_DIRS[0], template_name) |
455 | - self.template_paths.append(template_path) |
456 | - with io.open(template_path, encoding=settings.FILE_CHARSET) as file: |
457 | - template = Template(file.read()) |
458 | - self.templates.append(template) |
459 | - |
460 | - def _render_template(self, template, engine): |
461 | - if engine == "django": |
462 | - return template.render(Context(settings.COMPRESS_OFFLINE_CONTEXT)) |
463 | - elif engine == "jinja2": |
464 | - return template.render(settings.COMPRESS_OFFLINE_CONTEXT) + "\n" |
465 | - else: |
466 | - return None |
467 | - |
468 | - def _test_offline(self, engine): |
469 | - count, result = CompressCommand().compress(log=self.log, verbosity=self.verbosity, engine=engine) |
470 | - self.assertEqual(len(self.expected_hash), count) |
471 | - for expected_hash, template in zip(self.expected_hash, self.templates): |
472 | - expected_output = '<script type="text/javascript" src="/static/CACHE/js/%s.js"></script>' % (expected_hash, ) |
473 | - self.assertIn(expected_output, result) |
474 | - rendered_template = self._render_template(template, engine) |
475 | - self.assertEqual(rendered_template, expected_output + '\n') |
476 | - |
477 | - |
478 | -class OfflineGenerationInlineNonAsciiTestCase(OfflineTestCaseMixin, TestCase): |
479 | - templates_dir = "test_inline_non_ascii" |
480 | - |
481 | - def setUp(self): |
482 | - self.old_offline_context = settings.COMPRESS_OFFLINE_CONTEXT |
483 | - settings.COMPRESS_OFFLINE_CONTEXT = { |
484 | - 'test_non_ascii_value': '\u2014', |
485 | - } |
486 | - super(OfflineGenerationInlineNonAsciiTestCase, self).setUp() |
487 | - |
488 | - def tearDown(self): |
489 | - self.COMPRESS_OFFLINE_CONTEXT = self.old_offline_context |
490 | - super(OfflineGenerationInlineNonAsciiTestCase, self).tearDown() |
491 | - |
492 | - def _test_offline(self, engine): |
493 | - count, result = CompressCommand().compress(log=self.log, verbosity=self.verbosity, engine=engine) |
494 | - rendered_template = self._render_template(engine) |
495 | - self.assertEqual(rendered_template, "".join(result) + "\n") |
496 | - |
497 | - |
498 | -class OfflineGenerationComplexTestCase(OfflineTestCaseMixin, TestCase): |
499 | - templates_dir = "test_complex" |
500 | - |
501 | - def setUp(self): |
502 | - self.old_offline_context = settings.COMPRESS_OFFLINE_CONTEXT |
503 | - settings.COMPRESS_OFFLINE_CONTEXT = { |
504 | - 'condition': 'OK!', |
505 | - # Django templating does not allow definition of tuples in the |
506 | - # templates. Make sure this is same as test_templates_jinja2/test_complex. |
507 | - 'my_names': ("js/one.js", "js/nonasc.js"), |
508 | - } |
509 | - super(OfflineGenerationComplexTestCase, self).setUp() |
510 | - |
511 | - def tearDown(self): |
512 | - self.COMPRESS_OFFLINE_CONTEXT = self.old_offline_context |
513 | - super(OfflineGenerationComplexTestCase, self).tearDown() |
514 | - |
515 | - def _test_offline(self, engine): |
516 | - count, result = CompressCommand().compress(log=self.log, verbosity=self.verbosity, engine=engine) |
517 | - self.assertEqual(3, count) |
518 | - self.assertEqual([ |
519 | - '<script type="text/javascript" src="/static/CACHE/js/0e8807bebcee.js"></script>', |
520 | - '<script type="text/javascript" src="/static/CACHE/js/eed1d222933e.js"></script>', |
521 | - '<script type="text/javascript" src="/static/CACHE/js/00b4baffe335.js"></script>', |
522 | - ], result) |
523 | - rendered_template = self._render_template(engine) |
524 | - result = (result[0], result[2]) |
525 | - self.assertEqual(rendered_template, "".join(result) + "\n") |
526 | - |
527 | - |
528 | -# Coffin does not work on Python 3.2+ due to: |
529 | -# The line at coffin/template/__init__.py:15 |
530 | -# from library import * |
531 | -# causing 'ImportError: No module named library'. |
532 | -# It seems there is no evidence nor indicated support for Python 3+. |
533 | -@unittest.skipIf(sys.version_info >= (3, 2), |
534 | - "Coffin does not support 3.2+") |
535 | -class OfflineGenerationCoffinTestCase(OfflineTestCaseMixin, TestCase): |
536 | - templates_dir = "test_coffin" |
537 | - expected_hash = "32c8281e3346" |
538 | - engines = ("jinja2",) |
539 | - |
540 | - def _get_jinja2_env(self): |
541 | - import jinja2 |
542 | - from coffin.common import env |
543 | - from compressor.contrib.jinja2ext import CompressorExtension |
544 | - |
545 | - # Could have used the env.add_extension method, but it's only available |
546 | - # in Jinja2 v2.5 |
547 | - new_env = jinja2.Environment(extensions=[CompressorExtension]) |
548 | - env.extensions.update(new_env.extensions) |
549 | - |
550 | - return env |
551 | - |
552 | - |
553 | -# Jingo does not work when using Python 3.2 due to the use of Unicode string |
554 | -# prefix (and possibly other stuff), but it actually works when using Python 3.3 |
555 | -# since it tolerates the use of the Unicode string prefix. Python 3.3 support |
556 | -# is also evident in its tox.ini file. |
557 | -@unittest.skipIf(sys.version_info >= (3, 2) and sys.version_info < (3, 3), |
558 | - "Jingo does not support 3.2") |
559 | -class OfflineGenerationJingoTestCase(OfflineTestCaseMixin, TestCase): |
560 | - templates_dir = "test_jingo" |
561 | - expected_hash = "61ec584468eb" |
562 | - engines = ("jinja2",) |
563 | - |
564 | - def _get_jinja2_env(self): |
565 | - import jinja2 |
566 | - import jinja2.ext |
567 | - from jingo import env |
568 | - from compressor.contrib.jinja2ext import CompressorExtension |
569 | - from compressor.offline.jinja2 import SpacelessExtension, url_for |
570 | - |
571 | - # Could have used the env.add_extension method, but it's only available |
572 | - # in Jinja2 v2.5 |
573 | - new_env = jinja2.Environment(extensions=[CompressorExtension, SpacelessExtension, jinja2.ext.with_]) |
574 | - env.extensions.update(new_env.extensions) |
575 | - env.globals['url_for'] = url_for |
576 | - |
577 | - return env |
578 | |
579 | === removed directory '.pc/fix-test_settings.py-for-django-1.7.patch' |
580 | === removed directory '.pc/fix-test_settings.py-for-django-1.7.patch/compressor' |
581 | === removed file '.pc/fix-test_settings.py-for-django-1.7.patch/compressor/test_settings.py' |
582 | --- .pc/fix-test_settings.py-for-django-1.7.patch/compressor/test_settings.py 2014-09-08 17:31:54 +0000 |
583 | +++ .pc/fix-test_settings.py-for-django-1.7.patch/compressor/test_settings.py 1970-01-01 00:00:00 +0000 |
584 | @@ -1,40 +0,0 @@ |
585 | -import os |
586 | -import django |
587 | - |
588 | -TEST_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'tests') |
589 | - |
590 | -COMPRESS_CACHE_BACKEND = 'locmem://' |
591 | - |
592 | -DATABASES = { |
593 | - 'default': { |
594 | - 'ENGINE': 'django.db.backends.sqlite3', |
595 | - 'NAME': ':memory:', |
596 | - } |
597 | -} |
598 | - |
599 | -INSTALLED_APPS = [ |
600 | - 'compressor', |
601 | - 'coffin', |
602 | - 'jingo', |
603 | -] |
604 | - |
605 | -STATIC_URL = '/static/' |
606 | - |
607 | - |
608 | -STATIC_ROOT = os.path.join(TEST_DIR, 'static') |
609 | - |
610 | -TEMPLATE_DIRS = ( |
611 | - # Specifically choose a name that will not be considered |
612 | - # by app_directories loader, to make sure each test uses |
613 | - # a specific template without considering the others. |
614 | - os.path.join(TEST_DIR, 'test_templates'), |
615 | -) |
616 | - |
617 | -if django.VERSION[:2] < (1, 6): |
618 | - TEST_RUNNER = 'discover_runner.DiscoverRunner' |
619 | - |
620 | -SECRET_KEY = "iufoj=mibkpdz*%bob952x(%49rqgv8gg45k36kjcg76&-y5=!" |
621 | - |
622 | -PASSWORD_HASHERS = ( |
623 | - 'django.contrib.auth.hashers.UnsaltedMD5PasswordHasher', |
624 | -) |
625 | |
626 | === modified file '.travis.yml' |
627 | --- .travis.yml 2014-06-26 15:08:13 +0000 |
628 | +++ .travis.yml 2015-08-20 18:49:21 +0000 |
629 | @@ -3,20 +3,25 @@ |
630 | - sudo apt-get update |
631 | - sudo apt-get install csstidy libxml2-dev libxslt-dev |
632 | install: |
633 | - - pip install tox coveralls |
634 | + - pip install tox |
635 | script: |
636 | - tox |
637 | env: |
638 | - - TOXENV=py33-1.6.X |
639 | - - TOXENV=py32-1.6.X |
640 | - - TOXENV=py27-1.6.X |
641 | - - TOXENV=py26-1.6.X |
642 | - - TOXENV=py33-1.5.X |
643 | - - TOXENV=py32-1.5.X |
644 | - - TOXENV=py27-1.5.X |
645 | + - TOXENV=py26-1.4.X |
646 | - TOXENV=py26-1.5.X |
647 | - TOXENV=py27-1.4.X |
648 | - - TOXENV=py26-1.4.X |
649 | + - TOXENV=py27-1.5.X |
650 | + - TOXENV=py26-1.6.X |
651 | + - TOXENV=py27-1.6.X |
652 | + - TOXENV=py32-1.6.X |
653 | + - TOXENV=py33-1.6.X |
654 | + - TOXENV=py27-1.7.X |
655 | + - TOXENV=py32-1.7.X |
656 | + - TOXENV=py33-1.7.X |
657 | + - TOXENV=py34-1.7.X |
658 | + - TOXENV=py27-1.8.X |
659 | + - TOXENV=py32-1.8.X |
660 | + - TOXENV=py33-1.8.X |
661 | + - TOXENV=py34-1.8.X |
662 | notifications: |
663 | irc: "irc.freenode.org#django-compressor" |
664 | -after_success: coveralls |
665 | |
666 | === modified file 'AUTHORS' |
667 | --- AUTHORS 2015-01-06 14:31:34 +0000 |
668 | +++ AUTHORS 2015-08-20 18:49:21 +0000 |
669 | @@ -29,6 +29,7 @@ |
670 | Boris Shemigon |
671 | Brad Whittington |
672 | Bruno Renié |
673 | +Carlton Gibson |
674 | Cassus Adam Banko |
675 | Chris Adams |
676 | Chris Streeter |
677 | |
678 | === modified file 'README.rst' |
679 | --- README.rst 2015-01-06 14:31:34 +0000 |
680 | +++ README.rst 2015-08-20 18:49:21 +0000 |
681 | @@ -4,16 +4,19 @@ |
682 | .. image:: https://coveralls.io/repos/django-compressor/django-compressor/badge.png?branch=develop |
683 | :target: https://coveralls.io/r/django-compressor/django-compressor?branch=develop |
684 | |
685 | -.. image:: https://pypip.in/v/django_compressor/badge.png |
686 | - :target: https://pypi.python.org/pypi/django_compressor |
687 | - |
688 | -.. image:: https://pypip.in/d/django_compressor/badge.png |
689 | - :target: https://pypi.python.org/pypi/django_compressor |
690 | - |
691 | -.. image:: https://secure.travis-ci.org/django-compressor/django-compressor.png?branch=develop |
692 | +.. image:: https://pypip.in/v/django_compressor/badge.svg |
693 | + :target: https://pypi.python.org/pypi/django_compressor |
694 | + |
695 | +.. image:: https://pypip.in/d/django_compressor/badge.svg |
696 | + :target: https://pypi.python.org/pypi/django_compressor |
697 | + |
698 | +.. image:: https://secure.travis-ci.org/django-compressor/django-compressor.svg?branch=develop |
699 | :alt: Build Status |
700 | :target: http://travis-ci.org/django-compressor/django-compressor |
701 | |
702 | +.. image:: https://caniusepython3.com/project/django_compressor.svg |
703 | + :target: https://caniusepython3.com/project/django_compressor |
704 | + |
705 | Django Compressor combines and compresses linked and inline Javascript |
706 | or CSS in a Django template into cacheable static files by using the |
707 | ``compress`` template tag. |
708 | |
709 | === modified file 'compressor/__init__.py' |
710 | --- compressor/__init__.py 2015-01-06 14:31:34 +0000 |
711 | +++ compressor/__init__.py 2015-08-20 18:49:21 +0000 |
712 | @@ -1,2 +1,2 @@ |
713 | # following PEP 386 |
714 | -__version__ = "1.4a1" |
715 | +__version__ = "1.5" |
716 | |
717 | === modified file 'compressor/base.py' |
718 | --- compressor/base.py 2015-01-06 14:31:34 +0000 |
719 | +++ compressor/base.py 2015-08-20 18:49:21 +0000 |
720 | @@ -5,7 +5,10 @@ |
721 | from django.core.files.base import ContentFile |
722 | from django.template import Context |
723 | from django.template.loader import render_to_string |
724 | -from django.utils.importlib import import_module |
725 | +try: |
726 | + from importlib import import_module |
727 | +except: |
728 | + from django.utils.importlib import import_module |
729 | from django.utils.safestring import mark_safe |
730 | |
731 | try: |
732 | @@ -140,6 +143,9 @@ |
733 | """ |
734 | Reads file contents using given `charset` and returns it as text. |
735 | """ |
736 | + if charset == 'utf-8': |
737 | + # Removes BOM |
738 | + charset = 'utf-8-sig' |
739 | with codecs.open(filename, 'r', charset) as fd: |
740 | try: |
741 | return fd.read() |
742 | @@ -247,7 +253,7 @@ |
743 | mod_name, cls_name = get_mod_func(filter_or_command) |
744 | try: |
745 | mod = import_module(mod_name) |
746 | - except ImportError: |
747 | + except (ImportError, TypeError): |
748 | filter = CompilerFilter( |
749 | content, filter_type=self.type, filename=filename, |
750 | charset=charset, command=filter_or_command) |
751 | |
752 | === modified file 'compressor/cache.py' |
753 | --- compressor/cache.py 2015-01-06 14:31:34 +0000 |
754 | +++ compressor/cache.py 2015-08-20 18:49:21 +0000 |
755 | @@ -4,11 +4,21 @@ |
756 | import socket |
757 | import time |
758 | |
759 | -from django.core.cache import get_cache |
760 | +try: |
761 | + from django.core.cache import caches |
762 | + def get_cache(name): |
763 | + return caches[name] |
764 | +except ImportError: |
765 | + from django.core.cache import get_cache |
766 | + |
767 | from django.core.files.base import ContentFile |
768 | from django.utils.encoding import force_text, smart_bytes |
769 | from django.utils.functional import SimpleLazyObject |
770 | -from django.utils.importlib import import_module |
771 | + |
772 | +try: |
773 | + from importlib import import_module |
774 | +except: |
775 | + from django.utils.importlib import import_module |
776 | |
777 | from compressor.conf import settings |
778 | from compressor.storage import default_storage |
779 | @@ -39,7 +49,7 @@ |
780 | mod_name, func_name = get_mod_func( |
781 | settings.COMPRESS_CACHE_KEY_FUNCTION) |
782 | _cachekey_func = getattr(import_module(mod_name), func_name) |
783 | - except (AttributeError, ImportError) as e: |
784 | + except (AttributeError, ImportError, TypeError) as e: |
785 | raise ImportError("Couldn't import cache key function %s: %s" % |
786 | (settings.COMPRESS_CACHE_KEY_FUNCTION, e)) |
787 | return _cachekey_func(*args, **kwargs) |
788 | |
789 | === modified file 'compressor/conf.py' |
790 | --- compressor/conf.py 2015-01-06 14:31:34 +0000 |
791 | +++ compressor/conf.py 2015-08-20 18:49:21 +0000 |
792 | @@ -45,6 +45,8 @@ |
793 | YUGLIFY_BINARY = 'yuglify' |
794 | YUGLIFY_CSS_ARGUMENTS = '--terminal' |
795 | YUGLIFY_JS_ARGUMENTS = '--terminal' |
796 | + CLEAN_CSS_BINARY = 'cleancss' |
797 | + CLEAN_CSS_ARGUMENTS = '' |
798 | DATA_URI_MAX_SIZE = 1024 |
799 | |
800 | # the cache backend to use |
801 | |
802 | === modified file 'compressor/css.py' |
803 | --- compressor/css.py 2014-06-26 15:08:13 +0000 |
804 | +++ compressor/css.py 2015-08-20 18:49:21 +0000 |
805 | @@ -34,7 +34,7 @@ |
806 | self.media_nodes[-1][1].split_content.append(data) |
807 | else: |
808 | node = self.__class__(content=self.parser.elem_str(elem), |
809 | - context=self.context) |
810 | + context=self.context) |
811 | node.split_content.append(data) |
812 | self.media_nodes.append((media, node)) |
813 | return self.split_content |
814 | |
815 | === modified file 'compressor/filters/base.py' |
816 | --- compressor/filters/base.py 2015-01-06 14:31:34 +0000 |
817 | +++ compressor/filters/base.py 2015-08-20 18:49:21 +0000 |
818 | @@ -3,9 +3,27 @@ |
819 | import logging |
820 | import subprocess |
821 | |
822 | +from platform import system |
823 | + |
824 | +if system() != "Windows": |
825 | + try: |
826 | + from shlex import quote as shell_quote # Python 3 |
827 | + except ImportError: |
828 | + from pipes import quote as shell_quote # Python 2 |
829 | +else: |
830 | + from subprocess import list2cmdline |
831 | + def shell_quote(s): |
832 | + # shlex.quote/pipes.quote is not compatible with Windows |
833 | + return list2cmdline([s]) |
834 | + |
835 | from django.core.exceptions import ImproperlyConfigured |
836 | from django.core.files.temp import NamedTemporaryFile |
837 | -from django.utils.importlib import import_module |
838 | + |
839 | +try: |
840 | + from importlib import import_module |
841 | +except ImportError: |
842 | + from django.utils.importlib import import_module |
843 | + |
844 | from django.utils.encoding import smart_text |
845 | from django.utils import six |
846 | |
847 | @@ -26,7 +44,7 @@ |
848 | """ |
849 | def __init__(self, content, filter_type=None, filename=None, verbose=0, |
850 | charset=None): |
851 | - self.type = filter_type |
852 | + self.type = filter_type or getattr(self, 'type', None) |
853 | self.content = content |
854 | self.verbose = verbose or settings.COMPRESS_VERBOSE |
855 | self.logger = logger |
856 | @@ -65,7 +83,7 @@ |
857 | try: |
858 | mod_name, func_name = get_mod_func(self.callback) |
859 | func = getattr(import_module(mod_name), func_name) |
860 | - except ImportError: |
861 | + except (ImportError, TypeError): |
862 | if self.dependencies: |
863 | if len(self.dependencies) == 1: |
864 | warning = "dependency (%s) is" % self.dependencies[0] |
865 | @@ -147,6 +165,12 @@ |
866 | self.outfile = NamedTemporaryFile(mode='r+', suffix=ext) |
867 | options["outfile"] = self.outfile.name |
868 | |
869 | + # Quote infile and outfile for spaces etc. |
870 | + if "infile" in options: |
871 | + options["infile"] = shell_quote(options["infile"]) |
872 | + if "outfile" in options: |
873 | + options["outfile"] = shell_quote(options["outfile"]) |
874 | + |
875 | try: |
876 | command = self.command.format(**options) |
877 | proc = subprocess.Popen( |
878 | |
879 | === added file 'compressor/filters/cleancss.py' |
880 | --- compressor/filters/cleancss.py 1970-01-01 00:00:00 +0000 |
881 | +++ compressor/filters/cleancss.py 2015-08-20 18:49:21 +0000 |
882 | @@ -0,0 +1,10 @@ |
883 | +from compressor.conf import settings |
884 | +from compressor.filters import CompilerFilter |
885 | + |
886 | + |
887 | +class CleanCSSFilter(CompilerFilter): |
888 | + command = "{binary} {args} -o {outfile} {infile}" |
889 | + options = ( |
890 | + ("binary", settings.COMPRESS_CLEAN_CSS_BINARY), |
891 | + ("args", settings.COMPRESS_CLEAN_CSS_ARGUMENTS), |
892 | + ) |
893 | |
894 | === modified file 'compressor/filters/css_default.py' |
895 | --- compressor/filters/css_default.py 2015-01-06 14:31:34 +0000 |
896 | +++ compressor/filters/css_default.py 2015-08-20 18:49:21 +0000 |
897 | @@ -5,7 +5,6 @@ |
898 | from compressor.cache import get_hashed_mtime, get_hashed_content |
899 | from compressor.conf import settings |
900 | from compressor.filters import FilterBase, FilterError |
901 | -from compressor.utils import staticfiles |
902 | |
903 | URL_PATTERN = re.compile(r'url\(([^\)]+)\)') |
904 | SRC_PATTERN = re.compile(r'src=([\'"])(.+?)\1') |
905 | @@ -22,10 +21,7 @@ |
906 | self.has_scheme = False |
907 | |
908 | def input(self, filename=None, basename=None, **kwargs): |
909 | - if filename is not None: |
910 | - filename = os.path.normcase(os.path.abspath(filename)) |
911 | - if (not (filename and filename.startswith(self.root)) and |
912 | - not self.find(basename)): |
913 | + if not filename: |
914 | return self.content |
915 | self.path = basename.replace(os.sep, '/') |
916 | self.path = self.path.lstrip('/') |
917 | @@ -40,10 +36,6 @@ |
918 | return SRC_PATTERN.sub(self.src_converter, |
919 | URL_PATTERN.sub(self.url_converter, self.content)) |
920 | |
921 | - def find(self, basename): |
922 | - if settings.DEBUG and basename and staticfiles.finders: |
923 | - return staticfiles.finders.find(basename) |
924 | - |
925 | def guess_filename(self, url): |
926 | local_path = url |
927 | if self.has_scheme: |
928 | @@ -70,6 +62,8 @@ |
929 | suffix = get_hashed_mtime(filename) |
930 | elif settings.COMPRESS_CSS_HASHING_METHOD in ("hash", "content"): |
931 | suffix = get_hashed_content(filename) |
932 | + elif settings.COMPRESS_CSS_HASHING_METHOD is None: |
933 | + suffix = None |
934 | else: |
935 | raise FilterError('COMPRESS_CSS_HASHING_METHOD is configured ' |
936 | 'with an unknown method (%s).' % |
937 | |
938 | === modified file 'compressor/js.py' |
939 | --- compressor/js.py 2012-10-14 10:51:47 +0000 |
940 | +++ compressor/js.py 2015-08-20 18:49:21 +0000 |
941 | @@ -12,14 +12,42 @@ |
942 | def split_contents(self): |
943 | if self.split_content: |
944 | return self.split_content |
945 | + self.extra_nodes = [] |
946 | for elem in self.parser.js_elems(): |
947 | attribs = self.parser.elem_attribs(elem) |
948 | if 'src' in attribs: |
949 | basename = self.get_basename(attribs['src']) |
950 | filename = self.get_filename(basename) |
951 | content = (SOURCE_FILE, filename, basename, elem) |
952 | - self.split_content.append(content) |
953 | - else: |
954 | - content = self.parser.elem_content(elem) |
955 | - self.split_content.append((SOURCE_HUNK, content, None, elem)) |
956 | + else: |
957 | + content = (SOURCE_HUNK, self.parser.elem_content(elem), None, elem) |
958 | + self.split_content.append(content) |
959 | + if 'async' in attribs: |
960 | + extra = ' async' |
961 | + elif 'defer' in attribs: |
962 | + extra = ' defer' |
963 | + else: |
964 | + extra = '' |
965 | + # Append to the previous node if it had the same attribute |
966 | + append_to_previous = (self.extra_nodes and |
967 | + self.extra_nodes[-1][0] == extra) |
968 | + if append_to_previous and settings.COMPRESS_ENABLED: |
969 | + self.extra_nodes[-1][1].split_content.append(content) |
970 | + else: |
971 | + node = self.__class__(content=self.parser.elem_str(elem), |
972 | + context=self.context) |
973 | + node.split_content.append(content) |
974 | + self.extra_nodes.append((extra, node)) |
975 | return self.split_content |
976 | + |
977 | + def output(self, *args, **kwargs): |
978 | + if (settings.COMPRESS_ENABLED or settings.COMPRESS_PRECOMPILERS or |
979 | + kwargs.get('forced', False)): |
980 | + self.split_contents() |
981 | + if hasattr(self, 'extra_nodes'): |
982 | + ret = [] |
983 | + for extra, subnode in self.extra_nodes: |
984 | + subnode.extra_context.update({'extra': extra}) |
985 | + ret.append(subnode.output(*args, **kwargs)) |
986 | + return '\n'.join(ret) |
987 | + return super(JsCompressor, self).output(*args, **kwargs) |
988 | |
989 | === modified file 'compressor/management/commands/compress.py' |
990 | --- compressor/management/commands/compress.py 2015-01-06 14:31:34 +0000 |
991 | +++ compressor/management/commands/compress.py 2015-08-20 18:49:21 +0000 |
992 | @@ -5,6 +5,7 @@ |
993 | from fnmatch import fnmatch |
994 | from optparse import make_option |
995 | |
996 | +import django |
997 | from django.core.management.base import NoArgsCommand, CommandError |
998 | import django.template |
999 | from django.template import Context |
1000 | @@ -53,24 +54,30 @@ |
1001 | requires_model_validation = False |
1002 | |
1003 | def get_loaders(self): |
1004 | - from django.template.loader import template_source_loaders |
1005 | - if template_source_loaders is None: |
1006 | - try: |
1007 | - from django.template.loader import ( |
1008 | - find_template as finder_func) |
1009 | - except ImportError: |
1010 | - from django.template.loader import ( |
1011 | - find_template_source as finder_func) # noqa |
1012 | - try: |
1013 | - # Force django to calculate template_source_loaders from |
1014 | - # TEMPLATE_LOADERS settings, by asking to find a dummy template |
1015 | - source, name = finder_func('test') |
1016 | - except django.template.TemplateDoesNotExist: |
1017 | - pass |
1018 | - # Reload template_source_loaders now that it has been calculated ; |
1019 | - # it should contain the list of valid, instanciated template loaders |
1020 | - # to use. |
1021 | + if django.VERSION < (1, 8): |
1022 | from django.template.loader import template_source_loaders |
1023 | + if template_source_loaders is None: |
1024 | + try: |
1025 | + from django.template.loader import ( |
1026 | + find_template as finder_func) |
1027 | + except ImportError: |
1028 | + from django.template.loader import ( |
1029 | + find_template_source as finder_func) # noqa |
1030 | + try: |
1031 | + # Force django to calculate template_source_loaders from |
1032 | + # TEMPLATE_LOADERS settings, by asking to find a dummy template |
1033 | + source, name = finder_func('test') |
1034 | + except django.template.TemplateDoesNotExist: |
1035 | + pass |
1036 | + # Reload template_source_loaders now that it has been calculated ; |
1037 | + # it should contain the list of valid, instanciated template loaders |
1038 | + # to use. |
1039 | + from django.template.loader import template_source_loaders |
1040 | + else: |
1041 | + from django.template import engines |
1042 | + template_source_loaders = [] |
1043 | + for e in engines.all(): |
1044 | + template_source_loaders.extend(e.engine.get_template_loaders(e.engine.loaders)) |
1045 | loaders = [] |
1046 | # If template loader is CachedTemplateLoader, return the loaders |
1047 | # that it wraps around. So if we have |
1048 | @@ -130,7 +137,7 @@ |
1049 | if get_template_sources is None: |
1050 | get_template_sources = loader.get_template_sources |
1051 | paths.update(list(get_template_sources(''))) |
1052 | - except (ImportError, AttributeError): |
1053 | + except (ImportError, AttributeError, TypeError): |
1054 | # Yeah, this didn't work out so well, let's move on |
1055 | pass |
1056 | if not paths: |
1057 | |
1058 | === modified file 'compressor/offline/django.py' |
1059 | --- compressor/offline/django.py 2014-06-26 15:08:13 +0000 |
1060 | +++ compressor/offline/django.py 2015-08-20 18:49:21 +0000 |
1061 | @@ -1,13 +1,13 @@ |
1062 | from __future__ import absolute_import |
1063 | -import io |
1064 | from copy import copy |
1065 | |
1066 | +import django |
1067 | from django import template |
1068 | from django.conf import settings |
1069 | -from django.template import Template |
1070 | from django.template import Context |
1071 | from django.template.base import Node, VariableNode, TextNode, NodeList |
1072 | from django.template.defaulttags import IfNode |
1073 | +from django.template.loader import get_template |
1074 | from django.template.loader_tags import ExtendsNode, BlockNode, BlockContext |
1075 | |
1076 | |
1077 | @@ -15,7 +15,7 @@ |
1078 | from compressor.templatetags.compress import CompressorNode |
1079 | |
1080 | |
1081 | -def handle_extendsnode(extendsnode, block_context=None): |
1082 | +def handle_extendsnode(extendsnode, block_context=None, original=None): |
1083 | """Create a copy of Node tree of a derived template replacing |
1084 | all blocks tags with the nodes of appropriate blocks. |
1085 | Also handles {{ block.super }} tags. |
1086 | @@ -27,6 +27,9 @@ |
1087 | block_context.add_blocks(blocks) |
1088 | |
1089 | context = Context(settings.COMPRESS_OFFLINE_CONTEXT) |
1090 | + if original is not None: |
1091 | + context.template = original |
1092 | + |
1093 | compiled_parent = extendsnode.get_parent(context) |
1094 | parent_nodelist = compiled_parent.nodelist |
1095 | # If the parent template has an ExtendsNode it is not the root. |
1096 | @@ -34,7 +37,7 @@ |
1097 | # The ExtendsNode has to be the first non-text node. |
1098 | if not isinstance(node, TextNode): |
1099 | if isinstance(node, ExtendsNode): |
1100 | - return handle_extendsnode(node, block_context) |
1101 | + return handle_extendsnode(node, block_context, original) |
1102 | break |
1103 | # Add blocks of the root template to block context. |
1104 | blocks = dict((n.name, n) for n in |
1105 | @@ -55,6 +58,8 @@ |
1106 | if not block_stack: |
1107 | continue |
1108 | node = block_context.get_block(block_stack[-1].name) |
1109 | + if not node: |
1110 | + continue |
1111 | if isinstance(node, BlockNode): |
1112 | expanded_block = expand_blocknode(node, block_stack, block_context) |
1113 | new_nodelist.extend(expanded_block) |
1114 | @@ -93,13 +98,15 @@ |
1115 | self.charset = charset |
1116 | |
1117 | def parse(self, template_name): |
1118 | - with io.open(template_name, mode='rb') as file: |
1119 | - try: |
1120 | - return Template(file.read().decode(self.charset)) |
1121 | - except template.TemplateSyntaxError as e: |
1122 | - raise TemplateSyntaxError(str(e)) |
1123 | - except template.TemplateDoesNotExist as e: |
1124 | - raise TemplateDoesNotExist(str(e)) |
1125 | + try: |
1126 | + if django.VERSION < (1, 8): |
1127 | + return get_template(template_name) |
1128 | + else: |
1129 | + return get_template(template_name).template |
1130 | + except template.TemplateSyntaxError as e: |
1131 | + raise TemplateSyntaxError(str(e)) |
1132 | + except template.TemplateDoesNotExist as e: |
1133 | + raise TemplateDoesNotExist(str(e)) |
1134 | |
1135 | def process_template(self, template, context): |
1136 | return True |
1137 | @@ -111,15 +118,17 @@ |
1138 | pass |
1139 | |
1140 | def render_nodelist(self, template, context, node): |
1141 | + if django.VERSION >= (1, 8): |
1142 | + context.template = template |
1143 | return node.nodelist.render(context) |
1144 | |
1145 | def render_node(self, template, context, node): |
1146 | return node.render(context, forced=True) |
1147 | |
1148 | - def get_nodelist(self, node): |
1149 | + def get_nodelist(self, node, original=None): |
1150 | if isinstance(node, ExtendsNode): |
1151 | try: |
1152 | - return handle_extendsnode(node) |
1153 | + return handle_extendsnode(node, block_context=None, original=original) |
1154 | except template.TemplateSyntaxError as e: |
1155 | raise TemplateSyntaxError(str(e)) |
1156 | except template.TemplateDoesNotExist as e: |
1157 | @@ -134,10 +143,12 @@ |
1158 | nodelist = getattr(node, 'nodelist', []) |
1159 | return nodelist |
1160 | |
1161 | - def walk_nodes(self, node): |
1162 | - for node in self.get_nodelist(node): |
1163 | + def walk_nodes(self, node, original=None): |
1164 | + if django.VERSION >= (1, 8) and original is None: |
1165 | + original = node |
1166 | + for node in self.get_nodelist(node, original): |
1167 | if isinstance(node, CompressorNode) and node.is_offline_compression_enabled(forced=True): |
1168 | yield node |
1169 | else: |
1170 | - for node in self.walk_nodes(node): |
1171 | + for node in self.walk_nodes(node, original): |
1172 | yield node |
1173 | |
1174 | === modified file 'compressor/parser/__init__.py' |
1175 | --- compressor/parser/__init__.py 2014-06-26 15:08:13 +0000 |
1176 | +++ compressor/parser/__init__.py 2015-08-20 18:49:21 +0000 |
1177 | @@ -1,6 +1,9 @@ |
1178 | from django.utils import six |
1179 | from django.utils.functional import LazyObject |
1180 | -from django.utils.importlib import import_module |
1181 | +try: |
1182 | + from importlib import import_module |
1183 | +except ImportError: |
1184 | + from django.utils.importlib import import_module |
1185 | |
1186 | # support legacy parser module usage |
1187 | from compressor.parser.base import ParserBase # noqa |
1188 | @@ -30,5 +33,5 @@ |
1189 | import_module(dependency) |
1190 | self._wrapped = parser(content) |
1191 | break |
1192 | - except ImportError: |
1193 | + except (ImportError, TypeError): |
1194 | continue |
1195 | |
1196 | === modified file 'compressor/templates/compressor/js_file.html' |
1197 | --- compressor/templates/compressor/js_file.html 2012-10-14 10:51:47 +0000 |
1198 | +++ compressor/templates/compressor/js_file.html 2015-08-20 18:49:21 +0000 |
1199 | @@ -1,1 +1,1 @@ |
1200 | -<script type="text/javascript" src="{{ compressed.url }}"></script> |
1201 | \ No newline at end of file |
1202 | +<script type="text/javascript" src="{{ compressed.url }}"{{ compressed.extra }}></script> |
1203 | \ No newline at end of file |
1204 | |
1205 | === modified file 'compressor/test_settings.py' |
1206 | --- compressor/test_settings.py 2015-01-06 14:31:34 +0000 |
1207 | +++ compressor/test_settings.py 2015-08-20 18:49:21 +0000 |
1208 | @@ -3,7 +3,13 @@ |
1209 | |
1210 | TEST_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'tests') |
1211 | |
1212 | -COMPRESS_CACHE_BACKEND = 'django.core.cache.backends.locmem.CacheClass' |
1213 | + |
1214 | +CACHES = { |
1215 | + 'default': { |
1216 | + 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', |
1217 | + 'LOCATION': 'unique-snowflake' |
1218 | + } |
1219 | +} |
1220 | |
1221 | DATABASES = { |
1222 | 'default': { |
1223 | @@ -13,8 +19,17 @@ |
1224 | } |
1225 | |
1226 | INSTALLED_APPS = [ |
1227 | + 'django.contrib.staticfiles', |
1228 | 'compressor', |
1229 | - 'jingo', |
1230 | + 'coffin', |
1231 | +] |
1232 | +if django.VERSION < (1, 8): |
1233 | + INSTALLED_APPS.append('jingo') |
1234 | + |
1235 | +STATICFILES_FINDERS = [ |
1236 | + 'django.contrib.staticfiles.finders.FileSystemFinder', |
1237 | + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', |
1238 | + 'compressor.finders.CompressorFinder', |
1239 | ] |
1240 | |
1241 | STATIC_URL = '/static/' |
1242 | @@ -37,3 +52,5 @@ |
1243 | PASSWORD_HASHERS = ( |
1244 | 'django.contrib.auth.hashers.UnsaltedMD5PasswordHasher', |
1245 | ) |
1246 | + |
1247 | +MIDDLEWARE_CLASSES = [] |
1248 | |
1249 | === modified file 'compressor/tests/precompiler.py' |
1250 | --- compressor/tests/precompiler.py 2014-06-26 15:08:13 +0000 |
1251 | +++ compressor/tests/precompiler.py 2015-08-20 18:49:21 +0000 |
1252 | @@ -7,11 +7,11 @@ |
1253 | def main(): |
1254 | p = optparse.OptionParser() |
1255 | p.add_option('-f', '--file', action="store", |
1256 | - type="string", dest="filename", |
1257 | - help="File to read from, defaults to stdin", default=None) |
1258 | + type="string", dest="filename", |
1259 | + help="File to read from, defaults to stdin", default=None) |
1260 | p.add_option('-o', '--output', action="store", |
1261 | - type="string", dest="outfile", |
1262 | - help="File to write to, defaults to stdout", default=None) |
1263 | + type="string", dest="outfile", |
1264 | + help="File to write to, defaults to stdout", default=None) |
1265 | |
1266 | options, arguments = p.parse_args() |
1267 | |
1268 | |
1269 | === added file 'compressor/tests/static/css/filename with spaces.css' |
1270 | --- compressor/tests/static/css/filename with spaces.css 1970-01-01 00:00:00 +0000 |
1271 | +++ compressor/tests/static/css/filename with spaces.css 2015-08-20 18:49:21 +0000 |
1272 | @@ -0,0 +1,1 @@ |
1273 | +body { background:#424242; } |
1274 | \ No newline at end of file |
1275 | |
1276 | === added file 'compressor/tests/static/css/utf-8_with-BOM.css' |
1277 | --- compressor/tests/static/css/utf-8_with-BOM.css 1970-01-01 00:00:00 +0000 |
1278 | +++ compressor/tests/static/css/utf-8_with-BOM.css 2015-08-20 18:49:21 +0000 |
1279 | @@ -0,0 +1,1 @@ |
1280 | +.compress-test {color: red;} |
1281 | \ No newline at end of file |
1282 | |
1283 | === added file 'compressor/tests/static/js/three.js' |
1284 | --- compressor/tests/static/js/three.js 1970-01-01 00:00:00 +0000 |
1285 | +++ compressor/tests/static/js/three.js 2015-08-20 18:49:21 +0000 |
1286 | @@ -0,0 +1,1 @@ |
1287 | +hermanos = {} |
1288 | \ No newline at end of file |
1289 | |
1290 | === added file 'compressor/tests/static/js/two.js' |
1291 | --- compressor/tests/static/js/two.js 1970-01-01 00:00:00 +0000 |
1292 | +++ compressor/tests/static/js/two.js 2015-08-20 18:49:21 +0000 |
1293 | @@ -0,0 +1,1 @@ |
1294 | +pollos = {} |
1295 | \ No newline at end of file |
1296 | |
1297 | === modified file 'compressor/tests/test_base.py' |
1298 | --- compressor/tests/test_base.py 2015-01-06 14:31:34 +0000 |
1299 | +++ compressor/tests/test_base.py 2015-08-20 18:49:21 +0000 |
1300 | @@ -12,11 +12,13 @@ |
1301 | from django.test import SimpleTestCase |
1302 | from django.test.utils import override_settings |
1303 | |
1304 | -from compressor.base import SOURCE_HUNK, SOURCE_FILE |
1305 | +from compressor import cache as cachemod |
1306 | +from compressor.base import SOURCE_FILE, SOURCE_HUNK |
1307 | +from compressor.cache import get_cachekey |
1308 | from compressor.conf import settings |
1309 | from compressor.css import CssCompressor |
1310 | +from compressor.exceptions import FilterDoesNotExist, FilterError |
1311 | from compressor.js import JsCompressor |
1312 | -from compressor.exceptions import FilterDoesNotExist |
1313 | |
1314 | |
1315 | def make_soup(markup): |
1316 | @@ -112,6 +114,14 @@ |
1317 | hunks = '\n'.join([h for h in self.css_node.hunks()]) |
1318 | self.assertEqual(out, hunks) |
1319 | |
1320 | + def test_css_output_with_bom_input(self): |
1321 | + out = 'body { background:#990; }\n.compress-test {color: red;}' |
1322 | + css = ("""<link rel="stylesheet" href="/static/css/one.css" type="text/css" /> |
1323 | + <link rel="stylesheet" href="/static/css/utf-8_with-BOM.css" type="text/css" />""") |
1324 | + css_node_with_bom = CssCompressor(css) |
1325 | + hunks = '\n'.join([h for h in css_node_with_bom.hunks()]) |
1326 | + self.assertEqual(out, hunks) |
1327 | + |
1328 | def test_css_mtimes(self): |
1329 | is_date = re.compile(r'^\d{10}[\.\d]+$') |
1330 | for date in self.css_node.mtimes: |
1331 | @@ -208,6 +218,14 @@ |
1332 | css_node = CssCompressor(css) |
1333 | self.assertRaises(FilterDoesNotExist, css_node.output, 'inline') |
1334 | |
1335 | + @override_settings(COMPRESS_PRECOMPILERS=( |
1336 | + ('text/foobar', './foo -I ./bar/baz'), |
1337 | + ), COMPRESS_ENABLED=True) |
1338 | + def test_command_with_dot_precompiler(self): |
1339 | + css = '<style type="text/foobar">p { border:10px solid red;}</style>' |
1340 | + css_node = CssCompressor(css) |
1341 | + self.assertRaises(FilterError, css_node.output, 'inline') |
1342 | + |
1343 | |
1344 | class CssMediaTestCase(SimpleTestCase): |
1345 | def setUp(self): |
1346 | @@ -267,4 +285,49 @@ |
1347 | |
1348 | def test_correct_backend(self): |
1349 | from compressor.cache import cache |
1350 | - self.assertEqual(cache.__class__, locmem.CacheClass) |
1351 | + self.assertEqual(cache.__class__, locmem.LocMemCache) |
1352 | + |
1353 | + |
1354 | +class JsAsyncDeferTestCase(SimpleTestCase): |
1355 | + def setUp(self): |
1356 | + self.js = """\ |
1357 | + <script src="/static/js/one.js" type="text/javascript"></script> |
1358 | + <script src="/static/js/two.js" type="text/javascript" async></script> |
1359 | + <script src="/static/js/three.js" type="text/javascript" defer></script> |
1360 | + <script type="text/javascript">obj.value = "value";</script> |
1361 | + <script src="/static/js/one.js" type="text/javascript" async></script> |
1362 | + <script src="/static/js/two.js" type="text/javascript" async></script> |
1363 | + <script src="/static/js/three.js" type="text/javascript"></script>""" |
1364 | + |
1365 | + def test_js_output(self): |
1366 | + def extract_attr(tag): |
1367 | + if tag.has_attr('async'): |
1368 | + return 'async' |
1369 | + if tag.has_attr('defer'): |
1370 | + return 'defer' |
1371 | + js_node = JsCompressor(self.js) |
1372 | + output = [None, 'async', 'defer', None, 'async', None] |
1373 | + if six.PY3: |
1374 | + scripts = make_soup(js_node.output()).find_all('script') |
1375 | + attrs = [extract_attr(i) for i in scripts] |
1376 | + else: |
1377 | + scripts = make_soup(js_node.output()).findAll('script') |
1378 | + attrs = [s.get('async') or s.get('defer') for s in scripts] |
1379 | + self.assertEqual(output, attrs) |
1380 | + |
1381 | + |
1382 | +class CacheTestCase(SimpleTestCase): |
1383 | + |
1384 | + def setUp(self): |
1385 | + cachemod._cachekey_func = None |
1386 | + |
1387 | + def test_get_cachekey_basic(self): |
1388 | + self.assertEqual(get_cachekey("foo"), "django_compressor.foo") |
1389 | + |
1390 | + @override_settings(COMPRESS_CACHE_KEY_FUNCTION='.leading.dot') |
1391 | + def test_get_cachekey_leading_dot(self): |
1392 | + self.assertRaises(ImportError, lambda: get_cachekey("foo")) |
1393 | + |
1394 | + @override_settings(COMPRESS_CACHE_KEY_FUNCTION='invalid.module') |
1395 | + def test_get_cachekey_invalid_mod(self): |
1396 | + self.assertRaises(ImportError, lambda: get_cachekey("foo")) |
1397 | |
1398 | === modified file 'compressor/tests/test_filters.py' |
1399 | --- compressor/tests/test_filters.py 2015-01-06 14:31:34 +0000 |
1400 | +++ compressor/tests/test_filters.py 2015-08-20 18:49:21 +0000 |
1401 | @@ -1,4 +1,5 @@ |
1402 | from __future__ import with_statement, unicode_literals |
1403 | +from collections import defaultdict |
1404 | import io |
1405 | import os |
1406 | import sys |
1407 | @@ -17,9 +18,18 @@ |
1408 | from compressor.filters.cssmin import CSSMinFilter |
1409 | from compressor.filters.css_default import CssAbsoluteFilter |
1410 | from compressor.filters.template import TemplateFilter |
1411 | +from compressor.filters.closure import ClosureCompilerFilter |
1412 | +from compressor.filters.csstidy import CSSTidyFilter |
1413 | +from compressor.filters.yuglify import YUglifyCSSFilter, YUglifyJSFilter |
1414 | +from compressor.filters.yui import YUICSSFilter, YUIJSFilter |
1415 | +from compressor.filters.cleancss import CleanCSSFilter |
1416 | from compressor.tests.test_base import test_dir |
1417 | |
1418 | |
1419 | +def blankdict(*args, **kwargs): |
1420 | + return defaultdict(lambda: '', *args, **kwargs) |
1421 | + |
1422 | + |
1423 | @unittest.skipIf(find_command(settings.COMPRESS_CSSTIDY_BINARY) is None, |
1424 | 'CSStidy binary %r not found' % settings.COMPRESS_CSSTIDY_BINARY) |
1425 | class CssTidyTestCase(TestCase): |
1426 | @@ -30,7 +40,6 @@ |
1427 | color: black; |
1428 | } |
1429 | """) |
1430 | - from compressor.filters.csstidy import CSSTidyFilter |
1431 | ret = CSSTidyFilter(content).input() |
1432 | self.assertIsInstance(ret, six.text_type) |
1433 | self.assertEqual( |
1434 | @@ -39,10 +48,13 @@ |
1435 | |
1436 | class PrecompilerTestCase(TestCase): |
1437 | def setUp(self): |
1438 | - self.filename = os.path.join(test_dir, 'static/css/one.css') |
1439 | + self.test_precompiler = os.path.join(test_dir, 'precompiler.py') |
1440 | + self.setup_infile() |
1441 | + |
1442 | + def setup_infile(self, filename='static/css/one.css'): |
1443 | + self.filename = os.path.join(test_dir, filename) |
1444 | with io.open(self.filename, encoding=settings.FILE_CHARSET) as file: |
1445 | self.content = file.read() |
1446 | - self.test_precompiler = os.path.join(test_dir, 'precompiler.py') |
1447 | |
1448 | def test_precompiler_infile_outfile(self): |
1449 | command = '%s %s -f {infile} -o {outfile}' % (sys.executable, self.test_precompiler) |
1450 | @@ -51,6 +63,14 @@ |
1451 | charset=settings.FILE_CHARSET, command=command) |
1452 | self.assertEqual("body { color:#990; }", compiler.input()) |
1453 | |
1454 | + def test_precompiler_infile_with_spaces(self): |
1455 | + self.setup_infile('static/css/filename with spaces.css') |
1456 | + command = '%s %s -f {infile} -o {outfile}' % (sys.executable, self.test_precompiler) |
1457 | + compiler = CompilerFilter( |
1458 | + content=self.content, filename=self.filename, |
1459 | + charset=settings.FILE_CHARSET, command=command) |
1460 | + self.assertEqual("body { color:#424242; }", compiler.input()) |
1461 | + |
1462 | def test_precompiler_infile_stdout(self): |
1463 | command = '%s %s -f {infile}' % (sys.executable, self.test_precompiler) |
1464 | compiler = CompilerFilter( |
1465 | @@ -99,8 +119,8 @@ |
1466 | class CssAbsolutizingTestCase(TestCase): |
1467 | hashing_method = 'mtime' |
1468 | hashing_func = staticmethod(get_hashed_mtime) |
1469 | - content = ("p { background: url('../../img/python.png') }" |
1470 | - "p { filter: Alpha(src='../../img/python.png') }") |
1471 | + template = ("p { background: url('%(url)simg/python.png%(query)s%(hash)s%(frag)s') }" |
1472 | + "p { filter: Alpha(src='%(url)simg/python.png%(query)s%(hash)s%(frag)s') }") |
1473 | |
1474 | def setUp(self): |
1475 | self.old_enabled = settings.COMPRESS_ENABLED |
1476 | @@ -120,40 +140,55 @@ |
1477 | settings.COMPRESS_URL = self.old_url |
1478 | settings.COMPRESS_CSS_HASHING_METHOD = self.old_hashing_method |
1479 | |
1480 | + def test_css_no_hash(self): |
1481 | + settings.COMPRESS_CSS_HASHING_METHOD = None |
1482 | + filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css') |
1483 | + content = self.template % blankdict(url='../../') |
1484 | + params = blankdict({ |
1485 | + 'url': settings.COMPRESS_URL, |
1486 | + }) |
1487 | + output = self.template % params |
1488 | + filter = CssAbsoluteFilter(content) |
1489 | + self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css')) |
1490 | + |
1491 | + settings.COMPRESS_URL = params['url'] = 'http://static.example.com/' |
1492 | + output = self.template % params |
1493 | + filter = CssAbsoluteFilter(content) |
1494 | + self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css')) |
1495 | + |
1496 | def test_css_absolute_filter(self): |
1497 | filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css') |
1498 | imagefilename = os.path.join(settings.COMPRESS_ROOT, 'img/python.png') |
1499 | - params = { |
1500 | + content = self.template % blankdict(url='../../') |
1501 | + params = blankdict({ |
1502 | 'url': settings.COMPRESS_URL, |
1503 | - 'hash': self.hashing_func(imagefilename), |
1504 | - } |
1505 | - output = ("p { background: url('%(url)simg/python.png?%(hash)s') }" |
1506 | - "p { filter: Alpha(src='%(url)simg/python.png?%(hash)s') }") % params |
1507 | - filter = CssAbsoluteFilter(self.content) |
1508 | + 'hash': '?' + self.hashing_func(imagefilename), |
1509 | + }) |
1510 | + output = self.template % params |
1511 | + filter = CssAbsoluteFilter(content) |
1512 | self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css')) |
1513 | + |
1514 | settings.COMPRESS_URL = params['url'] = 'http://static.example.com/' |
1515 | - filter = CssAbsoluteFilter(self.content) |
1516 | - filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css') |
1517 | - output = ("p { background: url('%(url)simg/python.png?%(hash)s') }" |
1518 | - "p { filter: Alpha(src='%(url)simg/python.png?%(hash)s') }") % params |
1519 | + output = self.template % params |
1520 | + filter = CssAbsoluteFilter(content) |
1521 | self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css')) |
1522 | |
1523 | def test_css_absolute_filter_url_fragment(self): |
1524 | filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css') |
1525 | imagefilename = os.path.join(settings.COMPRESS_ROOT, 'img/python.png') |
1526 | - params = { |
1527 | + content = self.template % blankdict(url='../../', frag='#foo') |
1528 | + params = blankdict({ |
1529 | 'url': settings.COMPRESS_URL, |
1530 | - 'hash': self.hashing_func(imagefilename), |
1531 | - } |
1532 | - content = "p { background: url('../../img/python.png#foo') }" |
1533 | - |
1534 | - output = "p { background: url('%(url)simg/python.png?%(hash)s#foo') }" % params |
1535 | + 'hash': '?' + self.hashing_func(imagefilename), |
1536 | + 'frag': '#foo', |
1537 | + }) |
1538 | + output = self.template % params |
1539 | filter = CssAbsoluteFilter(content) |
1540 | self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css')) |
1541 | + |
1542 | settings.COMPRESS_URL = params['url'] = 'http://media.example.com/' |
1543 | + output = self.template % params |
1544 | filter = CssAbsoluteFilter(content) |
1545 | - filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css') |
1546 | - output = "p { background: url('%(url)simg/python.png?%(hash)s#foo') }" % params |
1547 | self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css')) |
1548 | |
1549 | def test_css_absolute_filter_only_url_fragment(self): |
1550 | @@ -161,64 +196,78 @@ |
1551 | content = "p { background: url('#foo') }" |
1552 | filter = CssAbsoluteFilter(content) |
1553 | self.assertEqual(content, filter.input(filename=filename, basename='css/url/test.css')) |
1554 | + |
1555 | settings.COMPRESS_URL = 'http://media.example.com/' |
1556 | filter = CssAbsoluteFilter(content) |
1557 | - filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css') |
1558 | self.assertEqual(content, filter.input(filename=filename, basename='css/url/test.css')) |
1559 | |
1560 | def test_css_absolute_filter_querystring(self): |
1561 | filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css') |
1562 | imagefilename = os.path.join(settings.COMPRESS_ROOT, 'img/python.png') |
1563 | - params = { |
1564 | + content = self.template % blankdict(url='../../', query='?foo') |
1565 | + params = blankdict({ |
1566 | 'url': settings.COMPRESS_URL, |
1567 | - 'hash': self.hashing_func(imagefilename), |
1568 | - } |
1569 | - content = "p { background: url('../../img/python.png?foo') }" |
1570 | - |
1571 | - output = "p { background: url('%(url)simg/python.png?foo&%(hash)s') }" % params |
1572 | + 'query': '?foo', |
1573 | + 'hash': '&' + self.hashing_func(imagefilename), |
1574 | + }) |
1575 | + output = self.template % params |
1576 | filter = CssAbsoluteFilter(content) |
1577 | self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css')) |
1578 | + |
1579 | settings.COMPRESS_URL = params['url'] = 'http://media.example.com/' |
1580 | + output = self.template % params |
1581 | filter = CssAbsoluteFilter(content) |
1582 | - filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css') |
1583 | - output = "p { background: url('%(url)simg/python.png?foo&%(hash)s') }" % params |
1584 | self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css')) |
1585 | |
1586 | def test_css_absolute_filter_https(self): |
1587 | filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css') |
1588 | imagefilename = os.path.join(settings.COMPRESS_ROOT, 'img/python.png') |
1589 | - params = { |
1590 | + content = self.template % blankdict(url='../../') |
1591 | + params = blankdict({ |
1592 | 'url': settings.COMPRESS_URL, |
1593 | - 'hash': self.hashing_func(imagefilename), |
1594 | - } |
1595 | - output = ("p { background: url('%(url)simg/python.png?%(hash)s') }" |
1596 | - "p { filter: Alpha(src='%(url)simg/python.png?%(hash)s') }") % params |
1597 | - filter = CssAbsoluteFilter(self.content) |
1598 | + 'hash': '?' + self.hashing_func(imagefilename), |
1599 | + }) |
1600 | + output = self.template % params |
1601 | + filter = CssAbsoluteFilter(content) |
1602 | self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css')) |
1603 | + |
1604 | settings.COMPRESS_URL = params['url'] = 'https://static.example.com/' |
1605 | - filter = CssAbsoluteFilter(self.content) |
1606 | - filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css') |
1607 | - output = ("p { background: url('%(url)simg/python.png?%(hash)s') }" |
1608 | - "p { filter: Alpha(src='%(url)simg/python.png?%(hash)s') }") % params |
1609 | + output = self.template % params |
1610 | + filter = CssAbsoluteFilter(content) |
1611 | self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css')) |
1612 | |
1613 | def test_css_absolute_filter_relative_path(self): |
1614 | filename = os.path.join(settings.TEST_DIR, 'whatever', '..', 'static', 'whatever/../css/url/test.css') |
1615 | imagefilename = os.path.join(settings.COMPRESS_ROOT, 'img/python.png') |
1616 | - params = { |
1617 | + content = self.template % blankdict(url='../../') |
1618 | + params = blankdict({ |
1619 | 'url': settings.COMPRESS_URL, |
1620 | - 'hash': self.hashing_func(imagefilename), |
1621 | - } |
1622 | - output = ("p { background: url('%(url)simg/python.png?%(hash)s') }" |
1623 | - "p { filter: Alpha(src='%(url)simg/python.png?%(hash)s') }") % params |
1624 | - filter = CssAbsoluteFilter(self.content) |
1625 | + 'hash': '?' + self.hashing_func(imagefilename), |
1626 | + }) |
1627 | + output = self.template % params |
1628 | + filter = CssAbsoluteFilter(content) |
1629 | self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css')) |
1630 | + |
1631 | settings.COMPRESS_URL = params['url'] = 'https://static.example.com/' |
1632 | - filter = CssAbsoluteFilter(self.content) |
1633 | - output = ("p { background: url('%(url)simg/python.png?%(hash)s') }" |
1634 | - "p { filter: Alpha(src='%(url)simg/python.png?%(hash)s') }") % params |
1635 | + output = self.template % params |
1636 | + filter = CssAbsoluteFilter(content) |
1637 | self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css')) |
1638 | |
1639 | + def test_css_absolute_filter_filename_outside_compress_root(self): |
1640 | + filename = '/foo/bar/baz/test.css' |
1641 | + content = self.template % blankdict(url='../qux/') |
1642 | + params = blankdict({ |
1643 | + 'url': settings.COMPRESS_URL + 'bar/qux/', |
1644 | + }) |
1645 | + output = self.template % params |
1646 | + filter = CssAbsoluteFilter(content) |
1647 | + self.assertEqual(output, filter.input(filename=filename, basename='bar/baz/test.css')) |
1648 | + settings.COMPRESS_URL = 'https://static.example.com/' |
1649 | + params['url'] = settings.COMPRESS_URL + 'bar/qux/' |
1650 | + output = self.template % params |
1651 | + filter = CssAbsoluteFilter(content) |
1652 | + self.assertEqual(output, filter.input(filename=filename, basename='bar/baz/test.css')) |
1653 | + |
1654 | def test_css_hunks(self): |
1655 | hash_dict = { |
1656 | 'hash1': self.hashing_func(os.path.join(settings.COMPRESS_ROOT, 'img/python.png')), |
1657 | @@ -253,14 +302,6 @@ |
1658 | hashing_method = 'content' |
1659 | hashing_func = staticmethod(get_hashed_content) |
1660 | |
1661 | - def setUp(self): |
1662 | - super(CssAbsolutizingTestCaseWithHash, self).setUp() |
1663 | - self.css = """ |
1664 | - <link rel="stylesheet" href="/static/css/url/url1.css" type="text/css" charset="utf-8"> |
1665 | - <link rel="stylesheet" href="/static/css/url/2/url2.css" type="text/css" charset="utf-8"> |
1666 | - """ |
1667 | - self.css_node = CssCompressor(self.css) |
1668 | - |
1669 | |
1670 | class CssDataUriTestCase(TestCase): |
1671 | def setUp(self): |
1672 | @@ -301,3 +342,38 @@ |
1673 | #footer {font-weight: bold;} |
1674 | """ |
1675 | self.assertEqual(input, TemplateFilter(content).input()) |
1676 | + |
1677 | + |
1678 | +class SpecializedFiltersTest(TestCase): |
1679 | + """ |
1680 | + Test to check the Specializations of filters. |
1681 | + """ |
1682 | + def test_closure_filter(self): |
1683 | + filter = ClosureCompilerFilter('') |
1684 | + self.assertEqual(filter.options, (('binary', six.text_type('java -jar compiler.jar')), ('args', six.text_type('')))) |
1685 | + |
1686 | + def test_csstidy_filter(self): |
1687 | + filter = CSSTidyFilter('') |
1688 | + self.assertEqual(filter.options, (('binary', six.text_type('csstidy')), ('args', six.text_type('--template=highest')))) |
1689 | + |
1690 | + def test_yuglify_filters(self): |
1691 | + filter = YUglifyCSSFilter('') |
1692 | + self.assertEqual(filter.command, '{binary} {args} --type=css') |
1693 | + self.assertEqual(filter.options, (('binary', six.text_type('yuglify')), ('args', six.text_type('--terminal')))) |
1694 | + |
1695 | + filter = YUglifyJSFilter('') |
1696 | + self.assertEqual(filter.command, '{binary} {args} --type=js') |
1697 | + self.assertEqual(filter.options, (('binary', six.text_type('yuglify')), ('args', six.text_type('--terminal')))) |
1698 | + |
1699 | + def test_yui_filters(self): |
1700 | + filter = YUICSSFilter('') |
1701 | + self.assertEqual(filter.command, '{binary} {args} --type=css') |
1702 | + self.assertEqual(filter.options, (('binary', six.text_type('java -jar yuicompressor.jar')), ('args', six.text_type('')))) |
1703 | + |
1704 | + filter = YUIJSFilter('', verbose=1) |
1705 | + self.assertEqual(filter.command, '{binary} {args} --type=js --verbose') |
1706 | + self.assertEqual(filter.options, (('binary', six.text_type('java -jar yuicompressor.jar')), ('args', six.text_type('')), ('verbose', 1))) |
1707 | + |
1708 | + def test_clean_css_filter(self): |
1709 | + filter = CleanCSSFilter('') |
1710 | + self.assertEqual(filter.options, (('binary', six.text_type('cleancss')), ('args', six.text_type('')))) |
1711 | |
1712 | === modified file 'compressor/tests/test_jinja2ext.py' |
1713 | --- compressor/tests/test_jinja2ext.py 2015-01-06 14:31:34 +0000 |
1714 | +++ compressor/tests/test_jinja2ext.py 2015-08-20 18:49:21 +0000 |
1715 | @@ -65,8 +65,7 @@ |
1716 | self.assertEqual(tag_body, template.render()) |
1717 | |
1718 | def test_empty_tag(self): |
1719 | - template = self.env.from_string("""{% compress js %}{% block js %} |
1720 | - {% endblock %}{% endcompress %}""") |
1721 | + template = self.env.from_string("""{% compress js %}{% block js %}{% endblock %}{% endcompress %}""") |
1722 | context = {'STATIC_URL': settings.COMPRESS_URL} |
1723 | self.assertEqual('', template.render(context)) |
1724 | |
1725 | |
1726 | === modified file 'compressor/tests/test_offline.py' |
1727 | --- compressor/tests/test_offline.py 2015-01-06 14:31:34 +0000 |
1728 | +++ compressor/tests/test_offline.py 2015-08-20 18:49:21 +0000 |
1729 | @@ -3,6 +3,7 @@ |
1730 | import os |
1731 | import sys |
1732 | |
1733 | +import django |
1734 | from django.core.management.base import CommandError |
1735 | from django.template import Template, Context |
1736 | from django.test import TestCase |
1737 | @@ -44,10 +45,6 @@ |
1738 | engines = ("django",) |
1739 | |
1740 | def setUp(self): |
1741 | - self._old_compress = settings.COMPRESS_ENABLED |
1742 | - self._old_compress_offline = settings.COMPRESS_OFFLINE |
1743 | - self._old_template_dirs = settings.TEMPLATE_DIRS |
1744 | - self._old_offline_context = settings.COMPRESS_OFFLINE_CONTEXT |
1745 | self.log = StringIO() |
1746 | |
1747 | # Reset template dirs, because it enables us to force compress to |
1748 | @@ -58,11 +55,18 @@ |
1749 | # template to be skipped over. |
1750 | django_template_dir = os.path.join(settings.TEST_DIR, 'test_templates', self.templates_dir) |
1751 | jinja2_template_dir = os.path.join(settings.TEST_DIR, 'test_templates_jinja2', self.templates_dir) |
1752 | - settings.TEMPLATE_DIRS = (django_template_dir, jinja2_template_dir) |
1753 | - |
1754 | - # Enable offline compress |
1755 | - settings.COMPRESS_ENABLED = True |
1756 | - settings.COMPRESS_OFFLINE = True |
1757 | + |
1758 | + override_settings = { |
1759 | + 'TEMPLATE_DIRS': (django_template_dir, jinja2_template_dir,), |
1760 | + 'COMPRESS_ENABLED': True, |
1761 | + 'COMPRESS_OFFLINE': True |
1762 | + } |
1763 | + |
1764 | + if "jinja2" in self.engines: |
1765 | + override_settings["COMPRESS_JINJA2_GET_ENVIRONMENT"] = lambda: self._get_jinja2_env() |
1766 | + |
1767 | + self.override_settings = self.settings(**override_settings) |
1768 | + self.override_settings.__enter__() |
1769 | |
1770 | if "django" in self.engines: |
1771 | self.template_path = os.path.join(django_template_dir, self.template_name) |
1772 | @@ -70,22 +74,16 @@ |
1773 | with io.open(self.template_path, encoding=settings.FILE_CHARSET) as file: |
1774 | self.template = Template(file.read()) |
1775 | |
1776 | - self._old_jinja2_get_environment = settings.COMPRESS_JINJA2_GET_ENVIRONMENT |
1777 | - |
1778 | if "jinja2" in self.engines: |
1779 | - # Setup Jinja2 settings. |
1780 | - settings.COMPRESS_JINJA2_GET_ENVIRONMENT = lambda: self._get_jinja2_env() |
1781 | - jinja2_env = settings.COMPRESS_JINJA2_GET_ENVIRONMENT() |
1782 | + jinja2_env = override_settings["COMPRESS_JINJA2_GET_ENVIRONMENT"]() |
1783 | self.template_path_jinja2 = os.path.join(jinja2_template_dir, self.template_name) |
1784 | |
1785 | with io.open(self.template_path_jinja2, encoding=settings.FILE_CHARSET) as file: |
1786 | self.template_jinja2 = jinja2_env.from_string(file.read()) |
1787 | |
1788 | def tearDown(self): |
1789 | - settings.COMPRESS_JINJA2_GET_ENVIRONMENT = self._old_jinja2_get_environment |
1790 | - settings.COMPRESS_ENABLED = self._old_compress |
1791 | - settings.COMPRESS_OFFLINE = self._old_compress_offline |
1792 | - settings.TEMPLATE_DIRS = self._old_template_dirs |
1793 | + self.override_settings.__exit__(None, None, None) |
1794 | + |
1795 | manifest_path = os.path.join('CACHE', 'manifest.json') |
1796 | if default_storage.exists(manifest_path): |
1797 | default_storage.delete(manifest_path) |
1798 | @@ -452,9 +450,10 @@ |
1799 | # from library import * |
1800 | # causing 'ImportError: No module named library'. |
1801 | # It seems there is no evidence nor indicated support for Python 3+. |
1802 | -@unittest.skip("Coffin tests disable under Ubuntu build") |
1803 | @unittest.skipIf(sys.version_info >= (3, 2), |
1804 | "Coffin does not support 3.2+") |
1805 | +@unittest.skipIf(django.VERSION >= (1, 8), |
1806 | + "Import error on 1.8") |
1807 | class OfflineGenerationCoffinTestCase(OfflineTestCaseMixin, TestCase): |
1808 | templates_dir = "test_coffin" |
1809 | expected_hash = "32c8281e3346" |
1810 | @@ -479,6 +478,8 @@ |
1811 | # is also evident in its tox.ini file. |
1812 | @unittest.skipIf(sys.version_info >= (3, 2) and sys.version_info < (3, 3), |
1813 | "Jingo does not support 3.2") |
1814 | +@unittest.skipIf(django.VERSION >= (1, 8), |
1815 | + "Import error on 1.8") |
1816 | class OfflineGenerationJingoTestCase(OfflineTestCaseMixin, TestCase): |
1817 | templates_dir = "test_jingo" |
1818 | expected_hash = "61ec584468eb" |
1819 | |
1820 | === modified file 'compressor/tests/test_templates/test_block_super_base_compressed/test_compressor_offline.html' |
1821 | --- compressor/tests/test_templates/test_block_super_base_compressed/test_compressor_offline.html 2014-06-26 15:08:13 +0000 |
1822 | +++ compressor/tests/test_templates/test_block_super_base_compressed/test_compressor_offline.html 2015-08-20 18:49:21 +0000 |
1823 | @@ -5,4 +5,9 @@ |
1824 | <script type="text/javascript"> |
1825 | alert("this alert shouldn't be alone!"); |
1826 | </script> |
1827 | + {% block orphan %} |
1828 | + {{ block.super }} |
1829 | + An 'orphan' block that refers to a non-existent super block. |
1830 | + Contents of this block are ignored. |
1831 | + {% endblock %} |
1832 | {% endspaceless %}{% endblock %} |
1833 | |
1834 | === modified file 'compressor/utils/staticfiles.py' |
1835 | --- compressor/utils/staticfiles.py 2014-06-26 15:08:13 +0000 |
1836 | +++ compressor/utils/staticfiles.py 2015-08-20 18:49:21 +0000 |
1837 | @@ -4,20 +4,10 @@ |
1838 | |
1839 | from compressor.conf import settings |
1840 | |
1841 | -INSTALLED = ("staticfiles" in settings.INSTALLED_APPS or |
1842 | - "django.contrib.staticfiles" in settings.INSTALLED_APPS) |
1843 | - |
1844 | -if INSTALLED: |
1845 | - if "django.contrib.staticfiles" in settings.INSTALLED_APPS: |
1846 | - from django.contrib.staticfiles import finders |
1847 | - else: |
1848 | - try: |
1849 | - from staticfiles import finders # noqa |
1850 | - except ImportError: |
1851 | - # Old (pre 1.0) and incompatible version of staticfiles |
1852 | - INSTALLED = False |
1853 | - |
1854 | - if (INSTALLED and "compressor.finders.CompressorFinder" |
1855 | +if "django.contrib.staticfiles" in settings.INSTALLED_APPS: |
1856 | + from django.contrib.staticfiles import finders # noqa |
1857 | + |
1858 | + if ("compressor.finders.CompressorFinder" |
1859 | not in settings.STATICFILES_FINDERS): |
1860 | raise ImproperlyConfigured( |
1861 | "When using Django Compressor together with staticfiles, " |
1862 | |
1863 | === modified file 'debian/changelog' |
1864 | --- debian/changelog 2015-01-06 14:31:34 +0000 |
1865 | +++ debian/changelog 2015-08-20 18:49:21 +0000 |
1866 | @@ -1,3 +1,23 @@ |
1867 | +python-django-compressor (1.5-1ubuntu1) UNRELEASED; urgency=low |
1868 | + |
1869 | + * Merge from Debian unstable. Remaining changes: |
1870 | + - d/control: Drop BD on python-coffin. |
1871 | + - d/p/disable-coffin-tests.patch: Rebase. |
1872 | + * Fix d/watch |
1873 | + |
1874 | + -- David Ames <david.ames@canonical.com> Tue, 04 Aug 2015 16:11:41 +0000 |
1875 | + |
1876 | +python-django-compressor (1.5-1) unstable; urgency=medium |
1877 | + |
1878 | + * New upstream release. |
1879 | + * Added Python 3 support. |
1880 | + * Removed django 1.7 patche. |
1881 | + * Added patch to not run a unit test that fails: |
1882 | + - compressor.tests.test_base.JsAsyncDeferTestCase |
1883 | + * Fixed PyPi watch file. |
1884 | + |
1885 | + -- Thomas Goirand <zigo@debian.org> Tue, 04 Aug 2015 08:17:03 +0000 |
1886 | + |
1887 | python-django-compressor (1.4-2ubuntu3) vivid; urgency=medium |
1888 | |
1889 | * d/control: Drop BD on python-coffin. |
1890 | |
1891 | === modified file 'debian/control' |
1892 | --- debian/control 2015-01-06 14:31:34 +0000 |
1893 | +++ debian/control 2015-08-20 18:49:21 +0000 |
1894 | @@ -5,34 +5,66 @@ |
1895 | XSBC-Original-Maintainer: PKG OpenStack <openstack-devel@lists.alioth.debian.org> |
1896 | Uploaders: Thomas Goirand <zigo@debian.org>, |
1897 | Build-Depends: debhelper (>= 9), |
1898 | + dh-python, |
1899 | openstack-pkg-tools, |
1900 | - python-all (>= 2.6.6-3~), |
1901 | - python-setuptools |
1902 | -Build-Depends-Indep: python-appconf, |
1903 | + python-all, |
1904 | + python-setuptools, |
1905 | + python3-all, |
1906 | + python3-setuptools |
1907 | +Build-Depends-Indep: csstidy, |
1908 | + python-appconf, |
1909 | python-bs4, |
1910 | python-coverage, |
1911 | - python-django (>= 1.6), |
1912 | + python-django, |
1913 | + python-django-discover-runner, |
1914 | python-html5lib, |
1915 | python-jingo, |
1916 | python-jinja2, |
1917 | python-lxml, |
1918 | python-mock, |
1919 | python-nose, |
1920 | - python-unittest2 |
1921 | -Standards-Version: 3.9.5 |
1922 | + python-unittest2, |
1923 | + python3-appconf, |
1924 | + python3-bs4, |
1925 | + python3-coverage, |
1926 | + python3-django, |
1927 | + python3-django-discover-runner, |
1928 | + python3-html5lib, |
1929 | + python3-jingo, |
1930 | + python3-jinja2, |
1931 | + python3-lxml, |
1932 | + python3-mock, |
1933 | + python3-nose, |
1934 | + python3-unittest2 |
1935 | +Standards-Version: 3.9.6 |
1936 | Vcs-Browser: http://anonscm.debian.org/gitweb/?p=openstack/python-django-compressor.git;a=summary |
1937 | Vcs-Git: git://anonscm.debian.org/openstack/python-django-compressor.git |
1938 | Homepage: http://pypi.python.org/pypi/django_compressor/ |
1939 | |
1940 | Package: python-compressor |
1941 | Architecture: all |
1942 | -Pre-Depends: dpkg (>= 1.15.6~) |
1943 | Depends: python-appconf, |
1944 | - python-django (>= 1.1), |
1945 | + python-django, |
1946 | ${misc:Depends}, |
1947 | ${python:Depends} |
1948 | Provides: ${python:Provides} |
1949 | -Description: Compresses linked and inline JavaScript or CSS into single cached files |
1950 | - Django Compressor combines and compresses linked and inline Javascript or CSS |
1951 | - in a Django templates into cacheable static files by using the compress |
1952 | - template tag. |
1953 | +Description: Compresses linked, inline JS or CSS into single cached files - Python 2.7 |
1954 | + Django Compressor combines and compresses linked and inline Javascript or CSS |
1955 | + in a Django templates into cacheable static files by using the compress |
1956 | + template tag. |
1957 | + . |
1958 | + This package contains the Python 2.7 module. |
1959 | + |
1960 | +Package: python3-compressor |
1961 | +Architecture: all |
1962 | +Depends: python3-appconf, |
1963 | + python3-django, |
1964 | + ${misc:Depends}, |
1965 | + ${python3:Depends} |
1966 | +Provides: ${python:Provides} |
1967 | +Description: Compresses linked, inline JS or CSS into single cached files - Python 3.x |
1968 | + Django Compressor combines and compresses linked and inline Javascript or CSS |
1969 | + in a Django templates into cacheable static files by using the compress |
1970 | + template tag. |
1971 | + . |
1972 | + This package contains the Python 3.x module. |
1973 | |
1974 | === modified file 'debian/gbp.conf' |
1975 | --- debian/gbp.conf 2013-05-12 15:20:14 +0000 |
1976 | +++ debian/gbp.conf 2015-08-20 18:49:21 +0000 |
1977 | @@ -4,5 +4,5 @@ |
1978 | upstream-tag = %(version)s |
1979 | compression = xz |
1980 | |
1981 | -[git-buildpackage] |
1982 | +[buildpackage] |
1983 | export-dir = ../build-area/ |
1984 | |
1985 | === modified file 'debian/patches/disable-coffin-tests.patch' |
1986 | --- debian/patches/disable-coffin-tests.patch 2015-01-06 12:34:51 +0000 |
1987 | +++ debian/patches/disable-coffin-tests.patch 2015-08-20 18:49:21 +0000 |
1988 | @@ -1,20 +1,20 @@ |
1989 | --- a/compressor/test_settings.py |
1990 | +++ b/compressor/test_settings.py |
1991 | -@@ -14,7 +14,6 @@ DATABASES = { |
1992 | - |
1993 | +@@ -21,7 +21,6 @@ |
1994 | INSTALLED_APPS = [ |
1995 | + 'django.contrib.staticfiles', |
1996 | 'compressor', |
1997 | - 'coffin', |
1998 | - 'jingo', |
1999 | ] |
2000 | - |
2001 | + if django.VERSION < (1, 8): |
2002 | + INSTALLED_APPS.append('jingo') |
2003 | --- a/compressor/tests/test_offline.py |
2004 | +++ b/compressor/tests/test_offline.py |
2005 | -@@ -452,6 +452,7 @@ class OfflineGenerationComplexTestCase(O |
2006 | +@@ -450,6 +450,7 @@ |
2007 | # from library import * |
2008 | # causing 'ImportError: No module named library'. |
2009 | # It seems there is no evidence nor indicated support for Python 3+. |
2010 | +@unittest.skip("Coffin tests disable under Ubuntu build") |
2011 | @unittest.skipIf(sys.version_info >= (3, 2), |
2012 | "Coffin does not support 3.2+") |
2013 | - class OfflineGenerationCoffinTestCase(OfflineTestCaseMixin, TestCase): |
2014 | + @unittest.skipIf(django.VERSION >= (1, 8), |
2015 | |
2016 | === removed file 'debian/patches/fix-test_settings.py-for-django-1.7.patch' |
2017 | --- debian/patches/fix-test_settings.py-for-django-1.7.patch 2014-09-08 17:31:54 +0000 |
2018 | +++ debian/patches/fix-test_settings.py-for-django-1.7.patch 1970-01-01 00:00:00 +0000 |
2019 | @@ -1,16 +0,0 @@ |
2020 | -Description: Fix test_settings.py for Django 1.7 compat |
2021 | -Author: Thomas Goirand <zigo@debian.org> |
2022 | -Forwarded: no |
2023 | -Last-Update: 2014-09-09 |
2024 | - |
2025 | ---- python-django-compressor-1.4.orig/compressor/test_settings.py |
2026 | -+++ python-django-compressor-1.4/compressor/test_settings.py |
2027 | -@@ -3,7 +3,7 @@ import django |
2028 | - |
2029 | - TEST_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'tests') |
2030 | - |
2031 | --COMPRESS_CACHE_BACKEND = 'locmem://' |
2032 | -+COMPRESS_CACHE_BACKEND = 'django.core.cache.backends.locmem.CacheClass' |
2033 | - |
2034 | - DATABASES = { |
2035 | - 'default': { |
2036 | |
2037 | === added file 'debian/patches/remove-failed-test.patch' |
2038 | --- debian/patches/remove-failed-test.patch 1970-01-01 00:00:00 +0000 |
2039 | +++ debian/patches/remove-failed-test.patch 2015-08-20 18:49:21 +0000 |
2040 | @@ -0,0 +1,43 @@ |
2041 | +Description: Removes failed test |
2042 | + This unit test is failing, so removing it to build the package. |
2043 | +Author: Thomas Goirand <zigo@debian.org> |
2044 | +Forwarded: no |
2045 | +Last-Update: 2015-08-04 |
2046 | + |
2047 | +--- python-django-compressor-1.5.orig/compressor/tests/test_base.py |
2048 | ++++ python-django-compressor-1.5/compressor/tests/test_base.py |
2049 | +@@ -288,34 +288,6 @@ class CacheBackendTestCase(CompressorTes |
2050 | + self.assertEqual(cache.__class__, locmem.LocMemCache) |
2051 | + |
2052 | + |
2053 | +-class JsAsyncDeferTestCase(SimpleTestCase): |
2054 | +- def setUp(self): |
2055 | +- self.js = """\ |
2056 | +- <script src="/static/js/one.js" type="text/javascript"></script> |
2057 | +- <script src="/static/js/two.js" type="text/javascript" async></script> |
2058 | +- <script src="/static/js/three.js" type="text/javascript" defer></script> |
2059 | +- <script type="text/javascript">obj.value = "value";</script> |
2060 | +- <script src="/static/js/one.js" type="text/javascript" async></script> |
2061 | +- <script src="/static/js/two.js" type="text/javascript" async></script> |
2062 | +- <script src="/static/js/three.js" type="text/javascript"></script>""" |
2063 | +- |
2064 | +- def test_js_output(self): |
2065 | +- def extract_attr(tag): |
2066 | +- if tag.has_attr('async'): |
2067 | +- return 'async' |
2068 | +- if tag.has_attr('defer'): |
2069 | +- return 'defer' |
2070 | +- js_node = JsCompressor(self.js) |
2071 | +- output = [None, 'async', 'defer', None, 'async', None] |
2072 | +- if six.PY3: |
2073 | +- scripts = make_soup(js_node.output()).find_all('script') |
2074 | +- attrs = [extract_attr(i) for i in scripts] |
2075 | +- else: |
2076 | +- scripts = make_soup(js_node.output()).findAll('script') |
2077 | +- attrs = [s.get('async') or s.get('defer') for s in scripts] |
2078 | +- self.assertEqual(output, attrs) |
2079 | +- |
2080 | +- |
2081 | + class CacheTestCase(SimpleTestCase): |
2082 | + |
2083 | + def setUp(self): |
2084 | |
2085 | === modified file 'debian/patches/series' |
2086 | --- debian/patches/series 2015-01-06 12:34:51 +0000 |
2087 | +++ debian/patches/series 2015-08-20 18:49:21 +0000 |
2088 | @@ -1,2 +1,2 @@ |
2089 | -fix-test_settings.py-for-django-1.7.patch |
2090 | disable-coffin-tests.patch |
2091 | +remove-failed-test.patch |
2092 | |
2093 | === modified file 'debian/rules' |
2094 | --- debian/rules 2014-12-05 10:34:59 +0000 |
2095 | +++ debian/rules 2015-08-20 18:49:21 +0000 |
2096 | @@ -1,13 +1,14 @@ |
2097 | #!/usr/bin/make -f |
2098 | |
2099 | -#export DH_VERBOSE=1 |
2100 | +PYTHONS:=$(shell pyversions -vr) |
2101 | +PYTHON3S:=$(shell py3versions -vr) |
2102 | |
2103 | UPSTREAM_GIT = git://github.com/jezdez/django_compressor.git |
2104 | |
2105 | include /usr/share/openstack-pkg-tools/pkgos.make |
2106 | |
2107 | %: |
2108 | - dh $@ --with python2 |
2109 | + dh $@ --buildsystem=python_distutils --with python2,python3 |
2110 | |
2111 | override_dh_clean: |
2112 | dh_clean |
2113 | @@ -15,24 +16,27 @@ |
2114 | |
2115 | override_dh_auto_test: |
2116 | ifeq (,$(findstring nocheck, $(DEB_BUILD_OPTIONS))) |
2117 | - PYTHONPATH=. python-coverage run --branch --source=compressor /usr/bin/django-admin test --settings=compressor.test_settings compressor |
2118 | - #PYTHONPATH=$PYTHONPATH:. python /usr/share/pyshared/django/bin/django-admin.py test --settings=compressor.test_settings compressor || true |
2119 | + PYTHON=python2 PYTHONPATH=. python-coverage run --branch --source=compressor /usr/bin/django-admin test --settings=compressor.test_settings compressor |
2120 | rm -rf $(CURDIR)/compressor/tests/static/CACHE |
2121 | + # TODO: make unit tests to work. |
2122 | +# PYTHON=python3 PYTHONPATH=. python3-coverage run --branch --source=compressor /usr/bin/django-admin test --settings=compressor.test_settings compressor |
2123 | +# rm -rf $(CURDIR)/compressor/tests/static/CACHE |
2124 | endif |
2125 | |
2126 | override_dh_auto_build: |
2127 | |
2128 | override_dh_install: |
2129 | - set -e ; for i in `pyversions -s` ; do \ |
2130 | - $$i setup.py install --install-layout=deb --root=debian/python-compressor ; \ |
2131 | - rm -f $(CURDIR)/debian/usr/lib/$$i/dist-packages/compressor/tests/static/CACHE/css/* ; \ |
2132 | - rm -f $(CURDIR)/debian/usr/lib/$$i/dist-packages/compressor/tests/static/CACHE/js/* ; \ |
2133 | - done |
2134 | - find debian/python-compressor -iname '*.pyc' -delete |
2135 | - |
2136 | -override_dh_usrlocal: |
2137 | - rm -f $(CURDIR)/debian/usr/share/pyshared/compressor/tests/static/CACHE/css/* |
2138 | - rm -f $(CURDIR)/debian/usr/share/pyshared/compressor/tests/static/CACHE/js/* |
2139 | + set -e ; for pyvers in $(PYTHONS); do \ |
2140 | + python$$pyvers setup.py install --install-layout=deb \ |
2141 | + --root $(CURDIR)/debian/python-compressor; \ |
2142 | + done |
2143 | + set -e ; for pyvers in $(PYTHON3S); do \ |
2144 | + python$$pyvers setup.py install --install-layout=deb \ |
2145 | + --root $(CURDIR)/debian/python3-compressor; \ |
2146 | + done |
2147 | + rm -f $(CURDIR)/debian/usr/lib/python*/dist-packages/compressor/tests/static/CACHE/css/* |
2148 | + rm -f $(CURDIR)/debian/usr/lib/python*/dist-packages/compressor/tests/static/CACHE/js/* |
2149 | + find debian -iname '*.pyc' -delete |
2150 | |
2151 | # Commands not to run |
2152 | override_dh_installcatalogs: |
2153 | |
2154 | === modified file 'debian/watch' |
2155 | --- debian/watch 2012-10-14 10:51:47 +0000 |
2156 | +++ debian/watch 2015-08-20 18:49:21 +0000 |
2157 | @@ -1,2 +1,3 @@ |
2158 | version=3 |
2159 | -http://pypi.python.org/packages/source/d/django-compressor/django-compressor-(.*)\.tar.gz |
2160 | +opts=uversionmangle=s/(rc|a|b|c)/~$1/ \ |
2161 | +http://pypi.debian.net/django_compressor/django_compressor-(.+)\.(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz))) |
2162 | |
2163 | === modified file 'docs/changelog.txt' |
2164 | --- docs/changelog.txt 2015-01-06 14:31:34 +0000 |
2165 | +++ docs/changelog.txt 2015-08-20 18:49:21 +0000 |
2166 | @@ -1,12 +1,46 @@ |
2167 | Changelog |
2168 | ========= |
2169 | |
2170 | -v1.4 |
2171 | ----- |
2172 | +v1.5 (03/27/2015) |
2173 | +----------------- |
2174 | + |
2175 | +`Full Changelog <https://github.com/django-compressor/django-compressor/compare/1.4...HEAD>`_ |
2176 | + |
2177 | +- Fix compress command and run automated tests for Django 1.8 |
2178 | + |
2179 | +- Fix Django 1.8 warnings |
2180 | + |
2181 | +- Handle TypeError from import_module |
2182 | + |
2183 | +- Fix reading UTF-8 files which have BOM |
2184 | + |
2185 | +- Fix incompatibility with Windows (shell_quote is not supported) |
2186 | + |
2187 | +- Run automated tests on Django 1.7 |
2188 | + |
2189 | +- Ignore non-existent {{ block.super }} in offline compression instead of raising AttributeError |
2190 | + |
2191 | +- Support for clean-css |
2192 | + |
2193 | +- Fix link markup |
2194 | + |
2195 | +- Add support for COMPRESS_CSS_HASHING_METHOD = None |
2196 | + |
2197 | +- Remove compatibility with old 'staticfiles' app |
2198 | + |
2199 | +- In compress command, use get_template() instead of opening template files manually, fixing compatibility issues with custom template loaders |
2200 | + |
2201 | +- Fix FilterBase so that does not override self.type for subclasses if filter_type is not specified at init |
2202 | + |
2203 | +- Remove unnecessary filename and existence checks in CssAbsoluteFilter |
2204 | + |
2205 | + |
2206 | +v1.4 (06/20/2014) |
2207 | +----------------- |
2208 | |
2209 | - Added Python 3 compatibility. |
2210 | |
2211 | -- Added compatibility with Django 1.6.x. |
2212 | +- Added compatibility with Django 1.6.x and dropped support for Django 1.3.X. |
2213 | |
2214 | - Fixed compatibility with html5lib 1.0. |
2215 | |
2216 | @@ -46,7 +80,7 @@ |
2217 | - Dropped support for Python 2.5. Removed ``any`` and ``walk`` compatibility |
2218 | functions in ``compressor.utils``. |
2219 | |
2220 | - - Removed compatibility with Django 1.2 for default values of some settings: |
2221 | + - Removed compatibility with some old django setttings: |
2222 | |
2223 | - :attr:`~COMPRESS_ROOT` no longer uses ``MEDIA_ROOT`` if ``STATIC_ROOT`` is |
2224 | not defined. It expects ``STATIC_ROOT`` to be defined instead. |
2225 | |
2226 | === modified file 'docs/contributing.txt' |
2227 | --- docs/contributing.txt 2015-01-06 14:31:34 +0000 |
2228 | +++ docs/contributing.txt 2015-08-20 18:49:21 +0000 |
2229 | @@ -9,11 +9,12 @@ |
2230 | Community |
2231 | --------- |
2232 | |
2233 | -People interested in developing for the Django Compressor should head |
2234 | -over to #django-compressor on the `freenode`_ IRC network for help and to |
2235 | +People interested in developing for the Django Compressor should: |
2236 | + |
2237 | +1. Head over to #django-compressor on the `freenode`_ IRC network for help and to |
2238 | discuss the development. |
2239 | +2. Open an issue on GitHub explaining your ideas. |
2240 | |
2241 | -You may also be interested in following `@jezdez`_ on Twitter. |
2242 | |
2243 | In a nutshell |
2244 | ------------- |
2245 | @@ -143,7 +144,7 @@ |
2246 | - Accessible. You should assume the reader to be moderately familiar with |
2247 | Python and Django, but not anything else. Link to documentation of libraries |
2248 | you use, for example, even if they are "obvious" to you. A brief |
2249 | - description of what it does is also welcome. |
2250 | + description of what it does is also welcome. |
2251 | |
2252 | Pulling of documentation is pretty fast and painless. Usually somebody goes |
2253 | over your text and merges it, since there are no "breaks" and that github |
2254 | |
2255 | === modified file 'docs/django-sekizai.txt' |
2256 | --- docs/django-sekizai.txt 2015-01-06 14:31:34 +0000 |
2257 | +++ docs/django-sekizai.txt 2015-08-20 18:49:21 +0000 |
2258 | @@ -3,12 +3,12 @@ |
2259 | django-sekizai Support |
2260 | ====================== |
2261 | |
2262 | -Django Compressor comes with support for _django-sekizai via an extension. |
2263 | -_django-sekizai provides the ability to include template code, from within |
2264 | +Django Compressor comes with support for django-sekizai_ via an extension. |
2265 | +django-sekizai provides the ability to include template code, from within |
2266 | any block, to a parent block. It is primarily used to include js/css from |
2267 | included templates to the master template. |
2268 | |
2269 | -It requires _django-sekizai to installed. Refer to the _django-sekizai _docs |
2270 | +It requires django-sekizai to be installed. Refer to the `django-sekizai docs`_ |
2271 | for how to use ``render_block`` |
2272 | |
2273 | Usage |
2274 | @@ -21,4 +21,4 @@ |
2275 | |
2276 | |
2277 | .. _django-sekizai: https://github.com/ojii/django-sekizai |
2278 | -.. _docs: http://django-sekizai.readthedocs.org/en/latest/ |
2279 | +.. _django-sekizai docs: http://django-sekizai.readthedocs.org/en/latest/ |
2280 | |
2281 | === modified file 'docs/jinja2.txt' |
2282 | --- docs/jinja2.txt 2015-01-06 14:31:34 +0000 |
2283 | +++ docs/jinja2.txt 2015-08-20 18:49:21 +0000 |
2284 | @@ -42,13 +42,13 @@ |
2285 | ================================== |
2286 | You'd need to configure ``COMPRESS_JINJA2_GET_ENVIRONMENT`` so that |
2287 | Compressor can retrieve the Jinja2 environment for rendering. |
2288 | -This can be a lamda or function that returns a Jinja2 environment. |
2289 | +This can be a lambda or function that returns a Jinja2 environment. |
2290 | |
2291 | Usage |
2292 | ----- |
2293 | -Run the following compress command along with an ``-engine`` parameter. The |
2294 | +Run the following compress command along with an ``--engine`` parameter. The |
2295 | parameter can be either jinja2 or django (default). For example, |
2296 | -"./manage.py compress -engine jinja2". |
2297 | +``./manage.py compress --engine jinja2``. |
2298 | |
2299 | Using both Django and Jinja2 templates |
2300 | -------------------------------------- |
2301 | @@ -60,9 +60,9 @@ |
2302 | |
2303 | A typical usage could be : |
2304 | |
2305 | -- "./manage.py compress" for processing Django templates first, skipping |
2306 | +- ``./manage.py compress`` for processing Django templates first, skipping |
2307 | Jinja2 templates. |
2308 | -- "./manage.py compress -engine jinja2" for processing Jinja2 templates, |
2309 | +- ``./manage.py compress --engine jinja2`` for processing Jinja2 templates, |
2310 | skipping Django templates. |
2311 | |
2312 | However, it is still recommended that you do not mix Django and Jinja2 |
2313 | @@ -172,4 +172,3 @@ |
2314 | .. _Jinja2: http://jinja.pocoo.org/docs/ |
2315 | .. _Coffin: http://pypi.python.org/pypi/Coffin |
2316 | .. _Jingo: https://jingo.readthedocs.org/en/latest/ |
2317 | - |
2318 | |
2319 | === modified file 'docs/quickstart.txt' |
2320 | --- docs/quickstart.txt 2015-01-06 14:31:34 +0000 |
2321 | +++ docs/quickstart.txt 2015-08-20 18:49:21 +0000 |
2322 | @@ -18,10 +18,8 @@ |
2323 | * See the list of :ref:`settings` to modify Django Compressor's |
2324 | default behaviour and make adjustments for your website. |
2325 | |
2326 | -* In case you use Django's staticfiles_ contrib app (or its standalone |
2327 | - counterpart django-staticfiles_) you have to add Django Compressor's file |
2328 | - finder to the ``STATICFILES_FINDERS`` setting, for example with |
2329 | - ``django.contrib.staticfiles``: |
2330 | +* In case you use Django's staticfiles_ contrib app you have to add Django |
2331 | + Compressor's file finder to the ``STATICFILES_FINDERS`` setting, like this: |
2332 | |
2333 | .. code-block:: python |
2334 | |
2335 | @@ -95,6 +93,6 @@ |
2336 | .. _lxml: http://codespeak.net/lxml/ |
2337 | .. _libxml2: http://xmlsoft.org/ |
2338 | .. _html5lib: http://code.google.com/p/html5lib/ |
2339 | -.. _`Slim It`: http://slimit.org/ |
2340 | +.. _`Slim It`: https://github.com/rspivak/slimit |
2341 | .. _django-appconf: http://pypi.python.org/pypi/django-appconf/ |
2342 | .. _versiontools: http://pypi.python.org/pypi/versiontools/ |
2343 | |
2344 | === modified file 'docs/remote-storages.txt' |
2345 | --- docs/remote-storages.txt 2015-01-06 14:31:34 +0000 |
2346 | +++ docs/remote-storages.txt 2015-08-20 18:49:21 +0000 |
2347 | @@ -39,12 +39,11 @@ |
2348 | Using staticfiles |
2349 | ^^^^^^^^^^^^^^^^^ |
2350 | |
2351 | -If you are using Django's staticfiles_ contrib app or the standalone |
2352 | -app django-staticfiles_, you'll need to use a temporary filesystem cache |
2353 | -for Django Compressor to know which files to compress. Since staticfiles |
2354 | -provides a management command to collect static files from various |
2355 | -locations which uses a storage backend, this is where both apps can be |
2356 | -integrated. |
2357 | +If you are using Django's staticfiles_ contrib app, you'll need to use a |
2358 | +temporary filesystem cache for Django Compressor to know which files to |
2359 | +compress. Since staticfiles provides a management command to collect static |
2360 | +files from various locations which uses a storage backend, this is where both |
2361 | +apps can be integrated. |
2362 | |
2363 | #. Make sure the :attr:`~django.conf.settings.COMPRESS_ROOT` and STATIC_ROOT_ |
2364 | settings are equal since both apps need to look at the same directories |
2365 | @@ -84,7 +83,6 @@ |
2366 | .. _Amazon S3: https://s3.amazonaws.com/ |
2367 | .. _boto: http://boto.cloudhackers.com/ |
2368 | .. _django-storages: http://code.welldev.org/django-storages/ |
2369 | -.. _django-staticfiles: http://github.com/jezdez/django-staticfiles/ |
2370 | .. _staticfiles: http://docs.djangoproject.com/en/dev/howto/static-files/ |
2371 | .. _STATIC_ROOT: http://docs.djangoproject.com/en/dev/ref/settings/#static-root |
2372 | .. _STATIC_URL: http://docs.djangoproject.com/en/dev/ref/settings/#static-url |
2373 | |
2374 | === modified file 'docs/settings.txt' |
2375 | --- docs/settings.txt 2015-01-06 14:31:34 +0000 |
2376 | +++ docs/settings.txt 2015-08-20 18:49:21 +0000 |
2377 | @@ -81,10 +81,11 @@ |
2378 | |
2379 | .. attribute:: COMPRESS_CSS_HASHING_METHOD |
2380 | |
2381 | - The method to use when calculating the hash to append to |
2382 | - processed URLs. Either ``'mtime'`` (default) or ``'content'``. |
2383 | - Use the latter in case you're using multiple server to serve your |
2384 | - static files. |
2385 | + The method to use when calculating the suffix to append to URLs in |
2386 | + your processed CSS files. Either ``None``, ``'mtime'`` (default) or |
2387 | + ``'content'``. Use the ``None`` if you want to completely disable that |
2388 | + feature, and the ``'content'`` in case you're using multiple servers |
2389 | + to serve your content. |
2390 | |
2391 | - ``compressor.filters.csstidy.CSSTidyFilter`` |
2392 | |
2393 | @@ -136,9 +137,24 @@ |
2394 | A filter that uses Zachary Voase's Python port of the YUI CSS compression |
2395 | algorithm cssmin_. |
2396 | |
2397 | + - ``compressor.filters.cleancss.CleanCSSFilter`` |
2398 | + |
2399 | + A filter that passes the CSS content to the `clean-css`_ tool. |
2400 | + |
2401 | + .. attribute:: CLEAN_CSS_BINARY |
2402 | + |
2403 | + The clean-css binary filesystem path. |
2404 | + |
2405 | + .. attribute:: CLEAN_CSS_ARGUMENTS |
2406 | + |
2407 | + The arguments passed to clean-css. |
2408 | + |
2409 | + |
2410 | .. _CSSTidy: http://csstidy.sourceforge.net/ |
2411 | .. _`data: URIs`: http://en.wikipedia.org/wiki/Data_URI_scheme |
2412 | .. _cssmin: http://pypi.python.org/pypi/cssmin/ |
2413 | + .. _`clean-css`: https://github.com/GoalSmashers/clean-css/ |
2414 | + |
2415 | |
2416 | - ``compressor.filters.template.TemplateFilter`` |
2417 | |
2418 | @@ -220,7 +236,7 @@ |
2419 | .. _`Google Closure compiler`: http://code.google.com/closure/compiler/ |
2420 | .. _`YUI compressor`: http://developer.yahoo.com/yui/compressor/ |
2421 | .. _`yUglify compressor`: https://github.com/yui/yuglify |
2422 | - .. _`Slim It`: http://slimit.org/ |
2423 | + .. _`Slim It`: https://github.com/rspivak/slimit |
2424 | |
2425 | .. attribute:: COMPRESS_PRECOMPILERS |
2426 | |
2427 | @@ -305,7 +321,7 @@ |
2428 | <link rel="stylesheet" href="/static/CACHE/css/8ccf8d877f18.css" type="text/css" charset="utf-8"> |
2429 | |
2430 | .. _less: http://lesscss.org/ |
2431 | - .. _CoffeeScript: http://jashkenas.github.com/coffee-script/ |
2432 | + .. _CoffeeScript: http://coffeescript.org/ |
2433 | |
2434 | .. attribute:: COMPRESS_STORAGE |
2435 | |
2436 | |
2437 | === modified file 'docs/usage.txt' |
2438 | --- docs/usage.txt 2015-01-06 14:31:34 +0000 |
2439 | +++ docs/usage.txt 2015-08-20 18:49:21 +0000 |
2440 | @@ -48,7 +48,7 @@ |
2441 | |
2442 | .. note:: |
2443 | |
2444 | - Remember that django-compressor will try to :ref:`group ouputs by media <css_notes>`. |
2445 | + Remember that django-compressor will try to :ref:`group outputs by media <css_notes>`. |
2446 | |
2447 | Linked files **must** be accessible via |
2448 | :attr:`~django.conf.settings.COMPRESS_URL`. |
2449 | |
2450 | === modified file 'tox.ini' |
2451 | --- tox.ini 2014-06-26 15:08:13 +0000 |
2452 | +++ tox.ini 2015-08-20 18:49:21 +0000 |
2453 | @@ -33,89 +33,34 @@ |
2454 | |
2455 | [tox] |
2456 | envlist = |
2457 | - py33-1.6.X, |
2458 | - py32-1.6.X, |
2459 | - py27-1.6.X, |
2460 | - py26-1.6.X, |
2461 | - py33-1.5.X, |
2462 | - py32-1.5.X, |
2463 | - py27-1.5.X, |
2464 | - py26-1.5.X, |
2465 | - py27-1.4.X, |
2466 | - py26-1.4.X |
2467 | - |
2468 | + {py26,py27}-{1.4.X,1.5.X}, |
2469 | + {py26,py27,py32,py33}-{1.6.X}, |
2470 | + {py27,py32,py33,py34}-{1.7.X}, |
2471 | + {py27,py32,py33,py34}-{1.8.X} |
2472 | [testenv] |
2473 | +basepython = |
2474 | + py26: python2.6 |
2475 | + py27: python2.7 |
2476 | + py32: python3.2 |
2477 | + py33: python3.3 |
2478 | + py34: python3.4 |
2479 | +usedevelop = true |
2480 | setenv = |
2481 | CPPFLAGS=-O0 |
2482 | -usedevelop = true |
2483 | whitelist_externals = /usr/bin/make |
2484 | downloadcache = {toxworkdir}/_download/ |
2485 | commands = |
2486 | django-admin.py --version |
2487 | make test |
2488 | - |
2489 | -[testenv:py33-1.6.X] |
2490 | -basepython = python3.3 |
2491 | -deps = |
2492 | - Django>=1.6,<1.7 |
2493 | - {[deps]three} |
2494 | - |
2495 | -[testenv:py32-1.6.X] |
2496 | -basepython = python3.2 |
2497 | -deps = |
2498 | - Django>=1.6,<1.7 |
2499 | - {[deps]three_two} |
2500 | - |
2501 | -[testenv:py27-1.6.X] |
2502 | -basepython = python2.7 |
2503 | -deps = |
2504 | - Django>=1.6,<1.7 |
2505 | - {[deps]two} |
2506 | - |
2507 | -[testenv:py26-1.6.X] |
2508 | -basepython = python2.6 |
2509 | -deps = |
2510 | - Django>=1.6,<1.7 |
2511 | - {[deps]two} |
2512 | - |
2513 | -[testenv:py33-1.5.X] |
2514 | -basepython = python3.3 |
2515 | -deps = |
2516 | - Django>=1.5,<1.6 |
2517 | - django-discover-runner |
2518 | - {[deps]three} |
2519 | - |
2520 | -[testenv:py32-1.5.X] |
2521 | -basepython = python3.2 |
2522 | -deps = |
2523 | - Django>=1.5,<1.6 |
2524 | - django-discover-runner |
2525 | - {[deps]three_two} |
2526 | - |
2527 | -[testenv:py27-1.5.X] |
2528 | -basepython = python2.7 |
2529 | -deps = |
2530 | - Django>=1.5,<1.6 |
2531 | - django-discover-runner |
2532 | - {[deps]two} |
2533 | - |
2534 | -[testenv:py26-1.5.X] |
2535 | -basepython = python2.6 |
2536 | -deps = |
2537 | - Django>=1.5,<1.6 |
2538 | - django-discover-runner |
2539 | - {[deps]two} |
2540 | - |
2541 | -[testenv:py27-1.4.X] |
2542 | -basepython = python2.7 |
2543 | -deps = |
2544 | - Django>=1.4,<1.5 |
2545 | - django-discover-runner |
2546 | - {[deps]two} |
2547 | - |
2548 | -[testenv:py26-1.4.X] |
2549 | -basepython = python2.6 |
2550 | -deps = |
2551 | - Django>=1.4,<1.5 |
2552 | - django-discover-runner |
2553 | - {[deps]two} |
2554 | +deps = |
2555 | + 1.4.X: Django>=1.4,<1.5 |
2556 | + 1.5.X: Django>=1.5,<1.6 |
2557 | + 1.6.X: Django>=1.6,<1.7 |
2558 | + 1.7.X: Django>=1.7,<1.8 |
2559 | + 1.8.X: Django>=1.8,<1.9 |
2560 | + py26: {[deps]two} |
2561 | + py27: {[deps]two} |
2562 | + py32: {[deps]three_two} |
2563 | + py33: {[deps]three} |
2564 | + py34: {[deps]three} |
2565 | + django-discover-runner |
Looks good but just a few minor adjustments.
There are a few extra differences in the d/control build-depends. Can we get those in sync or is that for a reason?
Also d/watch wasn't pulling down the orig tar for me, so you may want to update with:
version=3 ngle=s/ (rc|a|b| c)/~$1/ \ pypi.debian. net/django_ compressor/ django_ compressor- (.+)\.(?:zip| tgz|tbz| txz|(?: tar\.(? :gz|bz2| xz)))
opts=uversionma
http://
then we can submit that in a patch to debian (just the d/watch part) via the 'submittodebian' command.