Import upstream version 1.1.0
Debian Janitor
2 years ago
0 | # EditorConfig is awesome: https://EditorConfig.org | |
1 | ||
2 | root = true | |
3 | ||
4 | [*] | |
5 | charset = utf-8 | |
6 | end_of_line = lf | |
7 | indent_style = space | |
8 | insert_final_newline = true | |
9 | max_line_length = 80 | |
10 | trim_trailing_whitespace = true | |
11 | ||
12 | [*.{html,css,js,json,xml,yaml,yml}] | |
13 | indent_size = 2 | |
14 | ||
15 | [*.{md,ps1,sh,py,rst}] | |
16 | indent_size = 4 |
11 | 11 | |
12 | 12 | | | | |
13 | 13 | |------------------------|----------------------| |
14 | | Python Package |[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/django-sass)](https://pypi.org/project/django-sass/) [![PyPI - Django Version](https://img.shields.io/pypi/djversions/django-sass)](https://pypi.org/project/django-sass/) [![PyPI - Wheel](https://img.shields.io/pypi/wheel/django-sass)](https://pypi.org/project/django-sass/) [![PyPI - Downloads](https://img.shields.io/pypi/dm/django-sass)](https://pypi.org/project/django-sass/) [![PyPI](https://img.shields.io/pypi/v/django-sass)](https://pypi.org/project/django-sass/) | | |
15 | | Build | [![Build Status](https://dev.azure.com/coderedcorp/coderedcms/_apis/build/status/django-sass?branchName=master)](https://dev.azure.com/coderedcorp/coderedcms/_build/latest?definitionId=10&branchName=master) [![Azure DevOps tests (branch)](https://img.shields.io/azure-devops/tests/coderedcorp/coderedcms/10/master)](https://dev.azure.com/coderedcorp/coderedcms/_build/latest?definitionId=10&branchName=master) [![Azure DevOps coverage (branch)](https://img.shields.io/azure-devops/coverage/coderedcorp/coderedcms/10/master)](https://dev.azure.com/coderedcorp/coderedcms/_build/latest?definitionId=10&branchName=master) | | |
14 | | Python Package | [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/django-sass)](https://pypi.org/project/django-sass/) [![PyPI - Django Version](https://img.shields.io/pypi/djversions/django-sass)](https://pypi.org/project/django-sass/) [![PyPI - Wheel](https://img.shields.io/pypi/wheel/django-sass)](https://pypi.org/project/django-sass/) [![PyPI - Downloads](https://img.shields.io/pypi/dm/django-sass)](https://pypi.org/project/django-sass/) [![PyPI](https://img.shields.io/pypi/v/django-sass)](https://pypi.org/project/django-sass/) | | |
15 | | Build | [![Build Status](https://dev.azure.com/coderedcorp/cr-github/_apis/build/status/django-sass?branchName=main)](https://dev.azure.com/coderedcorp/cr-github/_build/latest?definitionId=10&branchName=main) [![Azure DevOps tests (branch)](https://img.shields.io/azure-devops/tests/coderedcorp/cr-github/10/main)](https://dev.azure.com/coderedcorp/cr-github/_build/latest?definitionId=10&branchName=main) [![Azure DevOps coverage (branch)](https://img.shields.io/azure-devops/coverage/coderedcorp/cr-github/10/main)](https://dev.azure.com/coderedcorp/cr-github/_build/latest?definitionId=10&branchName=main) | | |
16 | 16 | |
17 | 17 | |
18 | 18 | Installation |
92 | 92 | |
93 | 93 | ✨✨ **Congratulations, you are now a Django + Sass developer!** ✨✨ |
94 | 94 | |
95 | Now you can commit those CSS files to version control, or run `collectstatic` and deploy them as normal. | |
95 | Now you can commit those CSS files to version control, or run `collectstatic` | |
96 | and deploy them as normal. | |
96 | 97 | |
97 | 98 | For an example project layout, see `testproject/` in this repository. |
98 | 99 | |
100 | 101 | Watch Mode |
101 | 102 | ---------- |
102 | 103 | |
103 | To have `django-sass` watch files and recompile them as they change (useful in development), | |
104 | add the ``--watch`` flag. | |
104 | To have `django-sass` watch files and recompile them as they change (useful in | |
105 | development), add the ``--watch`` flag. | |
105 | 106 | |
106 | 107 | ``` |
107 | 108 | python manage.py sass app2/static/app2/scss/ app2/static/app2/css/ --watch |
132 | 133 | Limitations |
133 | 134 | ----------- |
134 | 135 | |
135 | * `@import` statements must reference a path relative to a path in `STATICFILES_FINDERS` | |
136 | (which will usually be an app's `static/` directory or some other directory specified | |
137 | in `STATICFILES_DIRS`). Or they can reference a relative path equal to or below the | |
138 | current file. It does not support traversing up the filesystem (i.e. `../`). | |
136 | * `@import` statements must reference a path relative to a path in | |
137 | `STATICFILES_FINDERS` (which will usually be an app's `static/` directory or | |
138 | some other directory specified in `STATICFILES_DIRS`). Or they can reference a | |
139 | relative path equal to or below the current file. It does not support | |
140 | traversing up the filesystem (i.e. `../`). | |
139 | 141 | |
140 | 142 | Legal imports: |
141 | 143 | ```scss |
148 | 150 | @import '../file'; |
149 | 151 | ``` |
150 | 152 | |
151 | * Only files ending in `.scss` are supported for now. | |
152 | ||
153 | * Only supports `-g`, `-p`, and `-t` options similar to `pysassc`. Ideally `django-sass` will | |
154 | be as similar as possible to the `pysassc` command line interface. | |
155 | ||
156 | Feel free to file an issue or make a pull request to improve any of these limitations. 🐱💻 | |
153 | * Only supports `-g`, `-p`, and `-t` options similar to `pysassc`. Ideally | |
154 | `django-sass` will be as similar as possible to the `pysassc` command line | |
155 | interface. | |
156 | ||
157 | Feel free to file an issue or make a pull request to improve any of these | |
158 | limitations. 🐱💻 | |
157 | 159 | |
158 | 160 | |
159 | 161 | Why django-sass? |
160 | 162 | ---------------- |
161 | 163 | |
162 | Other packages such as [django-libsass](https://github.com/torchbox/django-libsass) | |
163 | and [django-sass-processor](https://github.com/jrief/django-sass-processor), | |
164 | while nice packages, require `django-compressor` which itself depends on several | |
165 | other packages that require compilation to install. | |
166 | ||
167 | * If you simply want to use Sass in development without installing a web of unwanted | |
168 | dependencies, then `django-sass` is for you. | |
169 | * If you don't want to deploy any processors or compressors to your production server, | |
170 | then `django-sass` is for you. | |
164 | Other packages such as | |
165 | [django-libsass](https://github.com/torchbox/django-libsass) and | |
166 | [django-sass-processor](https://github.com/jrief/django-sass-processor), while | |
167 | nice packages, require `django-compressor` which itself depends on several other | |
168 | packages that require compilation to install. | |
169 | ||
170 | Installing `django-compressor` in your production web server requires a LOT of | |
171 | extra bloat including a C compiler. It then will compile the Sass on-the-fly | |
172 | while rendering the HTML templates. This is a wasteful use of CPU on your web | |
173 | server. | |
174 | ||
175 | Instead, `django-sass` lets you compile the Sass locally on your machine | |
176 | *before* deploying, to reduce dependencies and CPU time on your production web | |
177 | server. This helps keep things fast and simple. | |
178 | ||
179 | * If you simply want to use Sass in development without installing a web of | |
180 | unwanted dependencies, then `django-sass` is for you. | |
181 | * If you don't want to deploy any processors or compressors to your production | |
182 | server, then `django-sass` is for you. | |
171 | 183 | * If you don't want to change the way you reference and serve static files, |
172 | 184 | then `django-sass` is for you. |
173 | * And if you want the absolute simplest installation and setup possible for doing Sass, | |
174 | `django-sass` is for you too. | |
175 | ||
176 | django-sass only depends on libsass (which provides pre-built wheels for Windows, Mac, | |
177 | and Linux), and of course Django (any version). | |
185 | * And if you want the absolute simplest installation and setup possible for | |
186 | doing Sass, `django-sass` is for you too. | |
187 | ||
188 | django-sass only depends on libsass (which provides pre-built wheels for | |
189 | Windows, Mac, and Linux), and of course Django (any version). | |
178 | 190 | |
179 | 191 | |
180 | 192 | Programmatically Compiling Sass |
232 | 244 | Before committing, run static analysis tools: |
233 | 245 | |
234 | 246 | ``` |
247 | (myvenv)$ black . | |
235 | 248 | (myvenv)$ flake8 |
236 | 249 | (myvenv)$ mypy |
237 | 250 | ``` |
245 | 258 | |
246 | 259 | Changelog |
247 | 260 | --------- |
261 | ||
262 | #### 1.1.0 | |
263 | * New: Now compiles `.sass` files as well as `.scss` files. | |
264 | * Fix bug when input path is a file and output path does not exist. | |
265 | ||
266 | #### 1.0.1 | |
267 | * Maintanence release, no functional changes. | |
268 | * Add additional type hints within the codebase. | |
269 | * Tested against Django 3.1 | |
270 | * Formatted code with `black`. | |
248 | 271 | |
249 | 272 | #### 1.0.0 |
250 | 273 | * New: You can now use `django_sass` APIs directly in Python. |
252 | 275 | * Code quality improvements. |
253 | 276 | |
254 | 277 | #### 0.2.0 |
255 | * New feature: `-g` option to build a source map (when input is a file, not a directory). | |
278 | * New feature: `-g` option to build a source map (when input is a file, not a | |
279 | directory). | |
256 | 280 | |
257 | 281 | #### 0.1.2 |
258 | 282 | * Fix: Write compiled CSS files as UTF-8. |
259 | * Change: Default `-p` precision from 5 to 8 for better support building Bootstrap CSS. | |
283 | * Change: Default `-p` precision from 5 to 8 for better support building | |
284 | Bootstrap CSS. | |
260 | 285 | |
261 | 286 | #### 0.1.1 |
262 | 287 | * Fix: Create full file path if not exists when specifying a file output. |
14 | 14 | |
15 | 15 | |
16 | 16 | trigger: |
17 | - master | |
17 | - main | |
18 | 18 | |
19 | 19 | |
20 | 20 | stages: |
28 | 28 | vmImage: 'ubuntu-latest' |
29 | 29 | strategy: |
30 | 30 | matrix: |
31 | py3.5: | |
32 | PYTHON_VERSION: '3.5' | |
33 | 31 | py3.6: |
34 | 32 | PYTHON_VERSION: '3.6' |
35 | 33 | py3.7: |
36 | 34 | PYTHON_VERSION: '3.7' |
37 | 35 | py3.8: |
38 | 36 | PYTHON_VERSION: '3.8' |
37 | py3.9: | |
38 | PYTHON_VERSION: '3.9' | |
39 | 39 | |
40 | 40 | steps: |
41 | 41 | - task: UsePythonVersion@0 |
79 | 79 | - task: UsePythonVersion@0 |
80 | 80 | displayName: 'Use Python version' |
81 | 81 | inputs: |
82 | versionSpec: '3.8' | |
82 | versionSpec: '3.9' | |
83 | 83 | architecture: 'x64' |
84 | 84 | |
85 | 85 | - script: python -m pip install -r requirements-dev.txt |
87 | 87 | |
88 | 88 | - script: flake8 . |
89 | 89 | displayName: 'CR-QC: Static analysis (flake8)' |
90 | ||
91 | - script: black --check . | |
92 | displayName: 'CR-QC: Format check' | |
90 | 93 | |
91 | 94 | - script: mypy . |
92 | 95 | displayName: 'CR-QC: Type check (mypy)' |
1 | 1 | |
2 | 2 | <# |
3 | 3 | .SYNOPSIS |
4 | Compares code coverage percent of local coverage.xml file to master branch | |
4 | Compares code coverage percent of local coverage.xml file to main branch | |
5 | 5 | (Azure Pipeline API). |
6 | 6 | |
7 | 7 | .PARAMETER wd |
25 | 25 | param( |
26 | 26 | [string] $wd = (Get-Item (Split-Path $PSCommandPath -Parent)).Parent, |
27 | 27 | [string] $org = "coderedcorp", |
28 | [string] $project = "coderedcms", | |
28 | [string] $project = "cr-github", | |
29 | 29 | [string] $pipeline_name = "django-sass" |
30 | 30 | ) |
31 | 31 | |
40 | 40 | |
41 | 41 | |
42 | 42 | # Get list of all recent builds. |
43 | $masterBuildJson = ( | |
44 | Invoke-WebRequest "$ApiBase/_apis/build/builds?branchName=refs/heads/master&api-version=5.1" | |
43 | $mainBuildJson = ( | |
44 | Invoke-WebRequest "$ApiBase/_apis/build/builds?branchName=refs/heads/main&api-version=5.1" | |
45 | 45 | ).Content | ConvertFrom-Json |
46 | 46 | |
47 | 47 | # Get the latest matching build ID from the list of builds. |
48 | foreach ($build in $masterBuildJson.value) { | |
48 | foreach ($build in $mainBuildJson.value) { | |
49 | 49 | if ($build.definition.name -eq $pipeline_name) { |
50 | $masterLatestId = $build.id | |
50 | $mainLatestId = $build.id | |
51 | 51 | break |
52 | 52 | } |
53 | 53 | } |
54 | 54 | |
55 | 55 | # Retrieve code coverage for this build ID. |
56 | $masterCoverageJson = ( | |
57 | Invoke-WebRequest "$ApiBase/_apis/test/codecoverage?buildId=$masterLatestId&api-version=5.1-preview.1" | |
56 | $mainCoverageJson = ( | |
57 | Invoke-WebRequest "$ApiBase/_apis/test/codecoverage?buildId=$mainLatestId&api-version=5.1-preview.1" | |
58 | 58 | ).Content | ConvertFrom-Json |
59 | foreach ($cov in $masterCoverageJson.coverageData.coverageStats) { | |
59 | foreach ($cov in $mainCoverageJson.coverageData.coverageStats) { | |
60 | 60 | if ($cov.label -eq "Lines") { |
61 | $masterlinerate = [math]::Round(($cov.covered / $cov.total) * 100, 2) | |
61 | $mainlinerate = [math]::Round(($cov.covered / $cov.total) * 100, 2) | |
62 | 62 | } |
63 | 63 | } |
64 | 64 | |
83 | 83 | |
84 | 84 | |
85 | 85 | Write-Output "" |
86 | Write-Output "Master line coverage rate: $masterlinerate%" | |
87 | Write-Output "Branch line coverage rate: $branchlinerate%" | |
86 | Write-Output "Main coverage rate: $mainlinerate%" | |
87 | Write-Output "Branch coverage rate: $branchlinerate%" | |
88 | 88 | |
89 | if ($masterlinerate -eq 0) { | |
89 | if ($mainlinerate -eq 0) { | |
90 | 90 | $change = "Infinite" |
91 | 91 | } |
92 | 92 | else { |
93 | $change = [math]::Abs($branchlinerate - $masterlinerate) | |
93 | $change = [math]::Abs($branchlinerate - $mainlinerate) | |
94 | 94 | } |
95 | 95 | |
96 | if ($branchlinerate -gt $masterlinerate) { | |
96 | if ($branchlinerate -gt $mainlinerate) { | |
97 | 97 | Write-Host "Coverage increased by $change% 😀" -ForegroundColor Green |
98 | 98 | exit 0 |
99 | 99 | } |
100 | elseif ($branchlinerate -eq $masterlinerate) { | |
100 | elseif ($branchlinerate -eq $mainlinerate) { | |
101 | 101 | Write-Host "Coverage has not changed." -ForegroundColor Green |
102 | 102 | exit 0 |
103 | 103 | } |
23 | 23 | |
24 | 24 | def find_static_scss() -> List[str]: |
25 | 25 | """ |
26 | Finds all static scss files available in this Django project. | |
26 | Finds all static scss/sass files available in this Django project. | |
27 | 27 | |
28 | 28 | :returns: |
29 | List of paths of static scss files. | |
29 | List of paths of static scss/sass files. | |
30 | 30 | """ |
31 | 31 | scss_files = [] |
32 | 32 | for finder in get_finders(): |
33 | 33 | for path, storage in finder.list([]): |
34 | if path.endswith(".scss"): | |
34 | if path.endswith(".scss") or path.endswith(".sass"): | |
35 | 35 | fullpath = finder.find(path) |
36 | 36 | scss_files.append(fullpath) |
37 | 37 | return scss_files |
38 | 38 | |
39 | 39 | |
40 | def compile_sass(inpath: str, outpath: str, output_style: str = None, precision: int = None, | |
41 | source_map: bool = False, include_paths: List[str] = None) -> None: | |
40 | def compile_sass( | |
41 | inpath: str, | |
42 | outpath: str, | |
43 | output_style: str = None, | |
44 | precision: int = None, | |
45 | source_map: bool = False, | |
46 | include_paths: List[str] = None, | |
47 | ) -> None: | |
42 | 48 | """ |
43 | 49 | Calls sass.compile() within context of Django's known static file paths, |
44 | 50 | and writes output CSS and/or sourcemaps to file. |
45 | 51 | |
46 | 52 | :param str inpath: |
47 | Path to SCSS file or directory of SCSS files. | |
53 | Path to SCSS/Sass file or directory of SCSS/Sass files. | |
48 | 54 | :param str outpath: |
49 | 55 | Path to a CSS file or directory in which to write output. The path will |
50 | 56 | be created if it does not exist. |
80 | 86 | # Handle input files. |
81 | 87 | outfile = None |
82 | 88 | if os.path.isfile(inpath): |
89 | ||
83 | 90 | sassargs.update({"filename": inpath}) |
84 | if os.path.isdir(outpath): | |
91 | ||
92 | # If outpath does not exist, guess if it should be a dir and create it. | |
93 | if not os.path.exists(outpath): | |
94 | if not outpath.endswith(".css"): | |
95 | os.makedirs(outpath) | |
96 | ||
97 | # If outpath is a directory, create a child file. | |
98 | # Otherwise use provided file path. | |
99 | if os.path.exists(outpath) and os.path.isdir(outpath): | |
85 | 100 | outfile = os.path.join( |
86 | outpath, os.path.basename(inpath.replace(".scss", ".css")) | |
101 | outpath, | |
102 | os.path.basename( | |
103 | inpath.replace(".scss", ".css").replace(".sass", ".css") | |
104 | ), | |
87 | 105 | ) |
88 | 106 | else: |
89 | 107 | outfile = outpath |
108 | ||
109 | # Create source map if specified. | |
90 | 110 | if source_map: |
91 | 111 | sassargs.update({"source_map_filename": outfile + ".map"}) |
92 | 112 |
0 | from typing import Dict | |
0 | 1 | import os |
1 | 2 | import sys |
2 | 3 | import time |
70 | 71 | self.stdout.write("Watching...") |
71 | 72 | |
72 | 73 | # Track list of files to watch and their modified time. |
73 | watchfiles = {} | |
74 | watchfiles = {} # type: Dict[str, float] | |
74 | 75 | while True: |
75 | 76 | needs_updated = False |
76 | 77 | |
93 | 94 | precision=o_precision, |
94 | 95 | source_map=o_srcmap, |
95 | 96 | ) |
96 | self.stdout.write("Updated files at %s" % time.time()) | |
97 | self.stdout.write( | |
98 | "Updated files at %s" % time.time() | |
99 | ) | |
97 | 100 | except sass.CompileError as exc: |
98 | 101 | self.stdout.write(str(exc)) |
99 | 102 |
0 | [tool.black] | |
1 | line-length = 80 | |
2 | target-version = ['py36', 'py37', 'py38'] | |
3 | # Regular expression of files to exclude. | |
4 | exclude = ''' | |
5 | /( | |
6 | .venv | |
7 | | venv | |
8 | | migrations | |
9 | )/ | |
10 | ''' |
0 | 0 | [flake8] |
1 | 1 | max-line-length = 100 |
2 | exclude = migrations | |
2 | exclude = .venv,venv,migrations | |
3 | 3 | |
4 | 4 | [mypy] |
5 | 5 | ignore_missing_imports = True |
6 | namespace_packages = True | |
6 | 7 | |
7 | 8 | [tool:pytest] |
8 | 9 | DJANGO_SETTINGS_MODULE = testproject.settings |
1 | 1 | from setuptools import setup, find_packages |
2 | 2 | |
3 | 3 | |
4 | with open(os.path.join(os.path.dirname(__file__), "README.md"), encoding="utf8") as readme: | |
4 | with open( | |
5 | os.path.join(os.path.dirname(__file__), "README.md"), encoding="utf8" | |
6 | ) as readme: | |
5 | 7 | README = readme.read() |
6 | 8 | |
7 | 9 | setup( |
8 | 10 | name="django-sass", |
9 | version="1.0.0", | |
11 | version="1.1.0", | |
10 | 12 | author="CodeRed LLC", |
11 | 13 | author_email="info@coderedcorp.com", |
12 | 14 | url="https://github.com/coderedcorp/django-sass", |
13 | description=("The absolute simplest way to use Sass with Django. Pure Python, " | |
14 | "minimal dependencies, and no special configuration required!"), | |
15 | description=( | |
16 | "The absolute simplest way to use Sass with Django. Pure Python, " | |
17 | "minimal dependencies, and no special configuration required!" | |
18 | ), | |
15 | 19 | long_description=README, |
16 | 20 | long_description_content_type="text/markdown", |
17 | 21 | license="BSD license", |
19 | 23 | packages=find_packages(), |
20 | 24 | install_requires=[ |
21 | 25 | "django", |
22 | "libsass" | |
26 | "libsass", | |
23 | 27 | ], |
24 | 28 | classifiers=[ |
25 | "Intended Audience :: Developers", | |
26 | "License :: OSI Approved :: BSD License", | |
27 | "Natural Language :: English", | |
28 | "Programming Language :: Python :: 3", | |
29 | "Programming Language :: Python :: 3 :: Only", | |
30 | "Framework :: Django", | |
29 | "Environment :: Web Environment", | |
31 | 30 | "Framework :: Django :: 2.0", |
32 | 31 | "Framework :: Django :: 2.1", |
33 | 32 | "Framework :: Django :: 2.2", |
34 | 33 | "Framework :: Django :: 3.0", |
35 | "Environment :: Web Environment", | |
34 | "Framework :: Django :: 3.1", | |
35 | "Framework :: Django", | |
36 | "Intended Audience :: Developers", | |
37 | "License :: OSI Approved :: BSD License", | |
38 | "Natural Language :: English", | |
39 | "Programming Language :: Python :: 3 :: Only", | |
40 | "Programming Language :: Python :: 3", | |
41 | "Programming Language :: Python :: 3.6", | |
42 | "Programming Language :: Python :: 3.7", | |
43 | "Programming Language :: Python :: 3.8", | |
44 | "Programming Language :: Python :: 3.9", | |
36 | 45 | ], |
37 | 46 | ) |
4 | 4 | |
5 | 5 | |
6 | 6 | def main(): |
7 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testproject.settings') | |
7 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "testproject.settings") | |
8 | 8 | try: |
9 | 9 | from django.core.management import execute_from_command_line |
10 | 10 | except ImportError as exc: |
16 | 16 | execute_from_command_line(sys.argv) |
17 | 17 | |
18 | 18 | |
19 | if __name__ == '__main__': | |
19 | if __name__ == "__main__": | |
20 | 20 | main() |
10 | 10 | """ |
11 | 11 | |
12 | 12 | import os |
13 | from typing import List | |
13 | 14 | |
14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) |
15 | 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
19 | 20 | # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ |
20 | 21 | |
21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! |
22 | SECRET_KEY = '-_wl=tq26(*wyvfza+ncg_436c53pu81d=07j62+vm5y2pc)f^' | |
23 | SECRET_KEY = "-_wl=tq26(*wyvfza+ncg_436c53pu81d=07j62+vm5y2pc)f^" | |
23 | 24 | |
24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! |
25 | 26 | DEBUG = True |
26 | 27 | |
27 | ALLOWED_HOSTS = [] | |
28 | ALLOWED_HOSTS = [] # type: List[str] | |
28 | 29 | |
29 | 30 | |
30 | 31 | # Application definition |
31 | 32 | |
32 | 33 | INSTALLED_APPS = [ |
33 | 'app1', | |
34 | 'app2', | |
35 | 'django_sass', | |
36 | ||
37 | 'django.contrib.admin', | |
38 | 'django.contrib.auth', | |
39 | 'django.contrib.contenttypes', | |
40 | 'django.contrib.sessions', | |
41 | 'django.contrib.messages', | |
42 | 'django.contrib.staticfiles', | |
34 | "app1", | |
35 | "app2", | |
36 | "app3", | |
37 | "django_sass", | |
38 | "django.contrib.admin", | |
39 | "django.contrib.auth", | |
40 | "django.contrib.contenttypes", | |
41 | "django.contrib.sessions", | |
42 | "django.contrib.messages", | |
43 | "django.contrib.staticfiles", | |
43 | 44 | ] |
44 | 45 | |
45 | 46 | MIDDLEWARE = [ |
46 | 'django.middleware.security.SecurityMiddleware', | |
47 | 'django.contrib.sessions.middleware.SessionMiddleware', | |
48 | 'django.middleware.common.CommonMiddleware', | |
49 | 'django.middleware.csrf.CsrfViewMiddleware', | |
50 | 'django.contrib.auth.middleware.AuthenticationMiddleware', | |
51 | 'django.contrib.messages.middleware.MessageMiddleware', | |
52 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', | |
47 | "django.middleware.security.SecurityMiddleware", | |
48 | "django.contrib.sessions.middleware.SessionMiddleware", | |
49 | "django.middleware.common.CommonMiddleware", | |
50 | "django.middleware.csrf.CsrfViewMiddleware", | |
51 | "django.contrib.auth.middleware.AuthenticationMiddleware", | |
52 | "django.contrib.messages.middleware.MessageMiddleware", | |
53 | "django.middleware.clickjacking.XFrameOptionsMiddleware", | |
53 | 54 | ] |
54 | 55 | |
55 | ROOT_URLCONF = 'testproject.urls' | |
56 | ROOT_URLCONF = "testproject.urls" | |
56 | 57 | |
57 | 58 | TEMPLATES = [ |
58 | 59 | { |
59 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', | |
60 | 'DIRS': [], | |
61 | 'APP_DIRS': True, | |
62 | 'OPTIONS': { | |
63 | 'context_processors': [ | |
64 | 'django.template.context_processors.debug', | |
65 | 'django.template.context_processors.request', | |
66 | 'django.contrib.auth.context_processors.auth', | |
67 | 'django.contrib.messages.context_processors.messages', | |
60 | "BACKEND": "django.template.backends.django.DjangoTemplates", | |
61 | "DIRS": [], | |
62 | "APP_DIRS": True, | |
63 | "OPTIONS": { | |
64 | "context_processors": [ | |
65 | "django.template.context_processors.debug", | |
66 | "django.template.context_processors.request", | |
67 | "django.contrib.auth.context_processors.auth", | |
68 | "django.contrib.messages.context_processors.messages", | |
68 | 69 | ], |
69 | 70 | }, |
70 | 71 | }, |
71 | 72 | ] |
72 | 73 | |
73 | WSGI_APPLICATION = 'testproject.wsgi.application' | |
74 | WSGI_APPLICATION = "testproject.wsgi.application" | |
74 | 75 | |
75 | 76 | |
76 | 77 | # Database |
77 | 78 | # https://docs.djangoproject.com/en/2.2/ref/settings/#databases |
78 | 79 | |
79 | 80 | DATABASES = { |
80 | 'default': { | |
81 | 'ENGINE': 'django.db.backends.sqlite3', | |
82 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), | |
81 | "default": { | |
82 | "ENGINE": "django.db.backends.sqlite3", | |
83 | "NAME": os.path.join(BASE_DIR, "db.sqlite3"), | |
83 | 84 | } |
84 | 85 | } |
85 | 86 | |
87 | 88 | # Internationalization |
88 | 89 | # https://docs.djangoproject.com/en/2.2/topics/i18n/ |
89 | 90 | |
90 | LANGUAGE_CODE = 'en-us' | |
91 | LANGUAGE_CODE = "en-us" | |
91 | 92 | |
92 | TIME_ZONE = 'UTC' | |
93 | TIME_ZONE = "UTC" | |
93 | 94 | |
94 | 95 | USE_I18N = True |
95 | 96 | |
101 | 102 | # Static files (CSS, JavaScript, Images) |
102 | 103 | # https://docs.djangoproject.com/en/2.2/howto/static-files/ |
103 | 104 | |
104 | STATIC_URL = '/static/' | |
105 | STATIC_URL = "/static/" |
16 | 16 | from django.urls import path |
17 | 17 | |
18 | 18 | urlpatterns = [ |
19 | path('admin/', admin.site.urls), | |
19 | path("admin/", admin.site.urls), | |
20 | 20 | ] |
10 | 10 | |
11 | 11 | from django.core.wsgi import get_wsgi_application |
12 | 12 | |
13 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testproject.settings') | |
13 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "testproject.settings") | |
14 | 14 | |
15 | 15 | application = get_wsgi_application() |
2 | 2 | import subprocess |
3 | 3 | import time |
4 | 4 | import unittest |
5 | from typing import List | |
5 | 6 | |
6 | 7 | from django_sass import find_static_paths, find_static_scss |
7 | 8 | |
8 | 9 | |
9 | 10 | THIS_DIR = os.path.dirname(os.path.abspath(__file__)) |
10 | 11 | |
12 | SCSS_CONTAINS = [ | |
13 | "/* Tests: app1/scss/_include.scss */", | |
14 | "/* Tests: app2/scss/_samedir.scss */", | |
15 | "/* Tests: app2/scss/subdir/_subdir.scss */", | |
16 | "/* Tests: app2/scss/test.scss */", | |
17 | ] | |
18 | ||
11 | 19 | |
12 | 20 | class TestDjangoSass(unittest.TestCase): |
13 | ||
14 | 21 | def setUp(self): |
15 | 22 | self.outdir = os.path.join(THIS_DIR, "out") |
16 | 23 | |
18 | 25 | # Clean up output files |
19 | 26 | shutil.rmtree(self.outdir, ignore_errors=True) |
20 | 27 | |
21 | def assert_output(self, real_outpath: str): | |
28 | def assert_output( | |
29 | self, | |
30 | inpath: str, | |
31 | outpath: str, | |
32 | real_outpath: str, | |
33 | contains: List[str], | |
34 | args: List[str] = None, | |
35 | ): | |
36 | # Command to run | |
37 | args = args or [] | |
38 | cmd = ["python", "manage.py", "sass", *args, inpath, outpath] | |
39 | # Run the management command on testproject. | |
40 | proc = subprocess.run(cmd, cwd=THIS_DIR) | |
41 | # Verify the process exited cleanly. | |
42 | self.assertEqual(proc.returncode, 0) | |
22 | 43 | # Verify that the output file exists. |
23 | print(real_outpath) | |
24 | self.assertTrue(os.path.isfile(real_outpath)) | |
44 | # self.assertTrue(os.path.isfile(real_outpath)) | |
25 | 45 | |
26 | 46 | # Verify that the file contains expected output from all sass files. |
27 | 47 | with open(real_outpath, encoding="utf8") as f: |
28 | 48 | contents = f.read() |
29 | self.assertTrue("/* Tests: app1/scss/_include.scss */" in contents) | |
30 | self.assertTrue("/* Tests: app2/scss/_samedir.scss */" in contents) | |
31 | self.assertTrue("/* Tests: app2/scss/subdir/_subdir.scss */" in contents) | |
32 | self.assertTrue("/* Tests: app2/scss/test.scss */" in contents) | |
49 | for compiled_data in contains: | |
50 | self.assertTrue(compiled_data in contents) | |
33 | 51 | |
34 | 52 | def test_find_static_paths(self): |
35 | 53 | paths = find_static_paths() |
41 | 59 | files = find_static_scss() |
42 | 60 | # Assert that it found all of our scss files. |
43 | 61 | self.assertTrue( |
44 | os.path.join(THIS_DIR, "app1", "static", "app1", "scss", "_include.scss") in files) | |
62 | os.path.join( | |
63 | THIS_DIR, "app1", "static", "app1", "scss", "_include.scss" | |
64 | ) | |
65 | in files | |
66 | ) | |
45 | 67 | self.assertTrue( |
46 | os.path.join(THIS_DIR, "app2", "static", "app2", "scss", "_samedir.scss") in files) | |
68 | os.path.join( | |
69 | THIS_DIR, "app2", "static", "app2", "scss", "_samedir.scss" | |
70 | ) | |
71 | in files | |
72 | ) | |
47 | 73 | self.assertTrue( |
48 | os.path.join(THIS_DIR, "app2", "static", "app2", "scss", "test.scss") in files) | |
74 | os.path.join( | |
75 | THIS_DIR, "app2", "static", "app2", "scss", "test.scss" | |
76 | ) | |
77 | in files | |
78 | ) | |
49 | 79 | self.assertTrue( |
50 | os.path.join(THIS_DIR, "app2", "static", "app2", "scss", "subdir", "_subdir.scss") | |
51 | in files) | |
80 | os.path.join( | |
81 | THIS_DIR, | |
82 | "app2", | |
83 | "static", | |
84 | "app2", | |
85 | "scss", | |
86 | "subdir", | |
87 | "_subdir.scss", | |
88 | ) | |
89 | in files | |
90 | ) | |
91 | self.assertTrue( | |
92 | os.path.join( | |
93 | THIS_DIR, "app3", "static", "app3", "sass", "indent_test.sass" | |
94 | ) | |
95 | in files | |
96 | ) | |
52 | 97 | |
53 | 98 | def test_cli(self): |
54 | 99 | # Input and output paths relative to django static dirs. |
55 | 100 | inpath = os.path.join("app2", "static", "app2", "scss", "test.scss") |
56 | 101 | outpath = os.path.join(self.outdir, "test_file.css") |
57 | # Command to run | |
58 | cmd = ["python", "manage.py", "sass", inpath, outpath] | |
59 | # Run the management command on testproject. | |
60 | proc = subprocess.run(cmd, cwd=THIS_DIR) | |
61 | # Verify the process exited cleanly. | |
62 | self.assertEqual(proc.returncode, 0) | |
63 | # Assert output is correct. | |
64 | self.assert_output(outpath) | |
102 | self.assert_output( | |
103 | inpath=inpath, | |
104 | outpath=outpath, | |
105 | real_outpath=outpath, | |
106 | contains=SCSS_CONTAINS, | |
107 | ) | |
65 | 108 | |
66 | 109 | def test_cli_dir(self): |
67 | 110 | # Input and output paths relative to django static dirs. |
68 | 111 | inpath = os.path.join("app2", "static", "app2", "scss") |
69 | outpath = self.outdir | |
70 | 112 | # Expected output path on filesystem. |
71 | 113 | real_outpath = os.path.join(self.outdir, "test.css") |
72 | # Command to run | |
73 | cmd = ["python", "manage.py", "sass", inpath, outpath] | |
74 | # Run the management command on testproject. | |
75 | proc = subprocess.run(cmd, cwd=THIS_DIR) | |
76 | # Verify the process exited cleanly. | |
77 | self.assertEqual(proc.returncode, 0) | |
78 | # Assert output is correct. | |
79 | self.assert_output(real_outpath) | |
114 | self.assert_output( | |
115 | inpath=inpath, | |
116 | outpath=self.outdir, | |
117 | real_outpath=real_outpath, | |
118 | contains=SCSS_CONTAINS, | |
119 | ) | |
120 | ||
121 | def test_cli_infile_outdir(self): | |
122 | # Input is a file; output is non-existant path (without .css extension). | |
123 | inpath = os.path.join("app2", "static", "app2", "scss", "test.scss") | |
124 | outpath = os.path.join(self.outdir, "does-not-exist") | |
125 | # Expected output path on filesystem. | |
126 | real_outpath = os.path.join(outpath, "test.css") | |
127 | self.assert_output( | |
128 | inpath=inpath, | |
129 | outpath=outpath, | |
130 | real_outpath=real_outpath, | |
131 | contains=SCSS_CONTAINS, | |
132 | ) | |
133 | ||
134 | def test_sass_compiles(self): | |
135 | # Input and output paths relative to django static dirs. | |
136 | inpath = os.path.join("app3", "static", "app3", "sass") | |
137 | # Expected output path on filesystem. | |
138 | real_outpath = os.path.join(self.outdir, "indent_test.css") | |
139 | self.assert_output( | |
140 | inpath=inpath, | |
141 | outpath=self.outdir, | |
142 | real_outpath=real_outpath, | |
143 | contains=["/* Tests: app3/sass/indent_test.sass */"], | |
144 | ) | |
80 | 145 | |
81 | 146 | def test_cli_srcmap(self): |
82 | 147 | # Input and output paths relative to django static dirs. |
83 | 148 | inpath = os.path.join("app2", "static", "app2", "scss", "test.scss") |
84 | 149 | outpath = os.path.join(self.outdir, "test.css") |
85 | # Command to run | |
86 | cmd = ["python", "manage.py", "sass", "-g", inpath, outpath] | |
87 | # Run the management command on testproject. | |
88 | proc = subprocess.run(cmd, cwd=THIS_DIR) | |
89 | # Verify the process exited cleanly. | |
90 | self.assertEqual(proc.returncode, 0) | |
91 | # Assert output is correct. | |
92 | self.assert_output(outpath) | |
93 | self.assertTrue(os.path.isfile(os.path.join(self.outdir, "test.css.map"))) | |
150 | self.assert_output( | |
151 | inpath=inpath, | |
152 | outpath=outpath, | |
153 | real_outpath=outpath, | |
154 | contains=SCSS_CONTAINS, | |
155 | args=["-g"], | |
156 | ) | |
157 | self.assertTrue( | |
158 | os.path.isfile(os.path.join(self.outdir, "test.css.map")) | |
159 | ) | |
94 | 160 | |
95 | @unittest.skip | |
161 | @unittest.skip("Test needs fixed...") | |
96 | 162 | def test_cli_watch(self): |
97 | 163 | # Input and output paths relative to django static dirs. |
98 | 164 | inpath = os.path.join("app2", "static", "app2", "scss", "test.scss") |