From 7abbc54c8edb14b676a46a2533b37008dece8e93 Mon Sep 17 00:00:00 2001 From: Andrey Fedoseev Date: Sat, 19 Aug 2023 13:29:48 +0600 Subject: [PATCH] Start working on 3.0 - BREAKING: The minimum supported Django version is now 3.2 (other versions may work, but are not tested) - BREAKING: Drop support for Python 3.7 and below - BREAKING: Drop support for Ruby-based Sass compiler --- .pre-commit-config.yaml | 14 +- .readthedocs.yml | 13 +- CHANGES.md | 7 + Dockerfile | 7 +- Dockerfile-base | 12 +- docs/compiler-settings.rst | 49 --- docs/conf.py | 4 +- docs/general-settings.rst | 2 +- pyproject.toml | 6 +- setup.cfg | 10 +- src/static_precompiler/__init__.py | 2 +- src/static_precompiler/caching.py | 2 +- src/static_precompiler/compilers/babel.py | 13 +- src/static_precompiler/compilers/base.py | 9 +- .../compilers/coffeescript.py | 1 - src/static_precompiler/compilers/dart_sass.py | 9 +- .../compilers/handlebars.py | 10 +- src/static_precompiler/compilers/less.py | 1 - src/static_precompiler/compilers/libsass.py | 3 - .../compilers/livescript.py | 1 - src/static_precompiler/compilers/ruby_scss.py | 323 ------------------ src/static_precompiler/compilers/scss.py | 2 +- src/static_precompiler/compilers/stylus.py | 1 - .../management/commands/compilestatic.py | 4 +- .../migrations/0001_initial.py | 1 - src/static_precompiler/models.py | 1 - src/static_precompiler/registry.py | 4 +- src/static_precompiler/settings.py | 2 +- .../templatetags/compile_static.py | 6 +- src/static_precompiler/types.py | 4 +- src/static_precompiler/utils.py | 13 +- src/static_precompiler/watch.py | 11 +- .../styles/sass/test-compass-import.scss | 6 - tests/staticfiles_dir/config.rb | 5 - tests/staticfiles_dir/test-compass.scss | 4 - tests/test_babel.py | 2 - tests/test_base_compiler.py | 6 - tests/test_coffeescript.py | 1 - tests/test_handlebars.py | 3 - tests/test_less.py | 2 - tests/test_livescript.py | 1 - tests/test_management.py | 4 - tests/test_registry.py | 3 - tests/test_scss.py | 101 +----- tests/test_stylus.py | 2 - tests/test_templatetags.py | 1 - tests/test_url_converter.py | 2 - tests/test_utils.py | 1 - tox.ini | 13 +- 49 files changed, 84 insertions(+), 620 deletions(-) delete mode 100644 src/static_precompiler/compilers/ruby_scss.py delete mode 100644 tests/static/styles/sass/test-compass-import.scss delete mode 100644 tests/staticfiles_dir/config.rb delete mode 100644 tests/staticfiles_dir/test-compass.scss diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 834386f..5c23416 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v4.4.0 hooks: - id: check-ast - id: check-merge-conflict @@ -14,14 +14,14 @@ repos: - id: trailing-whitespace - repo: https://github.com/charliermarsh/ruff-pre-commit # Ruff version. - rev: 'v0.0.198' + rev: 'v0.0.285' hooks: - id: ruff # Respect `exclude` and `extend-exclude` settings. - args: ["--force-exclude", "--fix"] + args: ["--fix"] exclude: ^docs/.* - repo: https://github.com/psf/black - rev: 22.10.0 + rev: 23.7.0 hooks: - id: black - repo: https://github.com/keewis/blackdoc @@ -30,11 +30,11 @@ repos: - id: blackdoc files: ^docs/.*\.rst$ - repo: https://github.com/shellcheck-py/shellcheck-py - rev: v0.8.0.4 + rev: v0.9.0.5 hooks: - id: shellcheck - repo: https://github.com/codespell-project/codespell - rev: v2.2.2 + rev: v2.2.5 hooks: - id: codespell files: ^docs/.*\.rst$ @@ -44,7 +44,7 @@ repos: - id: woke-from-source files: ^docs/.*\.rst$ - repo: https://github.com/jackdewinter/pymarkdown - rev: v0.9.8 + rev: v0.9.12 hooks: - id: pymarkdown args: [--disable-rules, MD041, scan] diff --git a/.readthedocs.yml b/.readthedocs.yml index 075a54a..8564d5b 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -5,13 +5,16 @@ # Required version: 2 -# Build documentation in the docs/ directory with Sphinx -sphinx: - configuration: docs/conf.py +build: + os: ubuntu-22.04 + tools: + python: "3" -# Optionally set the version of Python and requirements required to build your docs python: - version: 3 install: - method: pip path: . + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/conf.py diff --git a/CHANGES.md b/CHANGES.md index 0b995dc..e8adeb0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,12 @@ ## Changelog +### 3.0 + +- BREAKING: The minimum supported Django version is now 3.2 + (other versions may work, but are not tested) +- BREAKING: Drop support for Python 3.7 and below +- BREAKING: Drop support for Ruby-based Sass compiler + ### 2.4 #### Deprecation diff --git a/Dockerfile b/Dockerfile index 5d50044..1c9d8ba 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,10 @@ -FROM andreyfedoseev/django-static-precompiler:18.04-2 +FROM andreyfedoseev/django-static-precompiler:20.04-1 ARG DEBIAN_FRONTEND=noninteractive ENV TZ=Etc/UTC RUN apt update && \ apt install software-properties-common -y && \ add-apt-repository ppa:deadsnakes/ppa -y && \ apt install -y \ - python3.6-dev \ python3.8-dev \ python3.9-dev \ python3.9-distutils \ @@ -13,6 +12,8 @@ RUN apt update && \ python3.10-distutils \ python3.11-dev \ python3.11-distutils \ + python3.12-dev \ + python3.12-distutils \ python3-pip \ sqlite3 RUN mkdir /app @@ -20,4 +21,4 @@ WORKDIR /app ADD requirements-*.txt /app/ RUN pip3 install -r requirements-test.txt ADD . /app/ -RUN pip3 install -e .[libsass] +RUN pip3 install .[libsass] diff --git a/Dockerfile-base b/Dockerfile-base index 21d5e6f..d0ddd14 100644 --- a/Dockerfile-base +++ b/Dockerfile-base @@ -1,6 +1,7 @@ -FROM ubuntu:18.04 -MAINTAINER Andrey Fedoseev -RUN apt-get update && apt-get install -y autoconf libtool ruby-dev npm wget +FROM ubuntu:20.04 +MAINTAINER Andrey Fedoseev +ENV DEBIAN_FRONTEND=noninteractive +RUN apt update && apt install -y autoconf libtool npm wget RUN ln -s /usr/bin/nodejs /usr/local/bin/node RUN npm install -g coffeescript@2.7.0 RUN npm install -g livescript@1.6.0 @@ -8,8 +9,7 @@ RUN npm install -g less@4.1.3 RUN npm install -g babel-cli@6.26.0 RUN npm install -g stylus@0.50.0 RUN npm install -g handlebars@4.7.7 -RUN gem install sass -v 3.4.22 -RUN gem install compass -v 1.0.1 -RUN wget -O /tmp/dart-sass.tar.gz https://github.com/sass/dart-sass/releases/download/1.56.1/dart-sass-1.56.1-linux-x64.tar.gz && \ +RUN wget -O /tmp/dart-sass.tar.gz https://github.com/sass/dart-sass/releases/download/1.66.0/dart-sass-1.66.0-linux-x64.tar.gz && \ tar -xzf /tmp/dart-sass.tar.gz -C /opt && \ rm -rf /tmp/dart-sass.tar.gz +ENV PATH="/opt/dart-sass:${PATH}" diff --git a/docs/compiler-settings.rst b/docs/compiler-settings.rst index 87d396d..b892ae3 100644 --- a/docs/compiler-settings.rst +++ b/docs/compiler-settings.rst @@ -119,55 +119,6 @@ Example: SASS / SCSS =========== -Ruby-based (legacy) SASS compiler. - -``executable`` - Path to SASS compiler executable. Default: "sass". - -``sourcemap_enabled`` - Boolean. Set to ``True`` to enable source maps. Default: ``False``. - -``compass_enabled`` - Boolean. Whether to use compass or not. Compass must be installed in your system. - Run ``sass --compass`` and if no error is shown it means that compass is installed. - -``load_paths`` - List of additional directories to look imported files (``--load-path`` command line option). Default: ``None``. - -``precision`` - How many digits of precision to use when outputting decimal numbers. Default: ``None``. - Set this to 8 or more if you compile Bootstrap. - -``output_style`` - Output style. Default: ``None``. - Can be nested, compact, compressed, or expanded. - -Example: - -.. code-block:: python - - STATIC_PRECOMPILER_COMPILERS = ( - ( - "static_precompiler.compilers.SCSS", - { - "executable": "/usr/bin/sass", - "sourcemap_enabled": True, - "compass_enabled": True, - "load_paths": ["/path"], - "precision": 8, - "output_style": "compressed", - }, - ), - ) - - -Dart Sass -========= - -Dart Sass is the current implementation of SASS. - -.. note:: Dart Sass compiler is not enabled by default. See the example below for how to enable it. - Options: ``sourcemap_enabled`` diff --git a/docs/conf.py b/docs/conf.py index b327c72..fe0907f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -51,9 +51,9 @@ # built documents. # # The short X.Y version. -version = "2.4" +version = "3.0" # The full version, including alpha/beta/rc tags. -release = version +release = "3.0a1" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/general-settings.rst b/docs/general-settings.rst index 46b2202..1519580 100644 --- a/docs/general-settings.rst +++ b/docs/general-settings.rst @@ -23,7 +23,7 @@ General settings STATIC_PRECOMPILER_COMPILERS = ( ("static_precompiler.compilers.CoffeeScript", {"executable": "/usr/bin/coffeescript"}), - ("static_precompiler.compilers.SCSS", {"compass_enabled": True}), + ("static_precompiler.compilers.SCSS", {"load_paths": ["/some/path"]}), ) diff --git a/pyproject.toml b/pyproject.toml index 9a323ea..04278b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,15 +7,15 @@ line-length = 120 [tool.ruff] select = ["F", "E", "W", "I", "YTT", "B", "C4", "T10", "SIM", "ERA", "RUF", "UP"] -ignore = ["RUF001"] +ignore = ["RUF001", "RUF012"] line-length = 120 -target-version = "py36" +target-version = "py38" [tool.ruff.isort] known-first-party = ["static_precompiler"] [tool.mypy] -python_version = "3.6" +python_version = "3.8" plugins = ["mypy_django_plugin.main"] [[tool.mypy.overrides]] diff --git a/setup.cfg b/setup.cfg index cbef67b..34a75ab 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = django-static-precompiler -version = 2.4 +version = 3.0a1 author = Andrey Fedoseev author_email = andrey@andreyfedoseev.com description = @@ -15,18 +15,16 @@ project_urls = Documentation = https://django-static-precompiler.readthedocs.io/ classifiers = Development Status :: 5 - Production/Stable - Framework :: Django :: 2 Framework :: Django :: 3 Framework :: Django :: 4 Intended Audience :: Developers License :: OSI Approved :: BSD License Operating System :: OS Independent - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 Topic :: Internet :: WWW/HTTP keywords = sass @@ -45,9 +43,9 @@ keywords = package_dir = = src packages = find: -python_requires = >=3.6 +python_requires = >=3.8 install_requires = - Django>=2.0 + Django>=3.2 tests_require = pytest pytest-django diff --git a/src/static_precompiler/__init__.py b/src/static_precompiler/__init__.py index 8f1ef02..405f20b 100644 --- a/src/static_precompiler/__init__.py +++ b/src/static_precompiler/__init__.py @@ -1 +1 @@ -__version__ = "2.4" +__version__ = "3.0a1" diff --git a/src/static_precompiler/caching.py b/src/static_precompiler/caching.py index 60ad186..fd0d8bc 100644 --- a/src/static_precompiler/caching.py +++ b/src/static_precompiler/caching.py @@ -12,7 +12,7 @@ def get_cache() -> BaseCache: if settings.CACHE_NAME: return django.core.cache.caches.get(settings.CACHE_NAME) # type: ignore - return django.core.cache.cache # type: ignore + return django.core.cache.cache def get_cache_key(key: str) -> str: diff --git a/src/static_precompiler/compilers/babel.py b/src/static_precompiler/compilers/babel.py index c83d7ff..b12223a 100644 --- a/src/static_precompiler/compilers/babel.py +++ b/src/static_precompiler/compilers/babel.py @@ -9,7 +9,6 @@ class Babel(base.BaseCompiler): - name = "babel" input_extension = "es6" output_extension = "js" @@ -25,7 +24,9 @@ def __init__( self.executable = executable self.is_sourcemap_enabled = sourcemap_enabled if modules: - warnings.warn("'modules' option is removed in Babel 6.0. Use `plugins` instead.", DeprecationWarning) + warnings.warn( + "'modules' option is removed in Babel 6.0. Use `plugins` instead.", DeprecationWarning, stacklevel=2 + ) self.modules = modules self.plugins = plugins self.presets = presets @@ -46,9 +47,7 @@ def get_extra_args(self) -> List[str]: return args def compile_file(self, source_path: str) -> str: - args = [ - self.executable, - ] + self.get_extra_args() + args = [self.executable, *self.get_extra_args()] if self.is_sourcemap_enabled: args.append("-s") @@ -72,9 +71,7 @@ def compile_file(self, source_path: str) -> str: return self.get_output_path(source_path) def compile_source(self, source: str) -> str: - args = [ - self.executable, - ] + self.get_extra_args() + args = [self.executable, *self.get_extra_args()] return_code, out, errors = utils.run_command(args, input=source) if return_code: diff --git a/src/static_precompiler/compilers/base.py b/src/static_precompiler/compilers/base.py index c4efb19..bd40d6f 100644 --- a/src/static_precompiler/compilers/base.py +++ b/src/static_precompiler/compilers/base.py @@ -1,3 +1,4 @@ +import contextlib import logging import os import posixpath @@ -17,7 +18,6 @@ class BaseCompiler: - name: str supports_dependencies: bool = False input_extension: str = "" @@ -49,10 +49,8 @@ def get_full_source_path(self, source_path: str) -> str: if os.path.exists(full_path): return full_path - try: - full_path = finders.find(norm_source_path) # type: ignore - except django.core.exceptions.SuspiciousOperation: - pass + with contextlib.suppress(django.core.exceptions.SuspiciousOperation): + full_path = finders.find(norm_source_path) if full_path is None: raise ValueError(f"Can't find staticfile named: {source_path}") @@ -157,7 +155,6 @@ def compile(self, source_path: str, from_management: bool = False, verbosity: in compiled_path = self.get_output_path(source_path) if self.should_compile(source_path, from_management=from_management): - compiled_path = self.compile_file(source_path) if self.supports_dependencies: diff --git a/src/static_precompiler/compilers/coffeescript.py b/src/static_precompiler/compilers/coffeescript.py index ecfa966..2c65a9e 100644 --- a/src/static_precompiler/compilers/coffeescript.py +++ b/src/static_precompiler/compilers/coffeescript.py @@ -7,7 +7,6 @@ class CoffeeScript(base.BaseCompiler): - name = "coffeescript" input_extension = "coffee" output_extension = "js" diff --git a/src/static_precompiler/compilers/dart_sass.py b/src/static_precompiler/compilers/dart_sass.py index b832493..67fba85 100644 --- a/src/static_precompiler/compilers/dart_sass.py +++ b/src/static_precompiler/compilers/dart_sass.py @@ -14,7 +14,6 @@ class SCSS(base.BaseCompiler): - name = "scss" supports_dependencies = True input_extension = "scss" @@ -56,7 +55,7 @@ def should_compile(self, source_path: str, from_management: bool = False) -> boo def compile_file(self, source_path: str) -> str: full_source_path = self.get_full_source_path(source_path) full_output_path = self.get_full_output_path(source_path) - args = [self.executable] + self.get_extra_args() + args = [self.executable, *self.get_extra_args()] if self.is_sourcemap_enabled: args.append("--source-map") else: @@ -91,7 +90,7 @@ def compile_file(self, source_path: str) -> str: return self.get_output_path(source_path) def compile_source(self, source: str) -> str: - args = [self.executable, "--stdin", "--no-indented"] + self.get_extra_args() + args = [self.executable, "--stdin", "--no-indented", *self.get_extra_args()] return_code, out, errors = utils.run_command(args, input=source) if return_code: @@ -112,7 +111,6 @@ def parse_import_string(self, import_string: str) -> List[str]: item_allowed = True for char in import_string: - if char == ")": in_parentheses = False continue @@ -273,7 +271,6 @@ def find_dependencies(self, source_path: str) -> List[str]: # noinspection PyAbstractClass class SASS(SCSS): - name = "sass" input_extension = "sass" import_extensions = ("sass", "scss") @@ -281,7 +278,7 @@ class SASS(SCSS): IMPORT_RE = re.compile(r"@import\s+(.+?)\s*(?:\n|$)") def compile_source(self, source: str) -> str: - args = [self.executable, "--stdin", "--indented"] + self.get_extra_args() + args = [self.executable, "--stdin", "--indented", *self.get_extra_args()] return_code, out, errors = utils.run_command(args, input=source) if return_code: diff --git a/src/static_precompiler/compilers/handlebars.py b/src/static_precompiler/compilers/handlebars.py index a942d8a..76397fc 100644 --- a/src/static_precompiler/compilers/handlebars.py +++ b/src/static_precompiler/compilers/handlebars.py @@ -9,7 +9,6 @@ class Handlebars(base.BaseCompiler): - name = "handlebars" input_extensions = ( "hbs", @@ -65,7 +64,8 @@ def compile_file(self, source_path: str) -> str: template_extension, "-f", full_output_path, - ] + self.get_extra_args() + *self.get_extra_args(), + ] if self.is_sourcemap_enabled: args += ["--map", full_output_path + ".map"] @@ -81,11 +81,7 @@ def compile_file(self, source_path: str) -> str: return self.get_output_path(source_path) def compile_source(self, source: str) -> str: - args = [ - self.executable, - "-i", - "-", - ] + self.get_extra_args() + args = [self.executable, "-i", "-", *self.get_extra_args()] return_code, out, errors = utils.run_command(args, input=source) if return_code: diff --git a/src/static_precompiler/compilers/less.py b/src/static_precompiler/compilers/less.py index f2b64b2..7a9c6e5 100644 --- a/src/static_precompiler/compilers/less.py +++ b/src/static_precompiler/compilers/less.py @@ -11,7 +11,6 @@ class LESS(base.BaseCompiler): - name = "less" supports_dependencies = True input_extension = "less" diff --git a/src/static_precompiler/compilers/libsass.py b/src/static_precompiler/compilers/libsass.py index 5b31e6f..3205afc 100644 --- a/src/static_precompiler/compilers/libsass.py +++ b/src/static_precompiler/compilers/libsass.py @@ -16,10 +16,8 @@ class SCSS(dart_sass.SCSS): - IMPORT_RE = re.compile(r"@import\s+(.+?)\s*;", re.DOTALL) indented = False - is_compass_enabled = False def __init__( self, @@ -89,7 +87,6 @@ def compile_source(self, source: str) -> str: # noinspection PyAbstractClass class SASS(SCSS): - name = "sass" input_extension = "sass" import_extensions = ("sass", "scss") diff --git a/src/static_precompiler/compilers/livescript.py b/src/static_precompiler/compilers/livescript.py index 3a52382..e6897c7 100644 --- a/src/static_precompiler/compilers/livescript.py +++ b/src/static_precompiler/compilers/livescript.py @@ -8,7 +8,6 @@ class LiveScript(base.BaseCompiler): - name = "livescript" input_extension = "ls" output_extension = "js" diff --git a/src/static_precompiler/compilers/ruby_scss.py b/src/static_precompiler/compilers/ruby_scss.py deleted file mode 100644 index 023adf1..0000000 --- a/src/static_precompiler/compilers/ruby_scss.py +++ /dev/null @@ -1,323 +0,0 @@ -import os -import posixpath -import re -import warnings -from typing import List, Match, Optional - -from .. import exceptions, url_converter, utils -from ..types import StrCollection -from . import base - -__all__ = ( - "SCSS", - "SASS", -) - - -warnings.warn( - "Ruby-based SCSS/SASS compilers are deprecated and will be removed in version 3.0. " - "The default SCSS/SASS compiler will be switched to Dart SASS in version 3.0", - DeprecationWarning, -) - - -class SCSS(base.BaseCompiler): - - name = "scss" - supports_dependencies = True - input_extension = "scss" - output_extension = "css" - import_extensions = ("scss", "sass") - - IMPORT_RE = re.compile(r"@import\s+(.+?)\s*;", re.DOTALL) - - def __init__( - self, - executable: str = "sass", - sourcemap_enabled: bool = False, - compass_enabled: bool = False, - load_paths: Optional[StrCollection] = None, - precision: Optional[int] = None, - output_style: Optional[str] = None, - ): - self.executable = executable - self.is_sourcemap_enabled = sourcemap_enabled - self.is_compass_enabled = compass_enabled - self.precision = precision - self.output_style = output_style - self.load_paths: StrCollection = load_paths or [] - super().__init__() - - def get_extra_args(self) -> List[str]: - args = [] - - for path in self.load_paths: - args += ["-I", path] - - if self.is_compass_enabled: - args.append("--compass") - - if self.precision: - args += ["--precision", str(self.precision)] - - if self.output_style: - args += ["-t", self.output_style] - - return args - - def should_compile(self, source_path: str, from_management: bool = False) -> bool: - # Do not compile the files that start with "_" if run from management - if from_management and os.path.basename(source_path).startswith("_"): - return False - return super().should_compile(source_path, from_management) - - def compile_file(self, source_path: str) -> str: - full_source_path = self.get_full_source_path(source_path) - full_output_path = self.get_full_output_path(source_path) - args = [ - self.executable, - "--sourcemap={}".format("auto" if self.is_sourcemap_enabled else "none"), - ] + self.get_extra_args() - - args.extend( - [ - self.get_full_source_path(source_path), - full_output_path, - ] - ) - - full_output_dirname = os.path.dirname(full_output_path) - if not os.path.exists(full_output_dirname): - os.makedirs(full_output_dirname) - - # `cwd` is a directory containing `source_path`. - # Ex: source_path = '1/2/3', full_source_path = '/abc/1/2/3' -> cwd = '/abc' - cwd = os.path.normpath(os.path.join(full_source_path, *([".."] * len(source_path.split("/"))))) - return_code, out, errors = utils.run_command(args, cwd=cwd) - - if return_code: - if os.path.exists(full_output_path): - os.remove(full_output_path) - raise exceptions.StaticCompilationError(errors) - - url_converter.convert_urls(full_output_path, source_path) - - if self.is_sourcemap_enabled: - utils.fix_sourcemap(full_output_path + ".map", source_path, full_output_path) - - return self.get_output_path(source_path) - - def compile_source(self, source: str) -> str: - args = [ - self.executable, - "-s", - ] + self.get_extra_args() - - if self.executable.endswith("sass"): - args.append("--scss") - - return_code, out, errors = utils.run_command(args, input=source) - if return_code: - raise exceptions.StaticCompilationError(errors) - - return out - - # noinspection PyMethodMayBeStatic - def parse_import_string(self, import_string: str) -> List[str]: - """Extract import items from import string. - :param import_string: import string - """ - items = [] - item = "" - in_quotes = False - quote = "" - in_parentheses = False - item_allowed = True - - for char in import_string: - - if char == ")": - in_parentheses = False - continue - - if in_parentheses: - continue - - if char == "(": - item = "" - in_parentheses = True - continue - - if char == ",": - if in_quotes: - item += char - else: - if item: - items.append(item) - item = "" - item_allowed = True - continue - - if char in " \t\n\r\f\v": - if in_quotes: - item += char - elif item: - items.append(item) - item_allowed = False - item = "" - continue - - if char in "\"'": - if in_quotes: - if char == quote: - # Close quote - in_quotes = False - else: - item += char - else: - in_quotes = True - quote = char - continue - - if not item_allowed: - break - - item += char - - if item: - items.append(item) - - return sorted(items) - - def strip_comments(self, source: str) -> str: - """Strip comments from source, it does not remove comments inside - strings or comments inside functions calls. - - Contribution taken from and added function call pattern - https://stackoverflow.com/questions/2319019/using-regex-to-remove-comments-from-source-files - - :param source: source code - """ - pattern = r"(\".*?\"|\'.*?\'|\(.*?\))|(\s*/\*.*?\*/|\s*//[^\r\n]*$)" - # first group captures quoted sources (double or single) - # second group captures comments (//single-line or /* multi-line */) - regex = re.compile(pattern, re.MULTILINE | re.DOTALL) - - def _replacer(match: Match[str]) -> str: - # if the 2nd group (capturing comments) is not None, - # it means we have captured a non-quoted (real) comment source. - if match.group(2) is not None: - return "" # so we will return empty to remove the comment - else: # otherwise, we will return the 1st group - return match.group(1) # captured quoted-source or function call - - return regex.sub(_replacer, source) - - def find_imports(self, source: str) -> List[str]: - """Find the imported files in the source code. - - :param source: source code - """ - source = self.strip_comments(source) - imports = set() - for import_string in self.IMPORT_RE.findall(source): - for import_item in self.parse_import_string(import_string): - import_item = import_item.strip() - if not import_item: - continue - if import_item.endswith(".css"): - continue - if import_item.startswith("http://") or import_item.startswith("https://"): - continue - if self.is_compass_enabled and ( - import_item in ("compass", "compass.scss") or import_item.startswith("compass/") - ): - # Ignore compass imports if Compass is enabled. - continue - imports.add(import_item) - return sorted(imports) - - def get_full_source_path(self, source_path: str) -> str: - try: - return super().get_full_source_path(source_path) - except ValueError: - # Try to locate the source file in directories specified in `load_paths` - norm_source_path = utils.normalize_path(source_path.lstrip("/")) - for dirname in self.load_paths: - full_path = os.path.join(dirname, norm_source_path) - if os.path.exists(full_path): - return full_path - raise - - def locate_imported_file(self, source_dir: str, import_path: str) -> str: - """Locate the imported file in the source directory. - Return the path to the imported file relative to STATIC_ROOT - - :param source_dir: source directory - :param import_path: path to the imported file - """ - import_filename = posixpath.basename(import_path) - import_dirname = posixpath.dirname(import_path) - import_filename_root, import_filename_extension = posixpath.splitext(import_filename) - - if import_filename_extension: - filenames_to_try = [import_filename] - else: - # No extension is specified for the imported file, try all supported extensions - filenames_to_try = [import_filename_root + "." + extension for extension in self.import_extensions] - - if not import_filename.startswith("_"): - # Try the files with "_" prefix - filenames_to_try += ["_" + filename for filename in filenames_to_try] - - # Try to locate the file in the directory relative to `source_dir` - for filename in filenames_to_try: - source_path = posixpath.normpath(posixpath.join(source_dir, import_dirname, filename)) - try: - self.get_full_source_path(source_path) - return source_path - except ValueError: - pass - - # Try to locate the file in the directories listed in `load_paths` - for dirname in self.load_paths: - for filename in filenames_to_try: - source_path = posixpath.join(import_dirname, filename) - if os.path.exists(os.path.join(dirname, utils.normalize_path(source_path))): - return source_path - - raise exceptions.StaticCompilationError(f"Can't locate the imported file: {import_path}") - - def find_dependencies(self, source_path: str) -> List[str]: - source = self.get_source(source_path) - source_dir = posixpath.dirname(source_path) - dependencies = set() - for import_path in self.find_imports(source): - import_path = self.locate_imported_file(source_dir, import_path) - dependencies.add(import_path) - dependencies.update(self.find_dependencies(import_path)) - return sorted(dependencies) - - -# noinspection PyAbstractClass -class SASS(SCSS): - - name = "sass" - input_extension = "sass" - import_extensions = ("sass", "scss") - - IMPORT_RE = re.compile(r"@import\s+(.+?)\s*(?:\n|$)") - - def compile_source(self, source: str) -> str: - args = [ - self.executable, - "-s", - ] + self.get_extra_args() - if self.executable.endswith("scss"): - args.append("--sass") - - return_code, out, errors = utils.run_command(args, input=source) - if return_code: - raise exceptions.StaticCompilationError(errors) - - return out diff --git a/src/static_precompiler/compilers/scss.py b/src/static_precompiler/compilers/scss.py index fde86ee..b2ae8b8 100644 --- a/src/static_precompiler/compilers/scss.py +++ b/src/static_precompiler/compilers/scss.py @@ -1 +1 @@ -from .ruby_scss import SASS, SCSS # noqa +from .dart_sass import SASS, SCSS # noqa diff --git a/src/static_precompiler/compilers/stylus.py b/src/static_precompiler/compilers/stylus.py index 71e8aa5..8bf5051 100644 --- a/src/static_precompiler/compilers/stylus.py +++ b/src/static_precompiler/compilers/stylus.py @@ -10,7 +10,6 @@ class Stylus(base.BaseCompiler): - name = "stylus" input_extension = "styl" output_extension = "css" diff --git a/src/static_precompiler/management/commands/compilestatic.py b/src/static_precompiler/management/commands/compilestatic.py index c56661a..a0c4f48 100644 --- a/src/static_precompiler/management/commands/compilestatic.py +++ b/src/static_precompiler/management/commands/compilestatic.py @@ -54,10 +54,9 @@ def delete_stale_files(compiled_files: StrCollection) -> None: class Command(django.core.management.base.BaseCommand): - help = "Compile static files." - requires_system_checks = [] # type: ignore + requires_system_checks = [] def add_arguments(self, parser: ArgumentParser) -> None: parser.add_argument( @@ -90,7 +89,6 @@ def add_arguments(self, parser: ArgumentParser) -> None: ) def handle(self, *args: Any, **options: Any) -> None: - if not options["watch"] and not options["initial_scan"]: sys.exit("--no-initial-scan option should be used with --watch.") diff --git a/src/static_precompiler/migrations/0001_initial.py b/src/static_precompiler/migrations/0001_initial.py index 7838099..bcb8eb8 100644 --- a/src/static_precompiler/migrations/0001_initial.py +++ b/src/static_precompiler/migrations/0001_initial.py @@ -2,7 +2,6 @@ class Migration(migrations.Migration): - operations = [ migrations.CreateModel( name="Dependency", diff --git a/src/static_precompiler/models.py b/src/static_precompiler/models.py index 60c0245..b6abff3 100644 --- a/src/static_precompiler/models.py +++ b/src/static_precompiler/models.py @@ -2,7 +2,6 @@ class Dependency(models.Model): - source = models.CharField(max_length=255, db_index=True) depends_on = models.CharField(max_length=255, db_index=True) diff --git a/src/static_precompiler/registry.py b/src/static_precompiler/registry.py index 413f619..f22faf0 100644 --- a/src/static_precompiler/registry.py +++ b/src/static_precompiler/registry.py @@ -27,7 +27,7 @@ def build_compilers() -> Dict[str, BaseCompiler]: if len(compiler_path) != 2: raise django.core.exceptions.ImproperlyConfigured( 'Compiler must be specified in the format ("path.to.CompilerClass", {{compiler options...}}),' - " got {0}".format(compiler_path) + f" got {compiler_path}" ) compiler_path, compiler_options = compiler_path if not isinstance(compiler_options, dict): @@ -55,7 +55,7 @@ def build_compilers() -> Dict[str, BaseCompiler]: compiler_to_add = compiler_class(**compiler_options) compiler = compilers.setdefault(compiler_class.name, compiler_to_add) if compiler_to_add != compiler: - warnings.warn(f"Both compilers {compiler_to_add} and {compiler} have the same name.") + warnings.warn(f"Both compilers {compiler_to_add} and {compiler} have the same name.", stacklevel=2) return compilers diff --git a/src/static_precompiler/settings.py b/src/static_precompiler/settings.py index 6832457..effe11c 100644 --- a/src/static_precompiler/settings.py +++ b/src/static_precompiler/settings.py @@ -6,7 +6,7 @@ STATIC_ROOT = getattr(settings, "STATIC_ROOT", settings.MEDIA_ROOT) STATIC_URL = getattr(settings, "STATIC_URL", settings.MEDIA_URL) -POSIX_COMPATIBLE = True if os.name == "posix" else False +POSIX_COMPATIBLE = os.name == "posix" MTIME_DELAY = getattr(settings, "STATIC_PRECOMPILER_MTIME_DELAY", 10) # 10 seconds diff --git a/src/static_precompiler/templatetags/compile_static.py b/src/static_precompiler/templatetags/compile_static.py index 0584c36..92c121f 100644 --- a/src/static_precompiler/templatetags/compile_static.py +++ b/src/static_precompiler/templatetags/compile_static.py @@ -25,11 +25,9 @@ def compile_tag(source_path: str, compiler: Optional[BaseCompiler] = None) -> st "{% compile %} tag has been deprecated, use `compile` filter from `compile_static` template tag library " "instead.", DeprecationWarning, + stacklevel=2, ) - if compiler: - compiled = compiler.compile(source_path) - else: - compiled = utils.compile_static(source_path) + compiled = compiler.compile(source_path) if compiler else utils.compile_static(source_path) if settings.PREPEND_STATIC_URL: compiled = django.templatetags.static.static(compiled) return compiled diff --git a/src/static_precompiler/types.py b/src/static_precompiler/types.py index f1ec6e1..0bcad94 100644 --- a/src/static_precompiler/types.py +++ b/src/static_precompiler/types.py @@ -1,4 +1,4 @@ -from typing import List, Set, Tuple, Union +from typing import Iterable, List, Tuple, Union -StrCollection = Union[List[str], Tuple[str, ...], Set[str]] +StrCollection = Iterable[str] OrderedStrCollection = Union[List[str], Tuple[str, ...]] diff --git a/src/static_precompiler/utils.py b/src/static_precompiler/utils.py index 96a94ed..e684d6c 100644 --- a/src/static_precompiler/utils.py +++ b/src/static_precompiler/utils.py @@ -4,18 +4,11 @@ import subprocess from typing import Any, Dict, List, Optional, Tuple, Union -import django.conf -import django.core.cache -import django.core.exceptions from django.utils import encoding from . import settings -def get_file_encoding() -> str: - return getattr(django.conf.settings, "FILE_CHARSET", "utf-8") - - def normalize_path(posix_path: str) -> str: """Convert posix style path to OS-dependent path.""" if settings.POSIX_COMPATIBLE: @@ -25,7 +18,7 @@ def normalize_path(posix_path: str) -> str: def read_file(path: str) -> str: """Return the contents of a file as text.""" - with open(path, encoding=get_file_encoding()) as file_object: + with open(path, encoding="utf-8") as file_object: return file_object.read() @@ -35,7 +28,7 @@ def write_file(content: str, path: str) -> None: # Convert to unicode content = encoding.force_str(content) - with open(path, "w+", encoding=get_file_encoding()) as file_object: + with open(path, "w+", encoding="utf-8") as file_object: file_object.write(content) @@ -48,7 +41,6 @@ def normalize_whitespace(text: str) -> str: def run_command( args: List[str], input: Optional[Union[bytes, str]] = None, cwd: Optional[str] = None ) -> Tuple[int, str, str]: - popen_kwargs: Dict[str, Any] = { "stdout": subprocess.PIPE, "stderr": subprocess.PIPE, @@ -88,7 +80,6 @@ def compile_static_lazy(path: str) -> str: def fix_sourcemap(sourcemap_full_path: str, source_path: str, compiled_full_path: str) -> None: - sourcemap = json.loads(read_file(sourcemap_full_path)) # Stylus, unlike SASS, can't add correct relative paths in source map when the compiled file diff --git a/src/static_precompiler/watch.py b/src/static_precompiler/watch.py index c89a4ef..adab6a9 100644 --- a/src/static_precompiler/watch.py +++ b/src/static_precompiler/watch.py @@ -1,14 +1,13 @@ import time from typing import Any, Collection, Iterable -from watchdog import events, observers # type: ignore +from watchdog import events, observers from . import exceptions, registry from .compilers import BaseCompiler -class EventHandler(events.FileSystemEventHandler): # type: ignore - +class EventHandler(events.FileSystemEventHandler): # noinspection PyShadowingNames def __init__(self, scanned_dir: str, verbosity: int, compilers: Collection[BaseCompiler]) -> None: self.scanned_dir = scanned_dir @@ -48,14 +47,14 @@ def watch_dirs(scanned_dirs: Iterable[str], verbosity: int) -> None: for scanned_dir in scanned_dirs: handler = EventHandler(scanned_dir, verbosity, compilers) - observer.schedule(handler, path=scanned_dir, recursive=True) + observer.schedule(handler, path=scanned_dir, recursive=True) # type: ignore - observer.start() + observer.start() # type: ignore try: while True: time.sleep(1) except KeyboardInterrupt: - observer.stop() + observer.stop() # type: ignore observer.join() diff --git a/tests/static/styles/sass/test-compass-import.scss b/tests/static/styles/sass/test-compass-import.scss deleted file mode 100644 index 6c1bb8c..0000000 --- a/tests/static/styles/sass/test-compass-import.scss +++ /dev/null @@ -1,6 +0,0 @@ -@import "compass/css3"; - - -.round-corners { - @include border-radius(4px, 4px); -} diff --git a/tests/staticfiles_dir/config.rb b/tests/staticfiles_dir/config.rb deleted file mode 100644 index 8f37809..0000000 --- a/tests/staticfiles_dir/config.rb +++ /dev/null @@ -1,5 +0,0 @@ -# Require any additional compass plugins here. - -# Set this to the root of your project when deployed: -http_path = "/static/" -images_dir = "images" diff --git a/tests/staticfiles_dir/test-compass.scss b/tests/staticfiles_dir/test-compass.scss deleted file mode 100644 index 8ba773a..0000000 --- a/tests/staticfiles_dir/test-compass.scss +++ /dev/null @@ -1,4 +0,0 @@ -p { - //noinspection SassScssUnresolvedFunction - background: image-url("test.png", false, false); -} diff --git a/tests/test_babel.py b/tests/test_babel.py index c81f462..c9668dd 100644 --- a/tests/test_babel.py +++ b/tests/test_babel.py @@ -27,7 +27,6 @@ def test_compile_file(monkeypatch, tmpdir): def test_sourcemap(monkeypatch, tmpdir): - monkeypatch.setattr("static_precompiler.settings.ROOT", tmpdir.strpath) compiler = compilers.Babel(sourcemap_enabled=False) @@ -65,7 +64,6 @@ def test_compile_source(): def test_get_extra_args(): - compiler = compilers.Babel(plugins="bar", presets="baz") assert compiler.get_extra_args() == [ diff --git a/tests/test_base_compiler.py b/tests/test_base_compiler.py index 187141f..2b678be 100644 --- a/tests/test_base_compiler.py +++ b/tests/test_base_compiler.py @@ -8,7 +8,6 @@ def test_is_supported(monkeypatch): - monkeypatch.setattr("static_precompiler.compilers.base.BaseCompiler.input_extension", "foo") compiler = compilers.BaseCompiler() @@ -18,7 +17,6 @@ def test_is_supported(monkeypatch): def test_get_output_filename(monkeypatch): - compiler = compilers.BaseCompiler() monkeypatch.setattr(compiler, "input_extension", "coffee") @@ -50,7 +48,6 @@ def test_get_full_source_path(): def test_get_output_path(monkeypatch): - compiler = compilers.BaseCompiler() monkeypatch.setattr(compiler, "get_output_filename", lambda source_path: source_path.replace(".coffee", ".js")) @@ -59,7 +56,6 @@ def test_get_output_path(monkeypatch): def test_get_full_output_path(monkeypatch): - compiler = compilers.BaseCompiler() monkeypatch.setattr(compiler, "get_output_path", lambda source_path: settings.OUTPUT_DIR + "/dummy.js") @@ -67,7 +63,6 @@ def test_get_full_output_path(monkeypatch): def test_get_source_mtime(monkeypatch): - compiler = compilers.BaseCompiler() monkeypatch.setattr(compiler, "get_full_source_path", lambda source_path: "dummy.coffee") @@ -77,7 +72,6 @@ def test_get_source_mtime(monkeypatch): def test_get_output_mtime(monkeypatch): - compiler = compilers.BaseCompiler() monkeypatch.setattr(compiler, "get_full_output_path", lambda output_path: "dummy.js") diff --git a/tests/test_coffeescript.py b/tests/test_coffeescript.py index 0a7931e..ac2a54f 100644 --- a/tests/test_coffeescript.py +++ b/tests/test_coffeescript.py @@ -24,7 +24,6 @@ def test_compile_file(monkeypatch, tmpdir): def test_sourcemap(monkeypatch, tmpdir): - monkeypatch.setattr("static_precompiler.settings.ROOT", tmpdir.strpath) compiler = compilers.CoffeeScript(sourcemap_enabled=False) diff --git a/tests/test_handlebars.py b/tests/test_handlebars.py index 3825e74..03b3491 100644 --- a/tests/test_handlebars.py +++ b/tests/test_handlebars.py @@ -13,7 +13,6 @@ def clean_javascript(js): def test_is_supported(): - compiler = compilers.Handlebars() assert compiler.is_supported("test.hbs") @@ -54,7 +53,6 @@ def test_compile_file(monkeypatch, tmpdir): def test_sourcemap(monkeypatch, tmpdir): - monkeypatch.setattr("static_precompiler.settings.ROOT", tmpdir.strpath) compiler = compilers.Handlebars(sourcemap_enabled=False) @@ -119,7 +117,6 @@ def test_find_dependencies(): def test_get_extra_args(monkeypatch): - compiler = compilers.Handlebars(known_helpers=["foo", "bar"], namespace="baz", simple=True) assert compiler.get_extra_args() == ["-k", "foo", "-k", "bar", "-n", "baz", "-s"] diff --git a/tests/test_less.py b/tests/test_less.py index e2e0c46..e31757f 100644 --- a/tests/test_less.py +++ b/tests/test_less.py @@ -38,7 +38,6 @@ def test_compile_file(monkeypatch, tmpdir): def test_sourcemap(monkeypatch, tmpdir): - monkeypatch.setattr("static_precompiler.settings.ROOT", tmpdir.strpath) monkeypatch.setattr("static_precompiler.url_converter.convert_urls", lambda *args: None) @@ -155,7 +154,6 @@ def test_find_dependencies(monkeypatch): def test_global_vars(monkeypatch, tmpdir): - monkeypatch.setattr("static_precompiler.settings.ROOT", tmpdir.strpath) monkeypatch.setattr("static_precompiler.url_converter.convert_urls", lambda *args: None) diff --git a/tests/test_livescript.py b/tests/test_livescript.py index b4953d3..83b80dc 100644 --- a/tests/test_livescript.py +++ b/tests/test_livescript.py @@ -23,7 +23,6 @@ def test_compile_file(monkeypatch, tmpdir): def test_sourcemap(monkeypatch, tmpdir): - monkeypatch.setattr("static_precompiler.settings.ROOT", tmpdir.strpath) compiler = compilers.LiveScript(sourcemap_enabled=False) diff --git a/tests/test_management.py b/tests/test_management.py index 1487e86..2b73369 100644 --- a/tests/test_management.py +++ b/tests/test_management.py @@ -8,7 +8,6 @@ def test_get_scanned_dirs(): - assert compilestatic.get_scanned_dirs() == sorted( [ os.path.join(os.path.dirname(__file__), "compilestatic"), @@ -28,7 +27,6 @@ def test_get_scanned_dirs(): ), ) def test_compilestatic_command(verbosity, capsys, monkeypatch, tmpdir): - monkeypatch.setattr( "static_precompiler.management.commands.compilestatic.get_scanned_dirs", lambda: (os.path.join(os.path.dirname(__file__), "compilestatic"),), @@ -66,7 +64,6 @@ def test_compilestatic_command(verbosity, capsys, monkeypatch, tmpdir): @pytest.mark.django_db def test_ignore_dependencies_option(django_assert_num_queries, monkeypatch, tmpdir): - monkeypatch.setattr( "static_precompiler.management.commands.compilestatic.get_scanned_dirs", lambda: (os.path.join(os.path.dirname(__file__), "compilestatic"),), @@ -79,7 +76,6 @@ def test_ignore_dependencies_option(django_assert_num_queries, monkeypatch, tmpd @pytest.mark.django_db def test_delete_stale_files(monkeypatch, tmpdir): - output_path = os.path.join(tmpdir.strpath, static_precompiler.settings.OUTPUT_DIR) if not os.path.exists(output_path): os.makedirs(output_path) diff --git a/tests/test_registry.py b/tests/test_registry.py index 062f9c1..bc526d3 100644 --- a/tests/test_registry.py +++ b/tests/test_registry.py @@ -6,7 +6,6 @@ def test_build_compilers(monkeypatch): - monkeypatch.setattr("static_precompiler.settings.COMPILERS", ["invalid_classpath"]) with pytest.raises(django.core.exceptions.ImproperlyConfigured): registry.build_compilers() @@ -48,7 +47,6 @@ def test_build_compilers(monkeypatch): def test_get_compiler_by_name(monkeypatch): - compiler_stub = stub() monkeypatch.setattr( @@ -65,7 +63,6 @@ def test_get_compiler_by_name(monkeypatch): def test_get_compiler_by_path(monkeypatch): - coffeescript_compiler_stub = stub(is_supported=lambda source_path: source_path.endswith(".coffee")) less_compiler_stub = stub(is_supported=lambda source_path: source_path.endswith(".less")) diff --git a/tests/test_scss.py b/tests/test_scss.py index 9dea01e..f550b21 100644 --- a/tests/test_scss.py +++ b/tests/test_scss.py @@ -6,10 +6,10 @@ import pytest from static_precompiler import exceptions, utils -from static_precompiler.compilers import dart_sass, libsass, ruby_scss +from static_precompiler.compilers import dart_sass, libsass -@pytest.fixture(scope="module", params=(libsass, ruby_scss, dart_sass)) +@pytest.fixture(scope="module", params=(libsass, dart_sass)) def compiler_factory(request): compiler_module = request.param @@ -25,7 +25,6 @@ def factory(compiler_type, *args, **kwargs): def test_get_full_source_path(compiler_factory): - compiler = compiler_factory("scss") with pytest.raises(ValueError): compiler.get_full_source_path("_extra.scss") @@ -59,7 +58,6 @@ def test_compile_file(compiler_factory, monkeypatch, tmpdir): def test_sourcemap(compiler_factory, monkeypatch, tmpdir): - monkeypatch.setattr("static_precompiler.settings.ROOT", tmpdir.strpath) monkeypatch.setattr("static_precompiler.url_converter.convert_urls", lambda *args: None) @@ -84,7 +82,6 @@ def test_sourcemap(compiler_factory, monkeypatch, tmpdir): def test_compile_source(compiler_factory): - compiler = compiler_factory("scss") assert ( utils.normalize_whitespace(compiler.compile_source("p {font-size: 15px; a {color: red;}}")) @@ -128,7 +125,6 @@ def test_parse_import_string(compiler_factory): def test_strip_comments(compiler_factory): - source = """ // Single-line comment a { @@ -182,29 +178,11 @@ def test_find_imports(): @import url(foo); // `.class-name { @import "single-line-comment"; }`). @import "rounded-corners", "text-shadow"; -@import "compass"; -@import "compass.scss"; -@import "compass/css3"; @import url(http://fonts.googleapis.com/css?family=Arvo:400,700,400italic,700italic); @import url("http://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,400,700,600,300"); @import "foo,bar", url(bar,baz), 'bar,foo'; """ - expected = [ - "bar,foo", - "compass", - "compass.scss", - "compass/css3", - "foo", - "foo,bar", - "foo.scss", - "rounded-corners", - "text-shadow", - ] - compiler = ruby_scss.SCSS(compass_enabled=False) - assert compiler.find_imports(source) == expected - - compiler = ruby_scss.SCSS(compass_enabled=True) expected = [ "bar,foo", "foo", @@ -213,11 +191,11 @@ def test_find_imports(): "rounded-corners", "text-shadow", ] + compiler = dart_sass.SCSS() assert compiler.find_imports(source) == expected def test_locate_imported_file(compiler_factory, monkeypatch): - root = os.path.dirname(__file__) existing_files = set() @@ -251,7 +229,6 @@ def test_find_dependencies(compiler_factory, monkeypatch): "A.scss": "@import 'B/C.scss';", "B/C.scss": "@import '../E';", "_E.scss": "p {color: red;}", - "compass-import.scss": '@import "compass"', } monkeypatch.setattr(compiler, "get_source", lambda x: files[x]) @@ -268,72 +245,7 @@ def test_find_dependencies(compiler_factory, monkeypatch): assert compiler.find_dependencies("_E.scss") == [] -def test_compass(monkeypatch, tmpdir): - monkeypatch.setattr("static_precompiler.settings.ROOT", tmpdir.strpath) - - compiler = ruby_scss.SCSS(compass_enabled=True) - - assert compiler.compile_file("test-compass.scss") == "COMPILED/test-compass.css" - - full_output_path = compiler.get_full_output_path("test-compass.scss") - assert os.path.exists(full_output_path) - with open(full_output_path) as compiled: - assert ( - compiled.read() - == """p { - background: url('/static/images/test.png'); } -""" - ) - - -def test_compass_import(monkeypatch, tmpdir): - monkeypatch.setattr("static_precompiler.settings.ROOT", tmpdir.strpath) - - compiler = ruby_scss.SCSS(compass_enabled=True) - - assert ( - compiler.compile_file("styles/sass/test-compass-import.scss") == "COMPILED/styles/sass/test-compass-import.css" - ) - - full_output_path = compiler.get_full_output_path("styles/sass/test-compass-import.scss") - assert os.path.exists(full_output_path) - - with open(full_output_path) as compiled: - assert ( - compiled.read() - == """.round-corners { - -moz-border-radius: 4px / 4px; - -webkit-border-radius: 4px 4px; - border-radius: 4px / 4px; } -""" - ) - - compiler = ruby_scss.SCSS(compass_enabled=False) - with pytest.raises(exceptions.StaticCompilationError): - compiler.compile_file("styles/sass/test-compass-import.scss") - - -def test_ruby_get_extra_args(): - - assert ruby_scss.SCSS().get_extra_args() == [] - - assert ruby_scss.SCSS( - compass_enabled=True, load_paths=["foo", "bar"], precision=10, output_style="compact" - ).get_extra_args() == [ - "-I", - "foo", - "-I", - "bar", - "--compass", - "--precision", - "10", - "-t", - "compact", - ] - - def test_get_extra_args(): - assert dart_sass.SCSS().get_extra_args() == [] assert dart_sass.SCSS(load_paths=["foo", "bar"], output_style="compact").get_extra_args() == [ @@ -365,16 +277,14 @@ def test_load_paths(compiler_factory, monkeypatch, tmpdir, settings): assert utils.normalize_whitespace(compiled.read()) == "p { font-weight: bold; }" -@pytest.mark.parametrize("compiler_module", (libsass, ruby_scss)) @pytest.mark.parametrize("precision", (None, 10)) -def test_precision(compiler_module, precision, monkeypatch, tmpdir): - +def test_precision(precision, monkeypatch, tmpdir): expected_precision = 5 if precision is None else precision monkeypatch.setattr("static_precompiler.settings.ROOT", tmpdir.strpath) monkeypatch.setattr("static_precompiler.url_converter.convert_urls", lambda *args: None) - compiler = compiler_module.SCSS(precision=precision) + compiler = libsass.SCSS(precision=precision) compiler.compile_file("styles/sass/precision.scss") @@ -388,7 +298,6 @@ def test_precision(compiler_module, precision, monkeypatch, tmpdir): def test_output_style(compiler_factory, monkeypatch, tmpdir): - monkeypatch.setattr("static_precompiler.settings.ROOT", tmpdir.strpath) monkeypatch.setattr("static_precompiler.url_converter.convert_urls", lambda *args: None) diff --git a/tests/test_stylus.py b/tests/test_stylus.py index 00ff3a7..85b2b4f 100644 --- a/tests/test_stylus.py +++ b/tests/test_stylus.py @@ -32,7 +32,6 @@ def test_compile_file(monkeypatch, tmpdir): def test_sourcemap(monkeypatch, tmpdir): - monkeypatch.setattr("static_precompiler.settings.ROOT", tmpdir.strpath) monkeypatch.setattr("static_precompiler.url_converter.convert_urls", lambda *args: None) @@ -106,7 +105,6 @@ def test_locate_imported_file(monkeypatch): def test_find_dependencies(): - compiler = compilers.Stylus() assert compiler.find_dependencies("styles/stylus/A.styl") == [ diff --git a/tests/test_templatetags.py b/tests/test_templatetags.py index 54d38bc..2d5544f 100644 --- a/tests/test_templatetags.py +++ b/tests/test_templatetags.py @@ -3,7 +3,6 @@ def test_compile_filter(monkeypatch): - compile_static = pretend.call_recorder(lambda source_path: "compiled") monkeypatch.setattr("static_precompiler.utils.compile_static", compile_static) template = django.template.Template("""{% load compile_static %}{{ "source"|compile }}""") diff --git a/tests/test_url_converter.py b/tests/test_url_converter.py index 2adf79c..33008ce 100644 --- a/tests/test_url_converter.py +++ b/tests/test_url_converter.py @@ -2,7 +2,6 @@ def test_convert_url(): - assert url_converter.convert_url("http://dummy.jpg", "styles/") == "http://dummy.jpg" assert url_converter.convert_url("https://dummy.jpg", "styles/") == "https://dummy.jpg" assert url_converter.convert_url("/dummy.jpg", "styles/") == "/dummy.jpg" @@ -13,7 +12,6 @@ def test_convert_url(): def test_convert(): - assert ( url_converter.convert("p {\n background: url(../images/ham.jpg) no-repeat 0 0;\n}", "styles/") == "p {\n background: url(/static/images/ham.jpg) no-repeat 0 0;\n}" diff --git a/tests/test_utils.py b/tests/test_utils.py index 1176f8b..1803287 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -17,7 +17,6 @@ def test_write_read_file(tmpdir, settings): def test_compile_static(monkeypatch): - compiler_stub = stub(compile=lambda x: "compiled", compile_lazy=lambda x: "compiled lazy") monkeypatch.setattr("static_precompiler.registry.get_compiler_by_path", lambda path: compiler_stub) diff --git a/tox.ini b/tox.ini index ad33e3b..7de0a1c 100644 --- a/tox.ini +++ b/tox.ini @@ -1,20 +1,17 @@ [tox] isolated_build = True envlist = - py36-django22, py38-django32, - py39-django40 - py310-django41 - py311-django41 - + py39-django42 + py310-django42 + py311-django42 + py312-django42 [testenv] passenv = GEM_PATH deps = -rrequirements-test.txt - django22: Django>=2.2,<3.0 django32: Django>=3.2,<4 - django40: Django>=4.0,<4.1 - django41: Django>=4.1,<4.2 + django42: Django>=4.2,<4.3 commands = pytest --cov static_precompiler --cov-report xml --cov-append setenv = PYTHONPATH = {toxinidir}