Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion LICENSE-MIT
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2015 Jacob Rief
Copyright (c) 2015-2023 Jacob Rief <jacob.rief@gmail.com>
Copyright (c) 2021 Dominik George <dominik.george@teckids.org>

Permission is hereby granted, free of charge, to any person obtaining a copy
Expand Down
6 changes: 0 additions & 6 deletions sass_processor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,3 @@
"""

__version__ = '1.2.2'

import django

if django.VERSION < (3, 2):
# https://docs.djangoproject.com/en/dev/releases/3.2/#whats-new-3-2
default_app_config = 'sass_processor.apps.SassProcessorConfig'
19 changes: 12 additions & 7 deletions sass_processor/storage.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
from django.conf import settings
from django.contrib.staticfiles.finders import get_finders
from django.core.files.storage import FileSystemStorage, get_storage_class
from django.core.files.storage import FileSystemStorage
from django.utils.functional import LazyObject
from django.utils.module_loading import import_string


class SassFileStorage(LazyObject):
def _setup(self):
storage_path = getattr(settings, 'SASS_PROCESSOR_STORAGE', settings.STATICFILES_STORAGE)
storage_options = getattr(settings, 'SASS_PROCESSOR_STORAGE_OPTIONS', {})
storage_class = get_storage_class(storage_path)

if storage_path == settings.STATICFILES_STORAGE and issubclass(storage_class, FileSystemStorage):
storage_options['location'] = getattr(settings, 'SASS_PROCESSOR_ROOT', settings.STATIC_ROOT)
if not (sass_processor_storage := settings.STORAGES.get('sass_processor')):
sass_processor_storage = settings.STORAGES.get('staticfiles', {})
storage_path = sass_processor_storage.get('BACKEND')
storage_options = sass_processor_storage.get('OPTIONS') or {}
storage_class = import_string(storage_path)
if issubclass(storage_class, FileSystemStorage):
storage_options['location'] = storage_options.pop('ROOT', None) or settings.STATIC_ROOT
storage_options['base_url'] = settings.STATIC_URL

self._wrapped = storage_class(**storage_options)

def __repr__(self):
return repr(self._wrapped)


def find_file(path):
for finder in get_finders():
Expand Down
15 changes: 8 additions & 7 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
[metadata]
name = django-sass-processor
version = attr: sass_processor.__version__
description = SASS processor to compile SCSS files into *.css, while rendering, or offline.
long_description = file: README.md
long_description_content_type = text/markdown
Expand All @@ -19,13 +18,15 @@ classifiers =
Operating System :: OS Independent
Programming Language :: Python
Topic :: Internet :: WWW/HTTP :: Dynamic Content
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Framework :: Django :: 2.2
Framework :: Django :: 3.2
Framework :: Django :: 4.0
Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.12
Framework :: Django :: 4.2
Framework :: Django :: 5.0
Framework :: Django :: 5.1
Framework :: Django :: 5.2
Framework :: Django :: 6.0


[options]
packages = find:
Expand Down
5 changes: 4 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#!/usr/bin/env python
from setuptools import setup
from sass_processor import __version__

setup()
setup(
version=__version__,
)
13 changes: 6 additions & 7 deletions tests/settings.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import os
from pathlib import Path

from tests.jinja2 import environment

Expand All @@ -13,6 +13,8 @@
}
}

PROJECT_ROOT = Path(__file__).parent

INSTALLED_APPS = [
'django.contrib.contenttypes',
'django.contrib.sites',
Expand Down Expand Up @@ -64,18 +66,18 @@

STATIC_URL = '/static/'

PROJECT_ROOT = os.path.abspath(os.path.join(__file__, os.path.pardir))

STATICFILES_FINDERS = [
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
'sass_processor.finders.CssFinder',
]

STATICFILES_DIRS = [
os.path.join(PROJECT_ROOT, 'static'),
PROJECT_ROOT / 'static',
]

STATIC_ROOT = PROJECT_ROOT / 'tmpstatic'

SASS_PROCESSOR_ENABLED = True

SASS_PROCESSOR_CUSTOM_FUNCTIONS = {
Expand All @@ -85,6 +87,3 @@
}

SASS_BLUE_COLOR = '#0000ff'


STATIC_ROOT = os.path.join(os.path.dirname(__file__), "tmpstatic")
63 changes: 43 additions & 20 deletions tests/test_sass_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,30 +28,30 @@ def assert_sass_src_engine(self, template_name, engine):
)
# Strip the line breaks.
template_content = template.render({}).strip()
self.assertEqual('/static/tests/css/main.css', template_content)
assert template_content == '/static/tests/css/main.css'

css_file = os.path.join(settings.STATIC_ROOT, 'tests/css/main.css')
self.assertTrue(os.path.exists(css_file))
with open(css_file, 'r') as f:
css_file = settings.STATIC_ROOT / 'tests/css/main.css'
assert css_file.exists()
with css_file.open('r') as f:
output = f.read()
expected = "#main p{color:#00ff00;width:97%}#main p .redbox{background-color:#ff0000}#main p .redbox:hover{color:#000000}\n\n/*# sourceMappingURL=main.css.map */"
self.assertEqual(expected, output)
assert expected == output

# check if compilation is skipped file for a second invocation of `sass_src`
timestamp = os.path.getmtime(css_file)
timestamp = css_file.stat().st_mtime
template.render({})
self.assertEqual(timestamp, os.path.getmtime(css_file))
assert timestamp == css_file.stat().st_mtime

# removing `main.css.map` should trigger a recompilation
os.remove(css_file + '.map')
css_file.with_suffix(css_file.suffix + '.map').unlink()
template.render({})
self.assertTrue(os.path.exists(css_file + '.map'))
assert css_file.with_suffix(css_file.suffix + '.map').exists()

# if `main.scss` is newer than `main.css`, recompile everything
longago = calendar.timegm(datetime(2017, 1, 1).timetuple())
os.utime(css_file, (longago, longago))
template.render({})
self.assertGreater(timestamp, os.path.getmtime(css_file))
assert timestamp > css_file.stat().st_mtime

def test_sass_src_django(self):
self.assert_sass_src_engine(
Expand All @@ -76,31 +76,31 @@ def test_sass_processor(self):

css_file = sass_processor('tests/css/bluebox.scss')
self.assertEqual('/static/tests/css/bluebox.css', css_file)
css_file = os.path.join(settings.STATIC_ROOT, 'tests/css/bluebox.css')
self.assertTrue(os.path.exists(css_file))
with open(css_file, 'r') as f:
css_file = settings.STATIC_ROOT / 'tests/css/bluebox.css'
assert css_file.exists()
with css_file.open('r') as f:
output = f.read()
expected = '.bluebox{background-color:#0000ff;margin:10.0px 5.0px 20.0px 15.0px;color:#fa0a78}\n\n/*# sourceMappingURL=bluebox.css.map */'
self.assertEqual(expected, output)
assert expected == output

def assert_management_command(self, **kwargs):
call_command(
'compilescss',
**kwargs
)
if kwargs.get('use_storage', False):
css_file = os.path.join(settings.STATIC_ROOT, 'tests/css/main.css')
css_file = settings.STATIC_ROOT / 'tests/css/main.css'
else:
css_file = os.path.join(settings.PROJECT_ROOT, 'static/tests/css/main.css')
with open(css_file, 'r') as f:
css_file = settings.PROJECT_ROOT / 'static/tests/css/main.css'
with css_file.open('r') as f:
output = f.read()
expected = '#main p{color:#00ff00;width:97%}#main p .redbox{background-color:#ff0000}#main p .redbox:hover{color:#000000}\n'
self.assertEqual(expected, output)
self.assertFalse(os.path.exists(css_file + '.map'))
assert expected == output
assert not css_file.with_suffix(css_file.suffix + '.map').exists()

if not kwargs.get('use_storage', False):
call_command('compilescss', delete_files=True)
self.assertFalse(os.path.exists(css_file))
assert not css_file.exists()

@override_settings(DEBUG=False)
def test_management_command_django(self):
Expand Down Expand Up @@ -140,3 +140,26 @@ def test_use_storage_multiple(self):
engine=['jinja2', 'django'],
use_storage=True
)

@override_settings(
DEBUG=False,
SASS_PROCESSOR_STORAGE='django.contrib.staticfiles.storage.StaticFilesStorage',
STATICFILES_STORAGE='django.contrib.staticfiles.storage.ManifestStaticFilesStorage',
)
def test_manifest_static_files_storage(self):
css_file = settings.PROJECT_ROOT / 'static/tests/css/main.css'
assert not css_file.exists()
call_command('compilescss', use_storage=False)
assert css_file.exists()
with css_file.open('r') as f:
output = f.read()
expected = '#main p{color:#00ff00;width:97%}#main p .redbox{background-color:#ff0000}#main p .redbox:hover{color:#000000}\n'
assert expected == output
hashed_css_file = settings.PROJECT_ROOT / 'tmpstatic/tests/css/main.08b498e281f7.css'
assert not hashed_css_file.exists()
call_command('collectstatic', interactive=False, ignore_patterns=['*.scss'])
assert hashed_css_file.exists()
with hashed_css_file.open('r') as f:
output = f.read()
assert expected == output
call_command('compilescss', use_storage=False, delete_files=True)