summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRocky Meza <rocky@fusionbox.com>2014-02-02 06:54:49 -0700
committerRocky Meza <rocky@fusionbox.com>2014-02-02 06:54:49 -0700
commit5e6cbfb3a36449a4cc767c5b086ea51561d3d014 (patch)
tree7128d80ffde7945492512a6592fad7145c3c48ff
parent2fe1daa5c5a6013e8d3df90d85089dd8f0fe8c95 (diff)
downloaddjango-pyscss-5e6cbfb3a36449a4cc767c5b086ea51561d3d014.tar.gz
Made relative imports possible
-rw-r--r--README.rst12
-rw-r--r--django_pyscss/scss.py83
-rw-r--r--testproject/testproject/static/css/bar.scss1
-rw-r--r--tests/test_scss.py42
4 files changed, 118 insertions, 20 deletions
diff --git a/README.rst b/README.rst
index 1333ae7..5f25769 100644
--- a/README.rst
+++ b/README.rst
@@ -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)