From 72b65b91f088d1823efab0fe6a7612ea6d75951b Mon Sep 17 00:00:00 2001 From: Jeremy Nauta Date: Fri, 2 Dec 2016 12:05:52 -0700 Subject: [PATCH 1/8] Configurable CSS Loader --- django_inlinecss/conf.py | 24 ++++++++-- django_inlinecss/css_loaders.py | 38 ++++++++++++++++ django_inlinecss/templatetags/inlinecss.py | 8 +--- django_inlinecss/tests/test_css_loaders.py | 52 ++++++++++++++++++++++ 4 files changed, 112 insertions(+), 10 deletions(-) create mode 100644 django_inlinecss/css_loaders.py create mode 100644 django_inlinecss/tests/test_css_loaders.py diff --git a/django_inlinecss/conf.py b/django_inlinecss/conf.py index 8258447..fa1a93c 100644 --- a/django_inlinecss/conf.py +++ b/django_inlinecss/conf.py @@ -12,10 +12,26 @@ DEFAULT_ENGINE = 'django_inlinecss.engines.PynlinerEngine' +def load_class_by_path(path): + i = path.rfind('.') + module_path, class_name = path[:i], path[i + 1:] + module = importlib.import_module(module_path) + return getattr(module, class_name) + + def get_engine(): from django.conf import settings engine_path = getattr(settings, 'INLINECSS_ENGINE', DEFAULT_ENGINE) - i = engine_path.rfind('.') - module_path, class_name = engine_path[:i], engine_path[i + 1:] - module = importlib.import_module(module_path) - return getattr(module, class_name) + return load_class_by_path(engine_path) + + +def get_css_loader(): + from django.conf import settings + + if settings.DEBUG: + default_css_loader = 'django_inlinecss.css_loaders.StaticFinderCSSLoader' + else: + default_css_loader = 'django_inlinecss.css_loaders.StaticPathCSSLoader' + + engine_path = getattr(settings, 'INLINECSS_CSS_LOADER', default_css_loader) + return load_class_by_path(engine_path) diff --git a/django_inlinecss/css_loaders.py b/django_inlinecss/css_loaders.py new file mode 100644 index 0000000..5aaf59d --- /dev/null +++ b/django_inlinecss/css_loaders.py @@ -0,0 +1,38 @@ +from django.contrib.staticfiles import finders +from django.contrib.staticfiles.storage import staticfiles_storage + + +def load_css_by_path(path): + with open(path) as css_file: + return css_file.read() + + +class BaseCSSLoader(object): + def __init__(self): + pass + + def load(self, path): + """ + Retrieves the contents of the static asset specified + :param path: path to the desired asset + :return: contents of asset + """ + raise NotImplementedError() + + +class StaticFinderCSSLoader(BaseCSSLoader): + def load(self, path): + """ + Retrieve CSS contents by static finders + """ + expanded_path = finders.find(path) + return load_css_by_path(expanded_path) + + +class StaticPathCSSLoader(BaseCSSLoader): + def load(self, path): + """ + Retrieve CSS contents by local file system + """ + expanded_path = staticfiles_storage.path(path) + return load_css_by_path(expanded_path) diff --git a/django_inlinecss/templatetags/inlinecss.py b/django_inlinecss/templatetags/inlinecss.py index e6cdad1..20c02bb 100644 --- a/django_inlinecss/templatetags/inlinecss.py +++ b/django_inlinecss/templatetags/inlinecss.py @@ -27,13 +27,9 @@ def render(self, context): path = expression.resolve(context, True) if path is not None: path = smart_text(path) - if settings.DEBUG: - expanded_path = finders.find(path) - else: - expanded_path = staticfiles_storage.path(path) - with open(expanded_path) as css_file: - css = ''.join((css, css_file.read())) + css_loader = conf.get_css_loader()() + css = ''.join((css, css_loader.load(path))) engine = conf.get_engine()(html=rendered_contents, css=css) return engine.render() diff --git a/django_inlinecss/tests/test_css_loaders.py b/django_inlinecss/tests/test_css_loaders.py new file mode 100644 index 0000000..d392646 --- /dev/null +++ b/django_inlinecss/tests/test_css_loaders.py @@ -0,0 +1,52 @@ +""" +Test CSS loaders +""" +import os + +from django.test import TestCase +from django.test.utils import override_settings + +from mock import patch + +from django_inlinecss.tests.constants import TESTS_STATIC_DIR +from django_inlinecss.css_loaders import StaticFinderCSSLoader, StaticPathCSSLoader + + +@override_settings(STATIC_ROOT=TESTS_STATIC_DIR) +class StaticFinderCSSLoaderTestCase(TestCase): + def setUp(self): + self.loader = StaticFinderCSSLoader() + super(StaticFinderCSSLoaderTestCase, self).setUp() + + @patch('django.contrib.staticfiles.finders.find') + def test_debug_mode_uses_staticfiles_finder(self, find): + full_path = os.path.join(TESTS_STATIC_DIR, 'bar.css') + find.return_value = full_path + css = self.loader.load(full_path) + self.assertIn('div.bar {', css) + + @patch('django.contrib.staticfiles.finders.find') + def test_load_file_does_not_exists(self, find): + full_path = os.path.join(TESTS_STATIC_DIR, 'missing.css') + find.return_value = full_path + with self.assertRaises(IOError) as e: + self.loader.load('missing.css') + + self.assertEqual(e.exception.strerror, 'No such file or directory') + + +@override_settings(STATIC_ROOT=TESTS_STATIC_DIR) +class StaticPathCSSLoaderTestCase(TestCase): + def setUp(self): + self.loader = StaticPathCSSLoader() + super(StaticPathCSSLoaderTestCase, self).setUp() + + def test_load_existing_css_file(self): + css = self.loader.load('bar.css') + self.assertIn('div.bar {', css) + + def test_load_file_does_not_exists(self): + with self.assertRaises(IOError) as e: + self.loader.load('missing.css') + + self.assertEqual(e.exception.strerror, 'No such file or directory') From dc406ff2f9c20c8027d8f86df1de6c8526a2d6be Mon Sep 17 00:00:00 2001 From: Stephen Burrows Date: Wed, 23 Oct 2019 12:02:32 -0700 Subject: [PATCH 2/8] Switched css_loader to a simple setting with a single default --- django_inlinecss/conf.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/django_inlinecss/conf.py b/django_inlinecss/conf.py index fa1a93c..dc0448e 100644 --- a/django_inlinecss/conf.py +++ b/django_inlinecss/conf.py @@ -10,6 +10,7 @@ from django.utils import importlib DEFAULT_ENGINE = 'django_inlinecss.engines.PynlinerEngine' +DEFAULT_CSS_LOADER = 'django_inlinecss.css_loaders.StaticfilesStorageCSSLoader' def load_class_by_path(path): @@ -27,11 +28,5 @@ def get_engine(): def get_css_loader(): from django.conf import settings - - if settings.DEBUG: - default_css_loader = 'django_inlinecss.css_loaders.StaticFinderCSSLoader' - else: - default_css_loader = 'django_inlinecss.css_loaders.StaticPathCSSLoader' - - engine_path = getattr(settings, 'INLINECSS_CSS_LOADER', default_css_loader) + engine_path = getattr(settings, 'INLINECSS_CSS_LOADER', DEFAULT_CSS_LOADER) return load_class_by_path(engine_path) From c05591b793d50a5f95187bcefb3eba9090b387b7 Mon Sep 17 00:00:00 2001 From: Stephen Burrows Date: Wed, 23 Oct 2019 12:02:56 -0700 Subject: [PATCH 3/8] Updated tests for lack of constants --- django_inlinecss/tests/test_css_loaders.py | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/django_inlinecss/tests/test_css_loaders.py b/django_inlinecss/tests/test_css_loaders.py index d392646..88f0bed 100644 --- a/django_inlinecss/tests/test_css_loaders.py +++ b/django_inlinecss/tests/test_css_loaders.py @@ -1,41 +1,26 @@ """ Test CSS loaders """ -import os - from django.test import TestCase -from django.test.utils import override_settings - -from mock import patch - -from django_inlinecss.tests.constants import TESTS_STATIC_DIR from django_inlinecss.css_loaders import StaticFinderCSSLoader, StaticPathCSSLoader -@override_settings(STATIC_ROOT=TESTS_STATIC_DIR) class StaticFinderCSSLoaderTestCase(TestCase): def setUp(self): self.loader = StaticFinderCSSLoader() super(StaticFinderCSSLoaderTestCase, self).setUp() - @patch('django.contrib.staticfiles.finders.find') - def test_debug_mode_uses_staticfiles_finder(self, find): - full_path = os.path.join(TESTS_STATIC_DIR, 'bar.css') - find.return_value = full_path - css = self.loader.load(full_path) + def test_debug_mode_uses_staticfiles_finder(self): + css = self.loader.load('bar.css') self.assertIn('div.bar {', css) - @patch('django.contrib.staticfiles.finders.find') - def test_load_file_does_not_exists(self, find): - full_path = os.path.join(TESTS_STATIC_DIR, 'missing.css') - find.return_value = full_path + def test_load_file_does_not_exists(self): with self.assertRaises(IOError) as e: self.loader.load('missing.css') self.assertEqual(e.exception.strerror, 'No such file or directory') -@override_settings(STATIC_ROOT=TESTS_STATIC_DIR) class StaticPathCSSLoaderTestCase(TestCase): def setUp(self): self.loader = StaticPathCSSLoader() From 91b1051865a8c57381e567d6865f9e155d877258 Mon Sep 17 00:00:00 2001 From: Stephen Burrows Date: Wed, 23 Oct 2019 13:07:02 -0700 Subject: [PATCH 4/8] Flake8 cleanup --- django_inlinecss/templatetags/inlinecss.py | 3 --- django_inlinecss/tests/test_css_loaders.py | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/django_inlinecss/templatetags/inlinecss.py b/django_inlinecss/templatetags/inlinecss.py index 20c02bb..c778ddf 100644 --- a/django_inlinecss/templatetags/inlinecss.py +++ b/django_inlinecss/templatetags/inlinecss.py @@ -4,9 +4,6 @@ from __future__ import unicode_literals from django import template -from django.conf import settings -from django.contrib.staticfiles import finders -from django.contrib.staticfiles.storage import staticfiles_storage from django.utils.encoding import smart_text from django_inlinecss import conf diff --git a/django_inlinecss/tests/test_css_loaders.py b/django_inlinecss/tests/test_css_loaders.py index 88f0bed..333d79c 100644 --- a/django_inlinecss/tests/test_css_loaders.py +++ b/django_inlinecss/tests/test_css_loaders.py @@ -2,6 +2,7 @@ Test CSS loaders """ from django.test import TestCase + from django_inlinecss.css_loaders import StaticFinderCSSLoader, StaticPathCSSLoader From 353ea7175527e5eb84ea64d233831b41ed99f302 Mon Sep 17 00:00:00 2001 From: Stephen Burrows Date: Wed, 23 Oct 2019 13:19:09 -0700 Subject: [PATCH 5/8] Corrected usage of django storage to support arbitrary storages --- django_inlinecss/css_loaders.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/django_inlinecss/css_loaders.py b/django_inlinecss/css_loaders.py index 5aaf59d..16ce00c 100644 --- a/django_inlinecss/css_loaders.py +++ b/django_inlinecss/css_loaders.py @@ -2,11 +2,6 @@ from django.contrib.staticfiles.storage import staticfiles_storage -def load_css_by_path(path): - with open(path) as css_file: - return css_file.read() - - class BaseCSSLoader(object): def __init__(self): pass @@ -20,19 +15,19 @@ def load(self, path): raise NotImplementedError() -class StaticFinderCSSLoader(BaseCSSLoader): +class StaticfilesFinderCSSLoader(BaseCSSLoader): def load(self, path): """ - Retrieve CSS contents by static finders + Retrieve CSS contents from the local filesystem with static finders """ expanded_path = finders.find(path) - return load_css_by_path(expanded_path) + with open(expanded_path) as css_file: + return css_file.read() -class StaticPathCSSLoader(BaseCSSLoader): +class StaticfilesStorageCSSLoader(BaseCSSLoader): def load(self, path): """ - Retrieve CSS contents by local file system + Retrieve CSS contents with staticfiles storage """ - expanded_path = staticfiles_storage.path(path) - return load_css_by_path(expanded_path) + return staticfiles_storage.open(path).read() From 448b226e7d1e4e6c273622a0918bc2c029a28e40 Mon Sep 17 00:00:00 2001 From: Stephen Burrows Date: Wed, 23 Oct 2019 13:28:38 -0700 Subject: [PATCH 6/8] Corrected missing file to raise an IOError for finders loader --- django_inlinecss/css_loaders.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/django_inlinecss/css_loaders.py b/django_inlinecss/css_loaders.py index 16ce00c..14048c6 100644 --- a/django_inlinecss/css_loaders.py +++ b/django_inlinecss/css_loaders.py @@ -21,6 +21,10 @@ def load(self, path): Retrieve CSS contents from the local filesystem with static finders """ expanded_path = finders.find(path) + + if expanded_path is None: + raise IOError('{} does not exist'.format(path)) + with open(expanded_path) as css_file: return css_file.read() From 3028a36163ac5289cbed11061f632b7884158d97 Mon Sep 17 00:00:00 2001 From: Stephen Burrows Date: Wed, 23 Oct 2019 13:35:30 -0700 Subject: [PATCH 7/8] Updated tests to account for configurable css loaders --- django_inlinecss/tests/test_css_loaders.py | 33 +++++++++++++-------- django_inlinecss/tests/test_templatetags.py | 20 ++++++------- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/django_inlinecss/tests/test_css_loaders.py b/django_inlinecss/tests/test_css_loaders.py index 333d79c..f79a1c2 100644 --- a/django_inlinecss/tests/test_css_loaders.py +++ b/django_inlinecss/tests/test_css_loaders.py @@ -1,37 +1,46 @@ """ Test CSS loaders """ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from django.conf import settings from django.test import TestCase +from django.test import override_settings -from django_inlinecss.css_loaders import StaticFinderCSSLoader, StaticPathCSSLoader +from django_inlinecss.css_loaders import StaticfilesFinderCSSLoader +from django_inlinecss.css_loaders import StaticfilesStorageCSSLoader -class StaticFinderCSSLoaderTestCase(TestCase): +@override_settings(STATICFILES_DIRS=[settings.STATIC_ROOT], STATIC_ROOT='') +class StaticfilesFinderCSSLoaderTestCase(TestCase): def setUp(self): - self.loader = StaticFinderCSSLoader() - super(StaticFinderCSSLoaderTestCase, self).setUp() + self.loader = StaticfilesFinderCSSLoader() + super(StaticfilesFinderCSSLoaderTestCase, self).setUp() - def test_debug_mode_uses_staticfiles_finder(self): + def test_loads_existing_css_file(self): css = self.loader.load('bar.css') self.assertIn('div.bar {', css) - def test_load_file_does_not_exists(self): + def test_load_file_does_not_exist(self): with self.assertRaises(IOError) as e: self.loader.load('missing.css') - self.assertEqual(e.exception.strerror, 'No such file or directory') + self.assertEqual(str(e.exception), 'missing.css does not exist') -class StaticPathCSSLoaderTestCase(TestCase): +class StaticfilesStorageCSSLoaderTestCase(TestCase): def setUp(self): - self.loader = StaticPathCSSLoader() - super(StaticPathCSSLoaderTestCase, self).setUp() + self.loader = StaticfilesStorageCSSLoader() + super(StaticfilesStorageCSSLoaderTestCase, self).setUp() - def test_load_existing_css_file(self): + def test_loads_existing_css_file(self): css = self.loader.load('bar.css') self.assertIn('div.bar {', css) - def test_load_file_does_not_exists(self): + def test_load_file_does_not_exist(self): with self.assertRaises(IOError) as e: self.loader.load('missing.css') diff --git a/django_inlinecss/tests/test_templatetags.py b/django_inlinecss/tests/test_templatetags.py index 2a05836..dc093cc 100644 --- a/django_inlinecss/tests/test_templatetags.py +++ b/django_inlinecss/tests/test_templatetags.py @@ -150,26 +150,26 @@ def test_comments_are_ignored(self): r'This is the "bar" div.\s+\s+') -class DebugModeStaticfilesTests(TestCase): - @override_settings(DEBUG=True) - @patch('django.contrib.staticfiles.finders.find') - def test_debug_mode_uses_staticfiles_finder(self, find): +class GetLoaderStaticfilesTests(TestCase): + @patch('django.contrib.staticfiles.storage.staticfiles_storage.path') + def test_default_uses_staticfiles_storage(self, path): full_path = os.path.join( settings.STATIC_ROOT, 'foobar.css', ) - find.return_value = full_path + path.return_value = full_path template = get_template('single_staticfiles_css.html') template.render({}) - find.assert_called_once_with("foobar.css") + path.assert_called_once_with("foobar.css") - @patch('django.contrib.staticfiles.storage.staticfiles_storage.path') - def test_non_debug_mode_uses_staticfiles_storage(self, path): + @override_settings(INLINECSS_CSS_LOADER='django_inlinecss.css_loaders.StaticfilesFinderCSSLoader') + @patch('django.contrib.staticfiles.finders.find') + def test_override_uses_staticfiles_finder(self, find): full_path = os.path.join( settings.STATIC_ROOT, 'foobar.css', ) - path.return_value = full_path + find.return_value = full_path template = get_template('single_staticfiles_css.html') template.render({}) - path.assert_called_once_with("foobar.css") + find.assert_called_once_with("foobar.css") From a4127aa34bf2e7292684f1453d7b4b4a51546f90 Mon Sep 17 00:00:00 2001 From: Stephen Burrows Date: Wed, 23 Oct 2019 13:46:01 -0700 Subject: [PATCH 8/8] Python 3 cleanup --- django_inlinecss/css_loaders.py | 11 ++++++++--- django_inlinecss/tests/test_templatetags.py | 14 +++++++------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/django_inlinecss/css_loaders.py b/django_inlinecss/css_loaders.py index 14048c6..3021874 100644 --- a/django_inlinecss/css_loaders.py +++ b/django_inlinecss/css_loaders.py @@ -1,3 +1,8 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + from django.contrib.staticfiles import finders from django.contrib.staticfiles.storage import staticfiles_storage @@ -25,8 +30,8 @@ def load(self, path): if expanded_path is None: raise IOError('{} does not exist'.format(path)) - with open(expanded_path) as css_file: - return css_file.read() + with open(expanded_path, 'rb') as css_file: + return css_file.read().decode('utf-8') class StaticfilesStorageCSSLoader(BaseCSSLoader): @@ -34,4 +39,4 @@ def load(self, path): """ Retrieve CSS contents with staticfiles storage """ - return staticfiles_storage.open(path).read() + return staticfiles_storage.open(path).read().decode('utf-8') diff --git a/django_inlinecss/tests/test_templatetags.py b/django_inlinecss/tests/test_templatetags.py index dc093cc..06d6227 100644 --- a/django_inlinecss/tests/test_templatetags.py +++ b/django_inlinecss/tests/test_templatetags.py @@ -28,7 +28,7 @@ def assert_foo_and_bar_rendered(self, rendered): r'
' r'\s+This is the "foo" div.\s+' r'<\/div>') - self.assertRegexpMatches( + self.assertRegex( rendered, foo_div_regex) @@ -36,7 +36,7 @@ def assert_foo_and_bar_rendered(self, rendered): r'
' r'\s+This is the "bar" div.\s+' r'<\/div>') - self.assertRegexpMatches( + self.assertRegex( rendered, bar_div_regex) @@ -120,10 +120,10 @@ def test_unicode_context_variables(self): rendered = template.render({ 'unicode_string': u'I love playing with my pi\xf1ata'}) - self.assertRegexpMatches( + self.assertRegex( rendered, '
') - self.assertRegexpMatches( + self.assertRegex( rendered, u'I love playing with my pi\xf1ata') @@ -139,13 +139,13 @@ def test_comments_are_ignored(self): template = get_template('comments_are_ignored.html') rendered = template.render({}) - self.assertRegexpMatches( + self.assertRegex( rendered, r'\s+\s+\s+') - self.assertRegexpMatches( + self.assertRegex( rendered, r'This is the "bar" div.\s+\s+')