summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRocky Meza <rocky@fusionbox.com>2015-03-03 03:41:33 -0700
committerRocky Meza <rocky@fusionbox.com>2015-04-22 16:07:00 -0600
commit518098530e4ab9c2f33303ba00f34a1575c7ba3c (patch)
tree855b44da4f5504cb2840e1785d621beb62c10611
parent313c0e02b8d7b43ab534dd2491e07e37deeb17d3 (diff)
downloaddjango-pyscss-518098530e4ab9c2f33303ba00f34a1575c7ba3c.tar.gz
Add pyScss 1.3 and Python 3 support
-rw-r--r--.gitignore2
-rw-r--r--.travis.yml37
-rw-r--r--README.rst95
-rw-r--r--django_pyscss/__init__.py1
-rw-r--r--django_pyscss/compiler.py58
-rw-r--r--django_pyscss/compressor.py13
-rw-r--r--django_pyscss/extension/__init__.py0
-rw-r--r--django_pyscss/extension/django.py44
-rw-r--r--django_pyscss/scss.py223
-rw-r--r--django_pyscss/utils.py28
-rw-r--r--setup.py5
-rw-r--r--testproject/runtests.py1
-rw-r--r--tests/test_compressor.py2
-rw-r--r--tests/test_scss.py55
-rw-r--r--tests/utils.py17
-rw-r--r--tox.ini21
16 files changed, 298 insertions, 304 deletions
diff --git a/.gitignore b/.gitignore
index 86a94f0..11f90e3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,5 @@ tmp/
.coverage
htmlcov/
/dist/
+.eggs
+.tox
diff --git a/.travis.yml b/.travis.yml
index 8e31eda..9bfe446 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,26 +1,23 @@
+sudo: false
language: python
python:
- "2.7"
- - "2.6"
- # - "3.3"
+cache:
+ directories:
+ - $HOME/.pip-cache/
env:
- - DJANGO_PACKAGE='Django>=1.5,<1.6'
- - DJANGO_PACKAGE='Django>=1.6,<1.7'
- - DJANGO_PACKAGE='Django>=1.7,<1.8'
-matrix:
- include:
- - python: "2.6"
- env: DJANGO_PACKAGE='Django>=1.4,<1.5'
- - python: "2.7"
- env: DJANGO_PACKAGE='Django>=1.4,<1.5'
- exclude:
- - python: "2.6"
- env: DJANGO_PACKAGE='Django>=1.7,<1.8'
+ - TOX_ENV=py26-dj14
+ - TOX_ENV=py27-dj14
+ - TOX_ENV=py27-dj17
+ - TOX_ENV=py27-dj18
+ - TOX_ENV=py33-dj17
+ - TOX_ENV=py33-dj18
+ - TOX_ENV=py34-dj17
+ - TOX_ENV=py34-dj18
install:
- - pip install -q $DJANGO_PACKAGE --use-mirrors
- - pip install --use-mirrors .
- - pip install coveralls
+ - pip install --upgrade pip
+ - pip install tox==1.8.0
script:
- - coverage run --source=django_pyscss setup.py test
-after_success:
- coveralls
+ - tox -e $TOX_ENV
+after_script:
+ - cat .tox/$TOX_ENV/log/*.log
diff --git a/README.rst b/README.rst
index beb5f46..b6099a3 100644
--- a/README.rst
+++ b/README.rst
@@ -11,6 +11,23 @@ A collection of tools for making it easier to use pyScss within Django.
:target: https://coveralls.io/r/fusionbox/django-pyscss
:alt: Coverage Status
+
+.. note::
+
+ This version only supports pyScss 1.3.4 and greater. For pyScss 1.2 support,
+ you can use the 1.x series of django-pyscss.
+
+
+Installation
+============
+
+django-pyscss supports Django 1.4+, and Pythons 2 and 3.
+
+You may install django-pyscss off of PyPI::
+
+ pip install django-pyscss
+
+
Why do we need this?
====================
@@ -20,7 +37,7 @@ This app smooths over a lot of things when dealing with pyScss in Django. It
can import SCSS files from any app (or any file that's findable by the
STATICFILES_FINDERS) with no hassle.
-- Configures pyScss to work with the staticfiles app for it's image functions
+- Configures pyScss to work with the staticfiles app for its image functions
(e.g. inline-image and sprite-map).
- It provides a django-compressor precompile filter class so that you can
@@ -34,41 +51,52 @@ This app smooths over a lot of things when dealing with pyScss in Django. It
Rendering SCSS manually
=======================
-You can render SCSS manually from a string like this::
+You can render SCSS manually from a string like this:
- from django_pyscss.scss import DjangoScss
+.. code-block:: python
- compiler = DjangoScss()
- compiler.compile(scss_string=".foo { color: green; }")
+ from django_pyscss import DjangoScssCompiler
-You can render SCSS from a file like this::
+ compiler = DjangoScssCompiler()
+ compiler.compile_string(".foo { color: green; }")
- from django_pyscss.scss import DjangoScss
+You can render SCSS from a file like this:
- compiler = DjangoScss()
- compiler.compile(scss_file='css/styles.scss')
+.. code-block:: python
+
+ from django_pyscss import DjangoScssCompiler
+
+ compiler = DjangoScssCompiler()
+ compiler.compile('css/styles.scss')
The file needs to be able to be located by staticfiles finders in order to be
used.
+The ``DjangoScssCompiler`` class is a subclass of ``scss.Compiler`` that
+injects the ``DjangoExtension``. ``DjangoExtension`` is what overrides the
+import mechanism.
+
+``DjangoScssCompiler`` also turns on the CompassExtension by default, if you
+wish to turn this off you do so:
-.. class:: django_pyscss.scss.DjangoScss
+.. code-block:: python
- A subclass of :class:`scss.Scss` that uses the Django staticfiles storage
- and finders instead of the filesystem. This obsoletes the load_paths
- option that was present previously by searching instead in your staticfiles
- directories.
+ from django_pyscss import DjangoScssCompiler
+ from django_pyscss.extensions.django import DjangoExtension
- In DEBUG mode, DjangoScss will search using all of the finders to find the
- file. If you are not in DEBUG mode, it assumes you have run collectstatic
- and will only use staticfiles_storage to find the file.
+ compiler = DjangoScssCompiler(extensions=[DjangoExtension])
+For a list of options that ``DjangoScssCompiler`` accepts, please see the
+pyScss `API documentation <http://pyscss.readthedocs.org/en/latest/python-api.html#new-api>`_.
-Using in conjunction with django-compressor.
-============================================
+
+Using in conjunction with django-compressor
+===========================================
django-pyscss comes with support for django-compressor. All you have to do is
-add it to your ``COMPRESS_PRECOMPILERS`` setting. ::
+add it to your ``COMPRESS_PRECOMPILERS`` setting. :
+
+.. code-block:: python
COMPRESS_PRECOMPILERS = (
# ...
@@ -76,12 +104,37 @@ add it to your ``COMPRESS_PRECOMPILERS`` setting. ::
# ...
)
-Then you can just use SCSS like you would use CSS normally. ::
+Then you can just use SCSS like you would use CSS normally. :
+
+.. code-block:: html+django
{% compress css %}
<link rel="stylesheet" type="text/x-scss" href="{% static 'css/styles.css' %}">
{% endcompress %}
+If you wish to provide your own compiler instance (for example if you wanted to
+change some settings on the ``DjangoScssCompiler``), you can subclass
+``DjangoScssFilter``. :
+
+.. code-block:: python
+
+ # myproject/scss_filter.py
+ from django_pyscss import DjangoScssCompiler
+ from django_pyscss.compressor import DjangoScssFilter
+
+ class MyDjangoScssFilter(DjangoScssFilter):
+ compiler = DjangoScssCompiler(
+ # Example configuration
+ output_style='compressed',
+ )
+
+ # settings.py
+ COMPRESS_PRECOMPILERS = (
+ # ...
+ ('text/x-scss', 'myproject.scss_filter.MyDjangoScssFilter'),
+ # ...
+ )
+
Running the tests
=================
diff --git a/django_pyscss/__init__.py b/django_pyscss/__init__.py
index e69de29..a31ff85 100644
--- a/django_pyscss/__init__.py
+++ b/django_pyscss/__init__.py
@@ -0,0 +1 @@
+from .compiler import DjangoScssCompiler # NOQA
diff --git a/django_pyscss/compiler.py b/django_pyscss/compiler.py
new file mode 100644
index 0000000..cf20595
--- /dev/null
+++ b/django_pyscss/compiler.py
@@ -0,0 +1,58 @@
+from __future__ import absolute_import
+
+import os
+from pathlib import PurePath
+
+from django.utils.six.moves import StringIO
+
+from django.conf import settings
+from django.contrib.staticfiles.storage import staticfiles_storage
+
+from scss import Compiler, config
+from scss.extension.compass import CompassExtension
+from scss.source import SourceFile
+
+from .extension.django import DjangoExtension
+from .utils import find_all_files, get_file_and_storage
+
+
+# TODO: It's really gross to modify this global settings variable.
+# This is where PyScss is supposed to find the image files for making sprites.
+config.STATIC_ROOT = find_all_files
+config.STATIC_URL = staticfiles_storage.url('scss/')
+
+# This is where PyScss places the sprite files.
+config.ASSETS_ROOT = os.path.join(settings.STATIC_ROOT, 'scss', 'assets')
+# PyScss expects a trailing slash.
+config.ASSETS_URL = staticfiles_storage.url('scss/assets/')
+
+
+class DjangoScssCompiler(Compiler):
+ def __init__(self, **kwargs):
+ kwargs.setdefault('extensions', (DjangoExtension, CompassExtension))
+ if not os.path.exists(config.ASSETS_ROOT):
+ os.makedirs(config.ASSETS_ROOT)
+ super(DjangoScssCompiler, self).__init__(**kwargs)
+
+ def compile(self, *paths):
+ compilation = self.make_compilation()
+ for path in paths:
+ path = PurePath(path)
+ if path.is_absolute():
+ path = path.relative_to('/')
+ filename, storage = get_file_and_storage(str(path))
+ with storage.open(filename) as f:
+ source = SourceFile.from_file(f, origin=path.parent, relpath=PurePath(path.name))
+ compilation.add_source(source)
+ return self.call_and_catch_errors(compilation.run)
+
+ def compile_string(self, string, filename=None):
+ compilation = self.make_compilation()
+ if filename is not None:
+ f = StringIO(string)
+ filename = PurePath(filename)
+ source = SourceFile.from_file(f, origin=filename.parent, relpath=PurePath(filename.name))
+ else:
+ source = SourceFile.from_string(string)
+ compilation.add_source(source)
+ return self.call_and_catch_errors(compilation.run)
diff --git a/django_pyscss/compressor.py b/django_pyscss/compressor.py
index 78d5fd0..68aec24 100644
--- a/django_pyscss/compressor.py
+++ b/django_pyscss/compressor.py
@@ -1,15 +1,13 @@
from __future__ import absolute_import
-import os
-
from compressor.filters import FilterBase
from compressor.conf import settings
-from django_pyscss.scss import DjangoScss
+from django_pyscss import DjangoScssCompiler
class DjangoScssFilter(FilterBase):
- compiler = DjangoScss()
+ compiler = DjangoScssCompiler()
def __init__(self, content, attrs=None, filter_type=None, filename=None, **kwargs):
# It looks like there is a bug in django-compressor because it expects
@@ -21,10 +19,9 @@ class DjangoScssFilter(FilterBase):
href = attrs['href']
except KeyError:
# this is a style tag which means this is inline SCSS.
- self.relative_to = None
+ self.filename = None
else:
- self.relative_to = os.path.dirname(href.replace(settings.STATIC_URL, ''))
+ self.filename = href.replace(settings.STATIC_URL, '')
def input(self, **kwargs):
- return self.compiler.compile(scss_string=self.content,
- relative_to=self.relative_to)
+ return self.compiler.compile_string(self.content, filename=self.filename)
diff --git a/django_pyscss/extension/__init__.py b/django_pyscss/extension/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/django_pyscss/extension/__init__.py
diff --git a/django_pyscss/extension/django.py b/django_pyscss/extension/django.py
new file mode 100644
index 0000000..c127270
--- /dev/null
+++ b/django_pyscss/extension/django.py
@@ -0,0 +1,44 @@
+from __future__ import absolute_import, unicode_literals
+
+from itertools import product
+from pathlib import PurePath
+
+from scss.extension.core import CoreExtension
+from scss.source import SourceFile
+
+from ..utils import get_file_and_storage
+
+
+class DjangoExtension(CoreExtension):
+ name = 'django'
+
+ def handle_import(self, name, compilation, rule):
+ """
+ Re-implementation of the core Sass import mechanism, which looks for
+ files using the staticfiles storage and staticfiles finders.
+ """
+ original_path = PurePath(name)
+
+ if original_path.suffix:
+ search_exts = [original_path.suffix]
+ else:
+ search_exts = compilation.compiler.dynamic_extensions
+
+ if original_path.is_absolute():
+ # Remove the beginning slash
+ search_path = original_path.relative_to('/').parent
+ elif rule.source_file.origin:
+ search_path = rule.source_file.origin
+ else:
+ search_path = original_path.parent
+
+ basename = original_path.stem
+
+ for prefix, suffix in product(('_', ''), search_exts):
+ filename = PurePath(prefix + basename + suffix)
+
+ full_filename, storage = get_file_and_storage(str(search_path / filename))
+
+ if full_filename:
+ with storage.open(full_filename) as f:
+ return SourceFile.from_file(f, origin=search_path, relpath=filename)
diff --git a/django_pyscss/scss.py b/django_pyscss/scss.py
deleted file mode 100644
index b4c5981..0000000
--- a/django_pyscss/scss.py
+++ /dev/null
@@ -1,223 +0,0 @@
-from __future__ import absolute_import, unicode_literals
-
-import os
-from itertools import product
-
-from django.contrib.staticfiles.storage import staticfiles_storage
-from django.conf import settings
-
-from scss import (
- Scss, dequote, log, SourceFile, SassRule, config,
-)
-
-from django_pyscss.utils import find_all_files
-
-
-# TODO: It's really gross to modify this global settings variable.
-# This is where PyScss is supposed to find the image files for making sprites.
-config.STATIC_ROOT = find_all_files
-config.STATIC_URL = staticfiles_storage.url('scss/')
-
-# This is where PyScss places the sprite files.
-config.ASSETS_ROOT = os.path.join(settings.STATIC_ROOT, 'scss', 'assets')
-# PyScss expects a trailing slash.
-config.ASSETS_URL = staticfiles_storage.url('scss/assets/')
-
-
-class DjangoScss(Scss):
- """
- A subclass of the Scss compiler that uses the storages API for accessing
- files.
- """
- supported_extensions = ['.scss', '.sass', '.css']
-
- def get_file_from_storage(self, filename):
- try:
- filename = staticfiles_storage.path(filename)
- except NotImplementedError:
- # remote storages don't implement path
- pass
- if staticfiles_storage.exists(filename):
- return filename, staticfiles_storage
- else:
- return None, None
-
- def get_file_from_finders(self, filename):
- for file_and_storage in find_all_files(filename):
- return file_and_storage
- return None, None
-
- def get_file_and_storage(self, filename):
- # TODO: the switch probably shouldn't be on DEBUG
- if settings.DEBUG:
- return self.get_file_from_finders(filename)
- else:
- return self.get_file_from_storage(filename)
-
- def get_possible_import_paths(self, path, relative_to=None):
- """
- Returns an iterable of possible paths for an import.
-
- relative_to is None in the case that the SCSS is being rendered from a
- string or if it is the first file.
- """
- paths = []
-
- if path.startswith('/'): # absolute import
- path = path[1:]
- elif relative_to: # relative import
- path = os.path.join(relative_to, path)
-
- dirname, filename = os.path.split(path)
- name, ext = os.path.splitext(filename)
- if ext:
- search_exts = [ext]
- else:
- search_exts = self.supported_extensions
- for prefix, suffix in product(('_', ''), search_exts):
- paths.append(os.path.join(dirname, prefix + name + suffix))
- paths.append(path)
- return paths
-
- def _find_source_file(self, filename, relative_to=None):
- paths = self.get_possible_import_paths(filename, relative_to)
- log.debug('Searching for %s in %s', filename, paths)
- for name in paths:
- full_filename, storage = self.get_file_and_storage(name)
- if full_filename:
- if full_filename not in self.source_file_index:
- with storage.open(full_filename) as f:
- source = f.read()
-
- source_file = SourceFile(
- full_filename,
- source,
- )
- # SourceFile.__init__ calls os.path.realpath on this, we don't want
- # that, we want them to remain relative.
- 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]
-
- def _do_import(self, rule, scope, block):
- """
- Implements @import using the django storages API.
- """
- # Protect against going to prohibited places...
- if any(scary_token in block.argument for scary_token in ('..', '://', 'url(')):
- rule.properties.append((block.prop, None))
- return
-
- full_filename = None
- names = block.argument.split(',')
- for name in names:
- name = dequote(name.strip())
-
- relative_to = rule.source_file.parent_dir
- source_file = self._find_source_file(name, relative_to)
-
- if source_file is None:
- i_codestr = self._do_magic_import(rule, scope, block)
-
- if i_codestr is not None:
- source_file = SourceFile.from_string(i_codestr)
- self.source_files.append(source_file)
- self.source_file_index[full_filename] = source_file
-
- if source_file is None:
- log.warn("File to import not found or unreadable: '%s' (%s)", name, rule.file_and_line)
- continue
-
- import_key = (name, source_file.parent_dir)
- if rule.namespace.has_import(import_key):
- # If already imported in this scope, skip
- continue
-
- _rule = SassRule(
- source_file=source_file,
- lineno=block.lineno,
- import_key=import_key,
- unparsed_contents=source_file.contents,
-
- # rule
- options=rule.options,
- properties=rule.properties,
- extends_selectors=rule.extends_selectors,
- ancestry=rule.ancestry,
- namespace=rule.namespace,
- )
- 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,
- relative_to=None):
- """
- Overwritten to call _find_source_file instead of
- SourceFile.from_filename. Also added the relative_to option.
- """
- if not os.path.exists(config.ASSETS_ROOT):
- os.makedirs(config.ASSETS_ROOT)
- 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)
- # Set the parent_dir to be something meaningful instead of the
- # current working directory, which is never correct for DjangoScss.
- source_file.parent_dir = relative_to
- 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/django_pyscss/utils.py b/django_pyscss/utils.py
index d668280..1f9e4b4 100644
--- a/django_pyscss/utils.py
+++ b/django_pyscss/utils.py
@@ -1,7 +1,9 @@
import fnmatch
import os
+from django.conf import settings
from django.contrib.staticfiles import finders
+from django.contrib.staticfiles.storage import staticfiles_storage
def find_all_files(glob):
@@ -17,3 +19,29 @@ def find_all_files(glob):
or '', path),
glob):
yield path, storage
+
+
+def get_file_from_storage(filename):
+ try:
+ filename = staticfiles_storage.path(filename)
+ except NotImplementedError:
+ # remote storages don't implement path
+ pass
+ if staticfiles_storage.exists(filename):
+ return filename, staticfiles_storage
+ else:
+ return None, None
+
+
+def get_file_from_finders(filename):
+ for file_and_storage in find_all_files(filename):
+ return file_and_storage
+ return None, None
+
+
+def get_file_and_storage(filename):
+ # TODO: the switch probably shouldn't be on DEBUG
+ if settings.DEBUG:
+ return get_file_from_finders(filename)
+ else:
+ return get_file_from_storage(filename)
diff --git a/setup.py b/setup.py
index 265e772..1c15cb3 100644
--- a/setup.py
+++ b/setup.py
@@ -11,7 +11,7 @@ def read(fname):
install_requires = [
'Django>=1.4',
- 'pyScss>=1.2.0,<1.3.0',
+ 'pyScss>=1.3.4',
]
tests_require = [
'Pillow',
@@ -47,6 +47,7 @@ setup(
'Programming Language :: Python',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
- #'Programming Language :: Python :: 3.3',
+ 'Programming Language :: Python :: 3.3',
+ 'Programming Language :: Python :: 3.4',
],
)
diff --git a/testproject/runtests.py b/testproject/runtests.py
index 09930f0..dc3fbc9 100644
--- a/testproject/runtests.py
+++ b/testproject/runtests.py
@@ -15,5 +15,6 @@ def runtests():
# Stolen from django/core/management/commands/test.py
TestRunner = get_runner(settings)
test_runner = TestRunner(verbosity=1, interactive=True)
+ # failures = test_runner.run_tests(['tests.test_scss.FindersImportTest.test_relative_import_with_filename'])
failures = test_runner.run_tests(['tests'])
sys.exit(bool(failures))
diff --git a/tests/test_compressor.py b/tests/test_compressor.py
index cd8827d..aa554fe 100644
--- a/tests/test_compressor.py
+++ b/tests/test_compressor.py
@@ -1,4 +1,4 @@
-from django.template.loader import Template, Context
+from django.template import Template, Context
from tests.utils import CollectStaticTestCase
diff --git a/tests/test_scss.py b/tests/test_scss.py
index 78cd46e..e8095f7 100644
--- a/tests/test_scss.py
+++ b/tests/test_scss.py
@@ -1,12 +1,15 @@
import os
+import re
from django.test import TestCase
from django.test.utils import override_settings
from django.conf import settings
-from django_pyscss.scss import DjangoScss
+from scss.errors import SassImportError
-from tests.utils import clean_css, CollectStaticTestCase
+from django_pyscss import DjangoScssCompiler
+
+from tests.utils import clean_css, CollectStaticTestCase, NoCollectStaticTestCase
with open(os.path.join(settings.BASE_DIR, 'testproject', 'static', 'css', 'foo.scss')) as f:
@@ -35,69 +38,69 @@ with open(os.path.join(settings.BASE_DIR, 'testproject', 'static', 'css', 'path_
class CompilerTestMixin(object):
def setUp(self):
- self.compiler = DjangoScss(scss_opts={
- # No compress so that I can compare more easily
- 'compress': 0,
- })
+ self.compiler = DjangoScssCompiler()
super(CompilerTestMixin, self).setUp()
class ImportTestMixin(CompilerTestMixin):
def test_import_from_staticfiles_dirs(self):
- actual = self.compiler.compile(scss_string='@import "/css/foo.scss";')
+ actual = self.compiler.compile_string('@import "/css/foo.scss";')
self.assertEqual(clean_css(actual), clean_css(FOO_CONTENTS))
def test_import_from_staticfiles_dirs_prefixed(self):
- actual = self.compiler.compile(scss_string='@import "/css_prefix/baz.scss";')
+ actual = self.compiler.compile_string('@import "/css_prefix/baz.scss";')
self.assertEqual(clean_css(actual), clean_css(FOO_CONTENTS))
def test_import_from_staticfiles_dirs_relative(self):
- actual = self.compiler.compile(scss_string='@import "css/foo.scss";')
+ actual = self.compiler.compile_string('@import "css/foo.scss";')
self.assertEqual(clean_css(actual), clean_css(FOO_CONTENTS))
def test_import_from_app(self):
- actual = self.compiler.compile(scss_string='@import "/css/app1.scss";')
+ actual = self.compiler.compile_string('@import "/css/app1.scss";')
self.assertEqual(clean_css(actual), clean_css(APP1_CONTENTS))
def test_import_from_app_relative(self):
- actual = self.compiler.compile(scss_string='@import "css/app1.scss";')
+ actual = self.compiler.compile_string('@import "css/app1.scss";')
self.assertEqual(clean_css(actual), clean_css(APP1_CONTENTS))
def test_imports_within_file(self):
- actual = self.compiler.compile(scss_string='@import "/css/app2.scss";')
+ actual = self.compiler.compile_string('@import "/css/app2.scss";')
self.assertEqual(clean_css(actual), clean_css(APP2_CONTENTS))
def test_relative_import(self):
- actual = self.compiler.compile(scss_file='/css/bar.scss')
+ actual = self.compiler.compile('/css/bar.scss')
+ self.assertEqual(clean_css(actual), clean_css(FOO_CONTENTS))
+
+ def test_relative_import_with_filename(self):
+ actual = self.compiler.compile_string('@import "foo.scss";', 'css/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), '')
+ self.assertRaises(SassImportError, self.compiler.compile_string, '@import "this-file-does-not-and-should-never-exist.scss";')
def test_no_extension_import(self):
- actual = self.compiler.compile(scss_string='@import "/css/foo";')
+ actual = self.compiler.compile_string('@import "/css/foo";')
self.assertEqual(clean_css(actual), clean_css(FOO_CONTENTS))
def test_no_extension_import_sass(self):
- actual = self.compiler.compile(scss_string='@import "/css/sass_file";')
+ actual = self.compiler.compile_string('@import "/css/sass_file";')
self.assertEqual(clean_css(actual), clean_css(SASS_CONTENTS))
- def test_no_extension_import_css(self):
- actual = self.compiler.compile(scss_string='@import "/css/css_file";')
- self.assertEqual(clean_css(actual), clean_css(CSS_CONTENTS))
+ # def test_no_extension_import_css(self):
+ # actual = self.compiler.compile_string('@import "/css/css_file";')
+ # self.assertEqual(clean_css(actual), clean_css(CSS_CONTENTS))
def test_import_underscore_file(self):
- actual = self.compiler.compile(scss_string='@import "/css/baz";')
+ actual = self.compiler.compile_string('@import "/css/baz";')
self.assertEqual(clean_css(actual), clean_css(BAZ_CONTENTS))
def test_import_conflict(self):
- actual = self.compiler.compile(scss_string='@import "/css/path_conflict";')
+ actual = self.compiler.compile_string('@import "/css/path_conflict";')
self.assertEqual(clean_css(actual), clean_css(PATH_CONFLICT_CONTENTS))
@override_settings(DEBUG=True)
-class FindersImportTest(ImportTestMixin, TestCase):
+class FindersImportTest(ImportTestMixin, NoCollectStaticTestCase):
pass
@@ -133,11 +136,11 @@ $widgets: sprite-map('images/icons/widget-*.png');
class AssetsTest(CompilerTestMixin, TestCase):
def test_inline_image(self):
- actual = self.compiler.compile(scss_string=INLINE_IMAGE)
+ actual = self.compiler.compile_string(INLINE_IMAGE)
self.assertEqual(clean_css(actual), clean_css(INLINED_IMAGE_EXPECTED))
def test_sprite_images(self):
- actual = self.compiler.compile(scss_string=SPRITE_MAP)
+ actual = self.compiler.compile_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)
+ self.assertTrue(re.search(r'url\(/static/scss/assets/images_icons-.+\.png\?_=\d+', actual))
diff --git a/tests/utils.py b/tests/utils.py
index 1936481..beba759 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -1,11 +1,22 @@
+import shutil
+
from django.test import TestCase
from django.core.management import call_command
+from django.conf import settings
class CollectStaticTestCase(TestCase):
- def setUp(self):
- call_command('collectstatic', interactive=False)
- super(CollectStaticTestCase, self).setUp()
+ @classmethod
+ def setUpClass(cls):
+ super(CollectStaticTestCase, cls).setUpClass()
+ call_command('collectstatic', interactive=False, verbosity=0)
+
+
+class NoCollectStaticTestCase(TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(NoCollectStaticTestCase, cls).setUpClass()
+ shutil.rmtree(settings.STATIC_ROOT, ignore_errors=True)
def clean_css(string):
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..05eb1b5
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,21 @@
+[tox]
+envlist=
+ py{26,27}-dj{14},
+ py{27,33,34}-dj{17,18}
+
+[testenv]
+basepython=
+ py26: python2.6
+ py27: python2.7
+ py33: python3.3
+ py34: python3.4
+commands=
+ /usr/bin/env
+ python setup.py test
+deps=
+ dj14: Django>=1.4,<1.5
+ dj17: Django>=1.7,<1.8
+ dj18: Django>=1.8,<1.9
+whitelist_externals=
+ env
+ make