diff options
author | Rocky Meza <rocky@fusionbox.com> | 2014-02-02 06:54:49 -0700 |
---|---|---|
committer | Rocky Meza <rocky@fusionbox.com> | 2014-02-02 06:54:49 -0700 |
commit | 5e6cbfb3a36449a4cc767c5b086ea51561d3d014 (patch) | |
tree | 7128d80ffde7945492512a6592fad7145c3c48ff | |
parent | 2fe1daa5c5a6013e8d3df90d85089dd8f0fe8c95 (diff) | |
download | django-pyscss-5e6cbfb3a36449a4cc767c5b086ea51561d3d014.tar.gz |
Made relative imports possible
-rw-r--r-- | README.rst | 12 | ||||
-rw-r--r-- | django_pyscss/scss.py | 83 | ||||
-rw-r--r-- | testproject/testproject/static/css/bar.scss | 1 | ||||
-rw-r--r-- | tests/test_scss.py | 42 |
4 files changed, 118 insertions, 20 deletions
@@ -33,6 +33,18 @@ You can render SCSS manually from a string like this:: compiler = DjangoScss() compiler.compile(scss_string=".foo { color: green; }") +You can render SCSS from a file like this:: + + from django_pyscss.scss import DjangoScss + + compiler = DjangoScss() + compiler.compile(scss_file='css/styles.scss') + +The file needs to be able to be located by staticfiles finders in order to be +used. All imports are relative to the ``STATIC_ROOT``, but you can also have +relative imports from a file. If you prefix an import with ``./``, you can +import a sibling file without having to write out the whole import path. + .. class:: django_pyscss.scss.DjangoScss diff --git a/django_pyscss/scss.py b/django_pyscss/scss.py index b2cd724..03e6c57 100644 --- a/django_pyscss/scss.py +++ b/django_pyscss/scss.py @@ -4,6 +4,7 @@ import os from django.contrib.staticfiles.storage import staticfiles_storage from django.conf import settings +from django.core.exceptions import SuspiciousFileOperation from scss import ( Scss, dequote, log, SourceFile, SassRule, config, @@ -34,7 +35,8 @@ class DjangoScss(Scss): except NotImplementedError: # remote storages don't implement path pass - return filename, staticfiles_storage + if staticfiles_storage.exists(filename): + return filename, staticfiles_storage def get_file_from_finders(self, filename): return find_one_file(filename) @@ -47,15 +49,22 @@ class DjangoScss(Scss): return self.get_file_from_storage(filename) def _find_source_file(self, name): - full_filename, storage = self.get_file_and_storage(name) + file_and_storage = self.get_file_and_storage(name) + if file_and_storage is None: + return None + else: + full_filename, storage = file_and_storage if name not in self.source_files: with storage.open(full_filename) as f: source = f.read() + source_file = SourceFile( full_filename, source, - parent_dir=os.path.realpath(os.path.dirname(full_filename)), ) + # SourceFile.__init__ calls os.path.realpath on this, we don't want + # that. + source_file.parent_dir = os.path.dirname(name) self.source_files.append(source_file) self.source_file_index[full_filename] = source_file return self.source_file_index[full_filename] @@ -74,6 +83,9 @@ class DjangoScss(Scss): for name in names: name = dequote(name.strip()) + if name.startswith('./'): + name = rule.source_file.parent_dir + name[1:] + source_file = self._find_source_file(name) if source_file is None: @@ -108,3 +120,68 @@ class DjangoScss(Scss): ) rule.namespace.add_import(import_key, rule.import_key, rule.file_and_line) self.manage_children(_rule, scope) + + def Compilation(self, scss_string=None, scss_file=None, super_selector=None, filename=None, is_sass=None, line_numbers=True): + """ + Overwritten to call _find_source_file instead of + SourceFile.from_filename. + """ + if super_selector: + self.super_selector = super_selector + ' ' + self.reset() + + source_file = None + if scss_string is not None: + source_file = SourceFile.from_string(scss_string, filename, is_sass, line_numbers) + elif scss_file is not None: + # Call _find_source_file instead of SourceFile.from_filename + source_file = self._find_source_file(scss_file) + + if source_file is not None: + # Clear the existing list of files + self.source_files = [] + self.source_file_index = dict() + + self.source_files.append(source_file) + self.source_file_index[source_file.filename] = source_file + + # this will compile and manage rule: child objects inside of a node + self.parse_children() + + # this will manage @extends + self.apply_extends() + + rules_by_file, css_files = self.parse_properties() + + all_rules = 0 + all_selectors = 0 + exceeded = '' + final_cont = '' + files = len(css_files) + for source_file in css_files: + rules = rules_by_file[source_file] + fcont, total_rules, total_selectors = self.create_css(rules) + all_rules += total_rules + all_selectors += total_selectors + if not exceeded and all_selectors > 4095: + exceeded = " (IE exceeded!)" + log.error("Maximum number of supported selectors in Internet Explorer (4095) exceeded!") + if files > 1 and self.scss_opts.get('debug_info', False): + if source_file.is_string: + final_cont += "/* %s %s generated add up to a total of %s %s accumulated%s */\n" % ( + total_selectors, + 'selector' if total_selectors == 1 else 'selectors', + all_selectors, + 'selector' if all_selectors == 1 else 'selectors', + exceeded) + else: + final_cont += "/* %s %s generated from '%s' add up to a total of %s %s accumulated%s */\n" % ( + total_selectors, + 'selector' if total_selectors == 1 else 'selectors', + source_file.filename, + all_selectors, + 'selector' if all_selectors == 1 else 'selectors', + exceeded) + final_cont += fcont + + return final_cont diff --git a/testproject/testproject/static/css/bar.scss b/testproject/testproject/static/css/bar.scss new file mode 100644 index 0000000..448776a --- /dev/null +++ b/testproject/testproject/static/css/bar.scss @@ -0,0 +1 @@ +@import "./foo.scss"; diff --git a/tests/test_scss.py b/tests/test_scss.py index 7b86c88..19e035c 100644 --- a/tests/test_scss.py +++ b/tests/test_scss.py @@ -9,16 +9,6 @@ from django_pyscss.scss import DjangoScss from tests.utils import clean_css -compiler = DjangoScss(scss_opts={ - # No compress so that I can compare more easily - 'compress': 0, -}) - - -def compile_string(string): - return compiler.compile(scss_string=string) - - IMPORT_FOO = """ @import "css/foo.scss"; """ @@ -42,19 +32,37 @@ IMPORT_APP2 = """ APP2_CONTENTS = FOO_CONTENTS + APP1_CONTENTS -class ImportTestMixin(object): +class CompilerTestMixin(object): + def setUp(self): + self.compiler = DjangoScss(scss_opts={ + # No compress so that I can compare more easily + 'compress': 0, + }) + super(CompilerTestMixin, self).setUp() + + +class ImportTestMixin(CompilerTestMixin): def test_import_from_staticfiles_dirs(self): - actual = compile_string(IMPORT_FOO) + actual = self.compiler.compile(scss_string=IMPORT_FOO) self.assertEqual(clean_css(actual), clean_css(FOO_CONTENTS)) def test_import_from_app(self): - actual = compile_string(IMPORT_APP1) + actual = self.compiler.compile(scss_string=IMPORT_APP1) self.assertEqual(clean_css(actual), clean_css(APP1_CONTENTS)) def test_imports_within_file(self): - actual = compile_string(IMPORT_APP2) + actual = self.compiler.compile(scss_string=IMPORT_APP2) self.assertEqual(clean_css(actual), clean_css(APP2_CONTENTS)) + def test_relative_import(self): + bar_scss = 'css/bar.scss' + actual = self.compiler.compile(scss_file=bar_scss) + self.assertEqual(clean_css(actual), clean_css(FOO_CONTENTS)) + + def test_bad_import(self): + actual = self.compiler.compile(scss_string='@import "this-file-does-not-and-should-never-exist.scss";') + self.assertEqual(clean_css(actual), '') + @override_settings(DEBUG=True) class FindersImportTest(ImportTestMixin, TestCase): @@ -91,13 +99,13 @@ $widgets: sprite-map('images/icons/widget-*.png'); """ -class AssetsTest(TestCase): +class AssetsTest(CompilerTestMixin, TestCase): def test_inline_image(self): - actual = compile_string(INLINE_IMAGE) + actual = self.compiler.compile(scss_string=INLINE_IMAGE) self.assertEqual(clean_css(actual), clean_css(INLINED_IMAGE_EXPECTED)) def test_sprite_images(self): - actual = compile_string(SPRITE_MAP) + actual = self.compiler.compile(scss_string=SPRITE_MAP) # pyScss puts a cachebuster query string on the end of the URLs, lets # just check that it made the file that we expected. self.assertIn('KUZdBAnPCdlG5qfocw9GYw.png', actual) |