Import upstream version 1.1.0
Debian Janitor
2 years ago
0 | # Explicitly declare text files that should always be normalized and converted | |
1 | # to unix line endings, to reduce cross-platform development issues. | |
2 | *.py text eol=lf | |
3 | *.html text eol=lf | |
4 | *.js text eol=lf | |
5 | *.css text eol=lf | |
6 | *.json text eol=lf | |
7 | *.md text eol=lf | |
8 | *.rst text eol=lf |
0 | # Created by https://www.gitignore.io, modified by CodeRed. | |
1 | ||
2 | ####################################### | |
3 | ### Editors | |
4 | ####################################### | |
5 | ||
6 | ||
7 | ### Emacs ### | |
8 | ||
9 | # -*- mode: gitignore; -*- | |
10 | *~ | |
11 | \#*\# | |
12 | /.emacs.desktop | |
13 | /.emacs.desktop.lock | |
14 | *.elc | |
15 | auto-save-list | |
16 | tramp | |
17 | .\#* | |
18 | ||
19 | # Org-mode | |
20 | .org-id-locations | |
21 | *_archive | |
22 | ||
23 | # flymake-mode | |
24 | *_flymake.* | |
25 | ||
26 | # eshell files | |
27 | /eshell/history | |
28 | /eshell/lastdir | |
29 | ||
30 | # elpa packages | |
31 | /elpa/ | |
32 | ||
33 | # reftex files | |
34 | *.rel | |
35 | ||
36 | # Flycheck | |
37 | flycheck_*.el | |
38 | ||
39 | # server auth directory | |
40 | /server/ | |
41 | ||
42 | # projectiles files | |
43 | .projectile | |
44 | ||
45 | # directory configuration | |
46 | .dir-locals.el | |
47 | ||
48 | # network security | |
49 | /network-security.data | |
50 | ||
51 | ||
52 | ### KomodoEdit ### | |
53 | ||
54 | *.komodoproject | |
55 | .komodotools | |
56 | ||
57 | ||
58 | ### PyCharm ### | |
59 | ||
60 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm | |
61 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 | |
62 | ||
63 | # User-specific stuff | |
64 | .idea/**/workspace.xml | |
65 | .idea/**/tasks.xml | |
66 | .idea/**/usage.statistics.xml | |
67 | .idea/**/dictionaries | |
68 | .idea/**/shelf | |
69 | ||
70 | # Generated files | |
71 | .idea/**/contentModel.xml | |
72 | ||
73 | # Sensitive or high-churn files | |
74 | .idea/**/dataSources/ | |
75 | .idea/**/dataSources.ids | |
76 | .idea/**/dataSources.local.xml | |
77 | .idea/**/sqlDataSources.xml | |
78 | .idea/**/dynamic.xml | |
79 | .idea/**/uiDesigner.xml | |
80 | .idea/**/dbnavigator.xml | |
81 | ||
82 | # Gradle | |
83 | .idea/**/gradle.xml | |
84 | .idea/**/libraries | |
85 | ||
86 | # Mongo Explorer plugin | |
87 | .idea/**/mongoSettings.xml | |
88 | ||
89 | # File-based project format | |
90 | *.iws | |
91 | ||
92 | # IntelliJ | |
93 | out/ | |
94 | ||
95 | # mpeltonen/sbt-idea plugin | |
96 | .idea_modules/ | |
97 | ||
98 | # JIRA plugin | |
99 | atlassian-ide-plugin.xml | |
100 | ||
101 | # Cursive Clojure plugin | |
102 | .idea/replstate.xml | |
103 | ||
104 | # Crashlytics plugin (for Android Studio and IntelliJ) | |
105 | com_crashlytics_export_strings.xml | |
106 | crashlytics.properties | |
107 | crashlytics-build.properties | |
108 | fabric.properties | |
109 | ||
110 | # Editor-based Rest Client | |
111 | .idea/httpRequests | |
112 | ||
113 | # Sonarlint plugin | |
114 | .idea/sonarlint | |
115 | ||
116 | ||
117 | ### SublimeText ### | |
118 | ||
119 | # Cache files for Sublime Text | |
120 | *.tmlanguage.cache | |
121 | *.tmPreferences.cache | |
122 | *.stTheme.cache | |
123 | ||
124 | # Workspace files are user-specific | |
125 | *.sublime-workspace | |
126 | ||
127 | # Project files should be checked into the repository, unless a significant | |
128 | # proportion of contributors will probably not be using Sublime Text | |
129 | # *.sublime-project | |
130 | ||
131 | # SFTP configuration file | |
132 | sftp-config.json | |
133 | ||
134 | # Package control specific files | |
135 | Package Control.last-run | |
136 | Package Control.ca-list | |
137 | Package Control.ca-bundle | |
138 | Package Control.system-ca-bundle | |
139 | Package Control.cache/ | |
140 | Package Control.ca-certs/ | |
141 | Package Control.merged-ca-bundle | |
142 | Package Control.user-ca-bundle | |
143 | oscrypto-ca-bundle.crt | |
144 | bh_unicode_properties.cache | |
145 | ||
146 | # Sublime-github package stores a github token in this file | |
147 | # https://packagecontrol.io/packages/sublime-github | |
148 | GitHub.sublime-settings | |
149 | ||
150 | ||
151 | ### TextMate ### | |
152 | ||
153 | *.tmproj | |
154 | *.tmproject | |
155 | tmtags | |
156 | ||
157 | ||
158 | ### Vim ### | |
159 | ||
160 | # Swap | |
161 | [._]*.s[a-v][a-z] | |
162 | [._]*.sw[a-p] | |
163 | [._]s[a-rt-v][a-z] | |
164 | [._]ss[a-gi-z] | |
165 | [._]sw[a-p] | |
166 | ||
167 | # Session | |
168 | Session.vim | |
169 | ||
170 | # Temporary | |
171 | .netrwhist | |
172 | # Auto-generated tag files | |
173 | tags | |
174 | # Persistent undo | |
175 | [._]*.un~ | |
176 | ||
177 | ||
178 | ### VisualStudioCode ### | |
179 | .vscode/ | |
180 | ||
181 | ### VisualStudioCode Patch ### | |
182 | # Ignore all local history of files | |
183 | .history | |
184 | ||
185 | ||
186 | ||
187 | ####################################### | |
188 | ### Django/Python Stack | |
189 | ####################################### | |
190 | ||
191 | ||
192 | ### Django ### | |
193 | ||
194 | *.log | |
195 | *.pot | |
196 | *.pyc | |
197 | __pycache__/ | |
198 | local_settings.py | |
199 | db.sqlite3 | |
200 | ||
201 | # If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/ | |
202 | # in your Git repository. Update and uncomment the following line accordingly. | |
203 | # <django-project-name>/staticfiles/ | |
204 | ||
205 | ||
206 | ### Django.Python Stack ### | |
207 | ||
208 | # Byte-compiled / optimized / DLL files | |
209 | *.py[cod] | |
210 | *$py.class | |
211 | ||
212 | # C extensions | |
213 | *.so | |
214 | ||
215 | # Distribution / packaging | |
216 | .Python | |
217 | build/ | |
218 | develop-eggs/ | |
219 | dist/ | |
220 | downloads/ | |
221 | eggs/ | |
222 | .eggs/ | |
223 | lib/ | |
224 | lib64/ | |
225 | parts/ | |
226 | sdist/ | |
227 | var/ | |
228 | wheels/ | |
229 | share/python-wheels/ | |
230 | *.egg-info/ | |
231 | .installed.cfg | |
232 | *.egg | |
233 | MANIFEST | |
234 | ||
235 | # PyInstaller | |
236 | # Usually these files are written by a python script from a template | |
237 | # before PyInstaller builds the exe, so as to inject date/other infos into it. | |
238 | *.manifest | |
239 | *.spec | |
240 | ||
241 | # Installer logs | |
242 | pip-log.txt | |
243 | pip-delete-this-directory.txt | |
244 | ||
245 | # Unit test / coverage reports | |
246 | htmlcov/ | |
247 | .tox/ | |
248 | .nox/ | |
249 | .coverage | |
250 | .coverage.* | |
251 | .cache | |
252 | nosetests.xml | |
253 | coverage.xml | |
254 | *.cover | |
255 | .hypothesis/ | |
256 | .pytest_cache/ | |
257 | junit/ | |
258 | ||
259 | # Translations | |
260 | *.mo | |
261 | ||
262 | # Flask stuff: | |
263 | instance/ | |
264 | .webassets-cache | |
265 | ||
266 | # Scrapy stuff: | |
267 | .scrapy | |
268 | ||
269 | # Sphinx documentation | |
270 | docs/_build/ | |
271 | ||
272 | # PyBuilder | |
273 | target/ | |
274 | ||
275 | # Jupyter Notebook | |
276 | .ipynb_checkpoints | |
277 | ||
278 | # IPython | |
279 | profile_default/ | |
280 | ipython_config.py | |
281 | ||
282 | # pyenv | |
283 | .python-version | |
284 | ||
285 | # celery beat schedule file | |
286 | celerybeat-schedule | |
287 | ||
288 | # SageMath parsed files | |
289 | *.sage.py | |
290 | ||
291 | # Environments | |
292 | .env | |
293 | .venv | |
294 | env/ | |
295 | venv/ | |
296 | ENV/ | |
297 | env.bak/ | |
298 | venv.bak/ | |
299 | ||
300 | # Spyder project settings | |
301 | .spyderproject | |
302 | .spyproject | |
303 | ||
304 | # Rope project settings | |
305 | .ropeproject | |
306 | ||
307 | # mkdocs documentation | |
308 | /site | |
309 | ||
310 | # mypy | |
311 | .mypy_cache/ | |
312 | .dmypy.json | |
313 | dmypy.json | |
314 | ||
315 | # Pyre type checker | |
316 | .pyre/ | |
317 | ||
318 | ||
319 | ### OSX ### | |
320 | ||
321 | # General | |
322 | .DS_Store | |
323 | .AppleDouble | |
324 | .LSOverride | |
325 | ||
326 | # Icon must end with two \r | |
327 | Icon | |
328 | ||
329 | # Thumbnails | |
330 | ._* | |
331 | ||
332 | # Files that might appear in the root of a volume | |
333 | .DocumentRevisions-V100 | |
334 | .fseventsd | |
335 | .Spotlight-V100 | |
336 | .TemporaryItems | |
337 | .Trashes | |
338 | .VolumeIcon.icns | |
339 | .com.apple.timemachine.donotpresent | |
340 | ||
341 | # Directories potentially created on remote AFP share | |
342 | .AppleDB | |
343 | .AppleDesktop | |
344 | Network Trash Folder | |
345 | Temporary Items | |
346 | .apdisk | |
347 | ||
348 | ||
349 | ||
350 | ####################################### | |
351 | ### Operating Systems | |
352 | ####################################### | |
353 | ||
354 | ||
355 | ### Windows ### | |
356 | ||
357 | # Windows thumbnail cache files | |
358 | Thumbs.db | |
359 | ehthumbs.db | |
360 | ehthumbs_vista.db | |
361 | ||
362 | # Dump file | |
363 | *.stackdump | |
364 | ||
365 | # Folder config file | |
366 | [Dd]esktop.ini | |
367 | ||
368 | # Recycle Bin used on file shares | |
369 | $RECYCLE.BIN/ | |
370 | ||
371 | # Windows Installer files | |
372 | *.cab | |
373 | *.msi | |
374 | *.msix | |
375 | *.msm | |
376 | *.msp | |
377 | ||
378 | # Windows shortcuts | |
379 | *.lnk | |
380 | ||
381 | ||
382 | ####################################### | |
383 | ### Custom | |
384 | ####################################### | |
385 | ||
386 | testproject/**/*.css |
0 | Metadata-Version: 2.1 | |
1 | Name: django-sass | |
2 | Version: 1.1.0 | |
3 | Summary: The absolute simplest way to use Sass with Django. Pure Python, minimal dependencies, and no special configuration required! | |
4 | Home-page: https://github.com/coderedcorp/django-sass | |
5 | Author: CodeRed LLC | |
6 | Author-email: info@coderedcorp.com | |
7 | License: BSD license | |
8 | Description: django-sass | |
9 | =========== | |
10 | ||
11 | The absolute simplest way to use [Sass](https://sass-lang.com/) with Django. | |
12 | Pure Python, minimal dependencies, and no special configuration required. | |
13 | ||
14 | [Source code on GitHub](https://github.com/coderedcorp/django-sass) | |
15 | ||
16 | ||
17 | Status | |
18 | ------ | |
19 | ||
20 | | | | | |
21 | |------------------------|----------------------| | |
22 | | 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/) | | |
23 | | 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) | | |
24 | ||
25 | ||
26 | Installation | |
27 | ------------ | |
28 | ||
29 | 1. Install from pip. | |
30 | ||
31 | ``` | |
32 | pip install django-sass | |
33 | ``` | |
34 | ||
35 | 2. Add to your `INSTALLED_APPS` (you only need to do this in a dev environment, | |
36 | you would not want this in your production settings file, although it adds zero | |
37 | overhead): | |
38 | ||
39 | ```python | |
40 | INSTALLED_APPS = [ | |
41 | ..., | |
42 | 'django_sass', | |
43 | ] | |
44 | ``` | |
45 | ||
46 | 3. Congratulations, you're done 😀 | |
47 | ||
48 | ||
49 | Usage | |
50 | ----- | |
51 | ||
52 | In your app's static files, use Sass as normal. The only difference is that | |
53 | you can **not** traverse upwards using `../` in `@import` statements. For example: | |
54 | ||
55 | ``` | |
56 | app1/ | |
57 | |- static/ | |
58 | |- app1/ | |
59 | |- scss/ | |
60 | |- _colors.scss | |
61 | |- app1.scss | |
62 | app2/ | |
63 | |- static/ | |
64 | |- app2/ | |
65 | |- scss/ | |
66 | |- _colors.scss | |
67 | |- app2.scss | |
68 | ``` | |
69 | ||
70 | In `app2.scss` you could reference app1's and app2's `_colors.scss` import as so: | |
71 | ||
72 | ```scss | |
73 | @import 'app1/scss/colors'; | |
74 | @import 'app2/scss/colors'; | |
75 | // Or since you are in app2, you can reference its colors with a relative path. | |
76 | @import 'colors'; | |
77 | ``` | |
78 | ||
79 | Then to compile `app2.scss` and put it in the `css` directory, | |
80 | run the following management command (the `-g` will build a source map, which | |
81 | is helpful for debugging CSS): | |
82 | ||
83 | ``` | |
84 | python manage.py sass app2/static/app2/scss/app2.scss app2/static/app2/css/app2.css -g | |
85 | ``` | |
86 | ||
87 | Or, you can compile the entire `scss` directory into | |
88 | a corresponding `css` directory. This will traverse all subdirectories as well: | |
89 | ||
90 | ``` | |
91 | python manage.py sass app2/static/app2/scss/ app2/static/app2/css/ | |
92 | ``` | |
93 | ||
94 | In your Django HTML template, reference the CSS file as normal: | |
95 | ||
96 | ```html | |
97 | {% load static %} | |
98 | <link href="{% static 'app2/css/app2.css' %}" rel="stylesheet"> | |
99 | ``` | |
100 | ||
101 | ✨✨ **Congratulations, you are now a Django + Sass developer!** ✨✨ | |
102 | ||
103 | Now you can commit those CSS files to version control, or run `collectstatic` | |
104 | and deploy them as normal. | |
105 | ||
106 | For an example project layout, see `testproject/` in this repository. | |
107 | ||
108 | ||
109 | Watch Mode | |
110 | ---------- | |
111 | ||
112 | To have `django-sass` watch files and recompile them as they change (useful in | |
113 | development), add the ``--watch`` flag. | |
114 | ||
115 | ``` | |
116 | python manage.py sass app2/static/app2/scss/ app2/static/app2/css/ --watch | |
117 | ``` | |
118 | ||
119 | ||
120 | Example: deploying compressed CSS to production | |
121 | ----------------------------------------------- | |
122 | ||
123 | To compile minified CSS, use the `-t` flag to specify compression level (one of: | |
124 | "expanded", "nested", "compact", "compressed"). The default is "expanded" which | |
125 | is human-readable. | |
126 | ||
127 | ``` | |
128 | python manage.py sass app2/static/app2/scss/ app2/static/app2/css/ -t compressed | |
129 | ``` | |
130 | ||
131 | You may now optionally commit the CSS files to version control if so desired, | |
132 | or omit them, whatever fits your needs better. Then run `collectsatic` as normal. | |
133 | ||
134 | ``` | |
135 | python manage.py collectstatic | |
136 | ``` | |
137 | ||
138 | And now proceed with deploying your files as normal. | |
139 | ||
140 | ||
141 | Limitations | |
142 | ----------- | |
143 | ||
144 | * `@import` statements must reference a path relative to a path in | |
145 | `STATICFILES_FINDERS` (which will usually be an app's `static/` directory or | |
146 | some other directory specified in `STATICFILES_DIRS`). Or they can reference a | |
147 | relative path equal to or below the current file. It does not support | |
148 | traversing up the filesystem (i.e. `../`). | |
149 | ||
150 | Legal imports: | |
151 | ```scss | |
152 | @import 'file-from-currdir'; | |
153 | @import 'subdir/file'; | |
154 | @import 'another-app/file'; | |
155 | ``` | |
156 | Illegal imports: | |
157 | ```scss | |
158 | @import '../file'; | |
159 | ``` | |
160 | ||
161 | * Only supports `-g`, `-p`, and `-t` options similar to `pysassc`. Ideally | |
162 | `django-sass` will be as similar as possible to the `pysassc` command line | |
163 | interface. | |
164 | ||
165 | Feel free to file an issue or make a pull request to improve any of these | |
166 | limitations. 🐱💻 | |
167 | ||
168 | ||
169 | Why django-sass? | |
170 | ---------------- | |
171 | ||
172 | Other packages such as | |
173 | [django-libsass](https://github.com/torchbox/django-libsass) and | |
174 | [django-sass-processor](https://github.com/jrief/django-sass-processor), while | |
175 | nice packages, require `django-compressor` which itself depends on several other | |
176 | packages that require compilation to install. | |
177 | ||
178 | Installing `django-compressor` in your production web server requires a LOT of | |
179 | extra bloat including a C compiler. It then will compile the Sass on-the-fly | |
180 | while rendering the HTML templates. This is a wasteful use of CPU on your web | |
181 | server. | |
182 | ||
183 | Instead, `django-sass` lets you compile the Sass locally on your machine | |
184 | *before* deploying, to reduce dependencies and CPU time on your production web | |
185 | server. This helps keep things fast and simple. | |
186 | ||
187 | * If you simply want to use Sass in development without installing a web of | |
188 | unwanted dependencies, then `django-sass` is for you. | |
189 | * If you don't want to deploy any processors or compressors to your production | |
190 | server, then `django-sass` is for you. | |
191 | * If you don't want to change the way you reference and serve static files, | |
192 | then `django-sass` is for you. | |
193 | * And if you want the absolute simplest installation and setup possible for | |
194 | doing Sass, `django-sass` is for you too. | |
195 | ||
196 | django-sass only depends on libsass (which provides pre-built wheels for | |
197 | Windows, Mac, and Linux), and of course Django (any version). | |
198 | ||
199 | ||
200 | Programmatically Compiling Sass | |
201 | ------------------------------- | |
202 | ||
203 | You can also use `django-sass` in Python to programmatically compile the sass. | |
204 | This is useful for build scripts and static site generators. | |
205 | ||
206 | ```python | |
207 | from django_sass import compile_sass | |
208 | ||
209 | # Compile scss and write to output file. | |
210 | compile_sass( | |
211 | inpath="/path/to/file.scss", | |
212 | outpath="/path/to/output.css", | |
213 | output_style="compressed", | |
214 | precision=8, | |
215 | source_map=True | |
216 | ) | |
217 | ``` | |
218 | ||
219 | For more advanced usage, you can specify additional sass search paths outside | |
220 | of your Django project by using the `include_paths` argument. | |
221 | ||
222 | ```python | |
223 | from django_sass import compile_sass, find_static_paths | |
224 | ||
225 | # Get Django's static paths. | |
226 | dirs = find_static_paths() | |
227 | ||
228 | # Add external paths. | |
229 | dirs.append("/external/path/") | |
230 | ||
231 | # Compile scss and write to output file. | |
232 | compile_sass( | |
233 | inpath="/path/to/file.scss", | |
234 | outpath="/path/to/output.css", | |
235 | output_style="compressed", | |
236 | precision=8, | |
237 | source_map=True, | |
238 | include_paths=dirs, | |
239 | ) | |
240 | ``` | |
241 | ||
242 | Contributing | |
243 | ------------ | |
244 | ||
245 | To set up a development environment, first check out this repository, create a | |
246 | venv, then: | |
247 | ||
248 | ``` | |
249 | (myvenv)$ pip install -r requirements-dev.txt | |
250 | ``` | |
251 | ||
252 | Before committing, run static analysis tools: | |
253 | ||
254 | ``` | |
255 | (myvenv)$ black . | |
256 | (myvenv)$ flake8 | |
257 | (myvenv)$ mypy | |
258 | ``` | |
259 | ||
260 | Then run the unit tests: | |
261 | ||
262 | ``` | |
263 | (myvenv)$ pytest | |
264 | ``` | |
265 | ||
266 | ||
267 | Changelog | |
268 | --------- | |
269 | ||
270 | #### 1.1.0 | |
271 | * New: Now compiles `.sass` files as well as `.scss` files. | |
272 | * Fix bug when input path is a file and output path does not exist. | |
273 | ||
274 | #### 1.0.1 | |
275 | * Maintanence release, no functional changes. | |
276 | * Add additional type hints within the codebase. | |
277 | * Tested against Django 3.1 | |
278 | * Formatted code with `black`. | |
279 | ||
280 | #### 1.0.0 | |
281 | * New: You can now use `django_sass` APIs directly in Python. | |
282 | * Added unit tests. | |
283 | * Code quality improvements. | |
284 | ||
285 | #### 0.2.0 | |
286 | * New feature: `-g` option to build a source map (when input is a file, not a | |
287 | directory). | |
288 | ||
289 | #### 0.1.2 | |
290 | * Fix: Write compiled CSS files as UTF-8. | |
291 | * Change: Default `-p` precision from 5 to 8 for better support building | |
292 | Bootstrap CSS. | |
293 | ||
294 | #### 0.1.1 | |
295 | * Fix: Create full file path if not exists when specifying a file output. | |
296 | ||
297 | #### 0.1.0 | |
298 | * Initial release | |
299 | ||
300 | Platform: UNKNOWN | |
301 | Classifier: Environment :: Web Environment | |
302 | Classifier: Framework :: Django :: 2.0 | |
303 | Classifier: Framework :: Django :: 2.1 | |
304 | Classifier: Framework :: Django :: 2.2 | |
305 | Classifier: Framework :: Django :: 3.0 | |
306 | Classifier: Framework :: Django :: 3.1 | |
307 | Classifier: Framework :: Django | |
308 | Classifier: Intended Audience :: Developers | |
309 | Classifier: License :: OSI Approved :: BSD License | |
310 | Classifier: Natural Language :: English | |
311 | Classifier: Programming Language :: Python :: 3 :: Only | |
312 | Classifier: Programming Language :: Python :: 3 | |
313 | Classifier: Programming Language :: Python :: 3.6 | |
314 | Classifier: Programming Language :: Python :: 3.7 | |
315 | Classifier: Programming Language :: Python :: 3.8 | |
316 | Classifier: Programming Language :: Python :: 3.9 | |
317 | Description-Content-Type: text/markdown |
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. |
0 | # Add steps that analyze code, save build artifacts, deploy, and more: | |
1 | # https://docs.microsoft.com/azure/devops/pipelines/languages/python | |
2 | # | |
3 | # NOTES: | |
4 | # | |
5 | # Display name of each step should be prefixed with one of the following: | |
6 | # CR-QC: for quality control measures. | |
7 | # CR-BUILD: for build-related tasks. | |
8 | # CR-DEPLOY: for publication or deployment. | |
9 | # [no prefix]: for unrelated CI setup/tooling. | |
10 | # | |
11 | # Use PowerShell Core for any utility scripts so they are re-usable across | |
12 | # Windows, macOS, and Linux. | |
13 | # | |
14 | ||
15 | ||
16 | trigger: | |
17 | - master | |
18 | ||
19 | ||
20 | stages: | |
21 | - stage: Unit_Tests | |
22 | displayName: Unit Tests | |
23 | ||
24 | jobs: | |
25 | - job: pytest | |
26 | displayName: pytest | |
27 | pool: | |
28 | vmImage: 'ubuntu-latest' | |
29 | strategy: | |
30 | matrix: | |
31 | py3.5: | |
32 | PYTHON_VERSION: '3.5' | |
33 | py3.6: | |
34 | PYTHON_VERSION: '3.6' | |
35 | py3.7: | |
36 | PYTHON_VERSION: '3.7' | |
37 | py3.8: | |
38 | PYTHON_VERSION: '3.8' | |
39 | ||
40 | steps: | |
41 | - task: UsePythonVersion@0 | |
42 | displayName: 'Use Python version' | |
43 | inputs: | |
44 | versionSpec: '$(PYTHON_VERSION)' | |
45 | architecture: 'x64' | |
46 | ||
47 | - script: python -m pip install -r requirements-dev.txt | |
48 | displayName: 'CR-QC: Install from local repo' | |
49 | ||
50 | - script: pytest ./testproject/ | |
51 | displayName: 'CR-QC: Run unit tests' | |
52 | ||
53 | - task: PublishTestResults@2 | |
54 | displayName: 'Publish unit test report' | |
55 | condition: succeededOrFailed() | |
56 | inputs: | |
57 | testResultsFiles: '**/test-*.xml' | |
58 | testRunTitle: 'Publish test results for Python $(python.version)' | |
59 | ||
60 | - task: PublishCodeCoverageResults@1 | |
61 | displayName: 'Publish code coverage report' | |
62 | condition: succeededOrFailed() | |
63 | inputs: | |
64 | codeCoverageTool: Cobertura | |
65 | summaryFileLocation: '$(System.DefaultWorkingDirectory)/coverage.xml' | |
66 | ||
67 | ||
68 | - stage: Static_Analysis | |
69 | displayName: Static Analysis | |
70 | dependsOn: Unit_Tests | |
71 | condition: succeeded('Unit_Tests') | |
72 | jobs: | |
73 | - job: lint | |
74 | displayName: Linters | |
75 | pool: | |
76 | vmImage: 'ubuntu-latest' | |
77 | ||
78 | steps: | |
79 | - task: UsePythonVersion@0 | |
80 | displayName: 'Use Python version' | |
81 | inputs: | |
82 | versionSpec: '3.8' | |
83 | architecture: 'x64' | |
84 | ||
85 | - script: python -m pip install -r requirements-dev.txt | |
86 | displayName: 'CR-QC: Install from local repo' | |
87 | ||
88 | - script: flake8 . | |
89 | displayName: 'CR-QC: Static analysis (flake8)' | |
90 | ||
91 | - script: mypy . | |
92 | displayName: 'CR-QC: Type check (mypy)' | |
93 | ||
94 | - job: codecov | |
95 | displayName: Code Coverage | |
96 | pool: | |
97 | vmImage: 'ubuntu-latest' | |
98 | ||
99 | steps: | |
100 | - task: DownloadPipelineArtifact@2 | |
101 | displayName: 'Download code coverage from current build' | |
102 | inputs: | |
103 | source: 'current' | |
104 | path: '$(Agent.WorkFolder)/current-artifacts' | |
105 | project: '$(System.TeamProjectId)' | |
106 | pipeline: '$(System.DefinitionId)' | |
107 | ||
108 | - pwsh: ./ci/compare-codecov.ps1 -wd $Env:WorkDir | |
109 | displayName: 'CR-QC: Compare code coverage' | |
110 | env: | |
111 | WorkDir: $(Agent.WorkFolder) |
0 | #!/usr/bin/env pwsh | |
1 | ||
2 | <# | |
3 | .SYNOPSIS | |
4 | Compares code coverage percent of local coverage.xml file to master branch | |
5 | (Azure Pipeline API). | |
6 | ||
7 | .PARAMETER wd | |
8 | The working directory in which to search for current coverage.xml. | |
9 | ||
10 | .PARAMETER org | |
11 | Name of the Azure DevOps organization where the pipeline is hosted. | |
12 | ||
13 | .PARAMETER project | |
14 | Name of the Azure DevOps project to which the pipeline belongs. | |
15 | ||
16 | .PARAMETER pipeline_name | |
17 | Name of the desired pipeline within the project. This is to support projects | |
18 | with multiple pipelines. | |
19 | #> | |
20 | ||
21 | ||
22 | # ---- SETUP ------------------------------------------------------------------- | |
23 | ||
24 | ||
25 | param( | |
26 | [string] $wd = (Get-Item (Split-Path $PSCommandPath -Parent)).Parent, | |
27 | [string] $org = "coderedcorp", | |
28 | [string] $project = "coderedcms", | |
29 | [string] $pipeline_name = "django-sass" | |
30 | ) | |
31 | ||
32 | # Hide "UI" and progress bars. | |
33 | $ProgressPreference = "SilentlyContinue" | |
34 | ||
35 | # API setup. | |
36 | $ApiBase = "https://dev.azure.com/$org/$project" | |
37 | ||
38 | ||
39 | # ---- GET CODE COVERAGE FROM RECENT BUILD ------------------------------------- | |
40 | ||
41 | ||
42 | # Get list of all recent builds. | |
43 | $masterBuildJson = ( | |
44 | Invoke-WebRequest "$ApiBase/_apis/build/builds?branchName=refs/heads/master&api-version=5.1" | |
45 | ).Content | ConvertFrom-Json | |
46 | ||
47 | # Get the latest matching build ID from the list of builds. | |
48 | foreach ($build in $masterBuildJson.value) { | |
49 | if ($build.definition.name -eq $pipeline_name) { | |
50 | $masterLatestId = $build.id | |
51 | break | |
52 | } | |
53 | } | |
54 | ||
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" | |
58 | ).Content | ConvertFrom-Json | |
59 | foreach ($cov in $masterCoverageJson.coverageData.coverageStats) { | |
60 | if ($cov.label -eq "Lines") { | |
61 | $masterlinerate = [math]::Round(($cov.covered / $cov.total) * 100, 2) | |
62 | } | |
63 | } | |
64 | ||
65 | ||
66 | # ---- GET COVERAGE FROM LOCAL RUN --------------------------------------------- | |
67 | ||
68 | ||
69 | # Get current code coverage from coverage.xml file. | |
70 | $coveragePath = Get-ChildItem -Recurse -Filter "coverage.xml" $wd | |
71 | if (Test-Path -Path $coveragePath) { | |
72 | [xml]$BranchXML = Get-Content $coveragePath | |
73 | } | |
74 | else { | |
75 | Write-Host -ForegroundColor Red ` | |
76 | "No code coverage from this build. Is pytest configured to output code coverage? Exiting." | |
77 | exit 1 | |
78 | } | |
79 | $branchlinerate = [math]::Round([decimal]$BranchXML.coverage.'line-rate' * 100, 2) | |
80 | ||
81 | ||
82 | # ---- PRINT OUTPUT ------------------------------------------------------------ | |
83 | ||
84 | ||
85 | Write-Output "" | |
86 | Write-Output "Master line coverage rate: $masterlinerate%" | |
87 | Write-Output "Branch line coverage rate: $branchlinerate%" | |
88 | ||
89 | if ($masterlinerate -eq 0) { | |
90 | $change = "Infinite" | |
91 | } | |
92 | else { | |
93 | $change = [math]::Abs($branchlinerate - $masterlinerate) | |
94 | } | |
95 | ||
96 | if ($branchlinerate -gt $masterlinerate) { | |
97 | Write-Host "Coverage increased by $change% 😀" -ForegroundColor Green | |
98 | exit 0 | |
99 | } | |
100 | elseif ($branchlinerate -eq $masterlinerate) { | |
101 | Write-Host "Coverage has not changed." -ForegroundColor Green | |
102 | exit 0 | |
103 | } | |
104 | else { | |
105 | Write-Host "Coverage decreased by $change% 😭" -ForegroundColor Red | |
106 | exit 4 | |
107 | } |
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 | Metadata-Version: 2.1 | |
1 | Name: django-sass | |
2 | Version: 1.1.0 | |
3 | Summary: The absolute simplest way to use Sass with Django. Pure Python, minimal dependencies, and no special configuration required! | |
4 | Home-page: https://github.com/coderedcorp/django-sass | |
5 | Author: CodeRed LLC | |
6 | Author-email: info@coderedcorp.com | |
7 | License: BSD license | |
8 | Description: django-sass | |
9 | =========== | |
10 | ||
11 | The absolute simplest way to use [Sass](https://sass-lang.com/) with Django. | |
12 | Pure Python, minimal dependencies, and no special configuration required. | |
13 | ||
14 | [Source code on GitHub](https://github.com/coderedcorp/django-sass) | |
15 | ||
16 | ||
17 | Status | |
18 | ------ | |
19 | ||
20 | | | | | |
21 | |------------------------|----------------------| | |
22 | | 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/) | | |
23 | | 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) | | |
24 | ||
25 | ||
26 | Installation | |
27 | ------------ | |
28 | ||
29 | 1. Install from pip. | |
30 | ||
31 | ``` | |
32 | pip install django-sass | |
33 | ``` | |
34 | ||
35 | 2. Add to your `INSTALLED_APPS` (you only need to do this in a dev environment, | |
36 | you would not want this in your production settings file, although it adds zero | |
37 | overhead): | |
38 | ||
39 | ```python | |
40 | INSTALLED_APPS = [ | |
41 | ..., | |
42 | 'django_sass', | |
43 | ] | |
44 | ``` | |
45 | ||
46 | 3. Congratulations, you're done 😀 | |
47 | ||
48 | ||
49 | Usage | |
50 | ----- | |
51 | ||
52 | In your app's static files, use Sass as normal. The only difference is that | |
53 | you can **not** traverse upwards using `../` in `@import` statements. For example: | |
54 | ||
55 | ``` | |
56 | app1/ | |
57 | |- static/ | |
58 | |- app1/ | |
59 | |- scss/ | |
60 | |- _colors.scss | |
61 | |- app1.scss | |
62 | app2/ | |
63 | |- static/ | |
64 | |- app2/ | |
65 | |- scss/ | |
66 | |- _colors.scss | |
67 | |- app2.scss | |
68 | ``` | |
69 | ||
70 | In `app2.scss` you could reference app1's and app2's `_colors.scss` import as so: | |
71 | ||
72 | ```scss | |
73 | @import 'app1/scss/colors'; | |
74 | @import 'app2/scss/colors'; | |
75 | // Or since you are in app2, you can reference its colors with a relative path. | |
76 | @import 'colors'; | |
77 | ``` | |
78 | ||
79 | Then to compile `app2.scss` and put it in the `css` directory, | |
80 | run the following management command (the `-g` will build a source map, which | |
81 | is helpful for debugging CSS): | |
82 | ||
83 | ``` | |
84 | python manage.py sass app2/static/app2/scss/app2.scss app2/static/app2/css/app2.css -g | |
85 | ``` | |
86 | ||
87 | Or, you can compile the entire `scss` directory into | |
88 | a corresponding `css` directory. This will traverse all subdirectories as well: | |
89 | ||
90 | ``` | |
91 | python manage.py sass app2/static/app2/scss/ app2/static/app2/css/ | |
92 | ``` | |
93 | ||
94 | In your Django HTML template, reference the CSS file as normal: | |
95 | ||
96 | ```html | |
97 | {% load static %} | |
98 | <link href="{% static 'app2/css/app2.css' %}" rel="stylesheet"> | |
99 | ``` | |
100 | ||
101 | ✨✨ **Congratulations, you are now a Django + Sass developer!** ✨✨ | |
102 | ||
103 | Now you can commit those CSS files to version control, or run `collectstatic` | |
104 | and deploy them as normal. | |
105 | ||
106 | For an example project layout, see `testproject/` in this repository. | |
107 | ||
108 | ||
109 | Watch Mode | |
110 | ---------- | |
111 | ||
112 | To have `django-sass` watch files and recompile them as they change (useful in | |
113 | development), add the ``--watch`` flag. | |
114 | ||
115 | ``` | |
116 | python manage.py sass app2/static/app2/scss/ app2/static/app2/css/ --watch | |
117 | ``` | |
118 | ||
119 | ||
120 | Example: deploying compressed CSS to production | |
121 | ----------------------------------------------- | |
122 | ||
123 | To compile minified CSS, use the `-t` flag to specify compression level (one of: | |
124 | "expanded", "nested", "compact", "compressed"). The default is "expanded" which | |
125 | is human-readable. | |
126 | ||
127 | ``` | |
128 | python manage.py sass app2/static/app2/scss/ app2/static/app2/css/ -t compressed | |
129 | ``` | |
130 | ||
131 | You may now optionally commit the CSS files to version control if so desired, | |
132 | or omit them, whatever fits your needs better. Then run `collectsatic` as normal. | |
133 | ||
134 | ``` | |
135 | python manage.py collectstatic | |
136 | ``` | |
137 | ||
138 | And now proceed with deploying your files as normal. | |
139 | ||
140 | ||
141 | Limitations | |
142 | ----------- | |
143 | ||
144 | * `@import` statements must reference a path relative to a path in | |
145 | `STATICFILES_FINDERS` (which will usually be an app's `static/` directory or | |
146 | some other directory specified in `STATICFILES_DIRS`). Or they can reference a | |
147 | relative path equal to or below the current file. It does not support | |
148 | traversing up the filesystem (i.e. `../`). | |
149 | ||
150 | Legal imports: | |
151 | ```scss | |
152 | @import 'file-from-currdir'; | |
153 | @import 'subdir/file'; | |
154 | @import 'another-app/file'; | |
155 | ``` | |
156 | Illegal imports: | |
157 | ```scss | |
158 | @import '../file'; | |
159 | ``` | |
160 | ||
161 | * Only supports `-g`, `-p`, and `-t` options similar to `pysassc`. Ideally | |
162 | `django-sass` will be as similar as possible to the `pysassc` command line | |
163 | interface. | |
164 | ||
165 | Feel free to file an issue or make a pull request to improve any of these | |
166 | limitations. 🐱💻 | |
167 | ||
168 | ||
169 | Why django-sass? | |
170 | ---------------- | |
171 | ||
172 | Other packages such as | |
173 | [django-libsass](https://github.com/torchbox/django-libsass) and | |
174 | [django-sass-processor](https://github.com/jrief/django-sass-processor), while | |
175 | nice packages, require `django-compressor` which itself depends on several other | |
176 | packages that require compilation to install. | |
177 | ||
178 | Installing `django-compressor` in your production web server requires a LOT of | |
179 | extra bloat including a C compiler. It then will compile the Sass on-the-fly | |
180 | while rendering the HTML templates. This is a wasteful use of CPU on your web | |
181 | server. | |
182 | ||
183 | Instead, `django-sass` lets you compile the Sass locally on your machine | |
184 | *before* deploying, to reduce dependencies and CPU time on your production web | |
185 | server. This helps keep things fast and simple. | |
186 | ||
187 | * If you simply want to use Sass in development without installing a web of | |
188 | unwanted dependencies, then `django-sass` is for you. | |
189 | * If you don't want to deploy any processors or compressors to your production | |
190 | server, then `django-sass` is for you. | |
191 | * If you don't want to change the way you reference and serve static files, | |
192 | then `django-sass` is for you. | |
193 | * And if you want the absolute simplest installation and setup possible for | |
194 | doing Sass, `django-sass` is for you too. | |
195 | ||
196 | django-sass only depends on libsass (which provides pre-built wheels for | |
197 | Windows, Mac, and Linux), and of course Django (any version). | |
198 | ||
199 | ||
200 | Programmatically Compiling Sass | |
201 | ------------------------------- | |
202 | ||
203 | You can also use `django-sass` in Python to programmatically compile the sass. | |
204 | This is useful for build scripts and static site generators. | |
205 | ||
206 | ```python | |
207 | from django_sass import compile_sass | |
208 | ||
209 | # Compile scss and write to output file. | |
210 | compile_sass( | |
211 | inpath="/path/to/file.scss", | |
212 | outpath="/path/to/output.css", | |
213 | output_style="compressed", | |
214 | precision=8, | |
215 | source_map=True | |
216 | ) | |
217 | ``` | |
218 | ||
219 | For more advanced usage, you can specify additional sass search paths outside | |
220 | of your Django project by using the `include_paths` argument. | |
221 | ||
222 | ```python | |
223 | from django_sass import compile_sass, find_static_paths | |
224 | ||
225 | # Get Django's static paths. | |
226 | dirs = find_static_paths() | |
227 | ||
228 | # Add external paths. | |
229 | dirs.append("/external/path/") | |
230 | ||
231 | # Compile scss and write to output file. | |
232 | compile_sass( | |
233 | inpath="/path/to/file.scss", | |
234 | outpath="/path/to/output.css", | |
235 | output_style="compressed", | |
236 | precision=8, | |
237 | source_map=True, | |
238 | include_paths=dirs, | |
239 | ) | |
240 | ``` | |
241 | ||
242 | Contributing | |
243 | ------------ | |
244 | ||
245 | To set up a development environment, first check out this repository, create a | |
246 | venv, then: | |
247 | ||
248 | ``` | |
249 | (myvenv)$ pip install -r requirements-dev.txt | |
250 | ``` | |
251 | ||
252 | Before committing, run static analysis tools: | |
253 | ||
254 | ``` | |
255 | (myvenv)$ black . | |
256 | (myvenv)$ flake8 | |
257 | (myvenv)$ mypy | |
258 | ``` | |
259 | ||
260 | Then run the unit tests: | |
261 | ||
262 | ``` | |
263 | (myvenv)$ pytest | |
264 | ``` | |
265 | ||
266 | ||
267 | Changelog | |
268 | --------- | |
269 | ||
270 | #### 1.1.0 | |
271 | * New: Now compiles `.sass` files as well as `.scss` files. | |
272 | * Fix bug when input path is a file and output path does not exist. | |
273 | ||
274 | #### 1.0.1 | |
275 | * Maintanence release, no functional changes. | |
276 | * Add additional type hints within the codebase. | |
277 | * Tested against Django 3.1 | |
278 | * Formatted code with `black`. | |
279 | ||
280 | #### 1.0.0 | |
281 | * New: You can now use `django_sass` APIs directly in Python. | |
282 | * Added unit tests. | |
283 | * Code quality improvements. | |
284 | ||
285 | #### 0.2.0 | |
286 | * New feature: `-g` option to build a source map (when input is a file, not a | |
287 | directory). | |
288 | ||
289 | #### 0.1.2 | |
290 | * Fix: Write compiled CSS files as UTF-8. | |
291 | * Change: Default `-p` precision from 5 to 8 for better support building | |
292 | Bootstrap CSS. | |
293 | ||
294 | #### 0.1.1 | |
295 | * Fix: Create full file path if not exists when specifying a file output. | |
296 | ||
297 | #### 0.1.0 | |
298 | * Initial release | |
299 | ||
300 | Platform: UNKNOWN | |
301 | Classifier: Environment :: Web Environment | |
302 | Classifier: Framework :: Django :: 2.0 | |
303 | Classifier: Framework :: Django :: 2.1 | |
304 | Classifier: Framework :: Django :: 2.2 | |
305 | Classifier: Framework :: Django :: 3.0 | |
306 | Classifier: Framework :: Django :: 3.1 | |
307 | Classifier: Framework :: Django | |
308 | Classifier: Intended Audience :: Developers | |
309 | Classifier: License :: OSI Approved :: BSD License | |
310 | Classifier: Natural Language :: English | |
311 | Classifier: Programming Language :: Python :: 3 :: Only | |
312 | Classifier: Programming Language :: Python :: 3 | |
313 | Classifier: Programming Language :: Python :: 3.6 | |
314 | Classifier: Programming Language :: Python :: 3.7 | |
315 | Classifier: Programming Language :: Python :: 3.8 | |
316 | Classifier: Programming Language :: Python :: 3.9 | |
317 | Description-Content-Type: text/markdown |
0 | LICENSE | |
1 | MANIFEST.in | |
2 | README.md | |
3 | pyproject.toml | |
4 | requirements-dev.txt | |
5 | setup.cfg | |
6 | setup.py | |
7 | django_sass/__init__.py | |
8 | django_sass/apps.py | |
9 | django_sass.egg-info/PKG-INFO | |
10 | django_sass.egg-info/SOURCES.txt | |
11 | django_sass.egg-info/dependency_links.txt | |
12 | django_sass.egg-info/requires.txt | |
13 | django_sass.egg-info/top_level.txt | |
14 | django_sass/management/commands/sass.py⏎ |
0 | django_sass |
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 | DJANGO_SETTINGS_MODULE = testproject.settings | |
9 | django_settings_module = testproject.settings | |
9 | 10 | junit_family = xunit2 |
10 | 11 | addopts = --cov django_sass --cov-report html --cov-report xml --junitxml junit/test-results.xml ./testproject/ |
11 | 12 | python_files = tests.py test_*.py |
12 | filterwarnings = | |
13 | ignore | |
14 | default:::django_sass.* | |
13 | filterwarnings = | |
14 | ignore | |
15 | default:::django_sass.* | |
16 | ||
17 | [egg_info] | |
18 | tag_build = | |
19 | tag_date = 0 | |
20 |
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 | ) |
0 | // app1/scss/_include.scss | |
1 | @import "app1/scss/include"; | |
2 | ||
3 | // app2/_samedir.scss | |
4 | @import "app2/scss/samedir"; | |
5 | ||
6 | // app2/subdir/_subdir.scss | |
7 | @import "app2/scss/subdir/subdir"; | |
8 | ||
9 | // _samedir.scss | |
10 | @import "samedir"; | |
11 | ||
12 | // subdir/_subdir.scss | |
13 | @import "subdir/subdir"; | |
14 | ||
15 | // test.scss | |
16 | /* Tests: app2/scss/test.scss */ | |
17 | .test { | |
18 | color: red; | |
19 | } |
0 | #!/usr/bin/env python | |
1 | """Django's command-line utility for administrative tasks.""" | |
2 | import os | |
3 | import sys | |
4 | ||
5 | ||
6 | def main(): | |
7 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testproject.settings') | |
8 | try: | |
9 | from django.core.management import execute_from_command_line | |
10 | except ImportError as exc: | |
11 | raise ImportError( | |
12 | "Couldn't import Django. Are you sure it's installed and " | |
13 | "available on your PYTHONPATH environment variable? Did you " | |
14 | "forget to activate a virtual environment?" | |
15 | ) from exc | |
16 | execute_from_command_line(sys.argv) | |
17 | ||
18 | ||
19 | if __name__ == '__main__': | |
20 | main() |
0 | """ | |
1 | Django settings for testproject project. | |
2 | ||
3 | Generated by 'django-admin startproject' using Django 2.2.1. | |
4 | ||
5 | For more information on this file, see | |
6 | https://docs.djangoproject.com/en/2.2/topics/settings/ | |
7 | ||
8 | For the full list of settings and their values, see | |
9 | https://docs.djangoproject.com/en/2.2/ref/settings/ | |
10 | """ | |
11 | ||
12 | import os | |
13 | ||
14 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) | |
15 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
16 | ||
17 | ||
18 | # Quick-start development settings - unsuitable for production | |
19 | # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ | |
20 | ||
21 | # SECURITY WARNING: keep the secret key used in production secret! | |
22 | SECRET_KEY = '-_wl=tq26(*wyvfza+ncg_436c53pu81d=07j62+vm5y2pc)f^' | |
23 | ||
24 | # SECURITY WARNING: don't run with debug turned on in production! | |
25 | DEBUG = True | |
26 | ||
27 | ALLOWED_HOSTS = [] | |
28 | ||
29 | ||
30 | # Application definition | |
31 | ||
32 | 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', | |
43 | ] | |
44 | ||
45 | 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', | |
53 | ] | |
54 | ||
55 | ROOT_URLCONF = 'testproject.urls' | |
56 | ||
57 | TEMPLATES = [ | |
58 | { | |
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', | |
68 | ], | |
69 | }, | |
70 | }, | |
71 | ] | |
72 | ||
73 | WSGI_APPLICATION = 'testproject.wsgi.application' | |
74 | ||
75 | ||
76 | # Database | |
77 | # https://docs.djangoproject.com/en/2.2/ref/settings/#databases | |
78 | ||
79 | DATABASES = { | |
80 | 'default': { | |
81 | 'ENGINE': 'django.db.backends.sqlite3', | |
82 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), | |
83 | } | |
84 | } | |
85 | ||
86 | ||
87 | # Internationalization | |
88 | # https://docs.djangoproject.com/en/2.2/topics/i18n/ | |
89 | ||
90 | LANGUAGE_CODE = 'en-us' | |
91 | ||
92 | TIME_ZONE = 'UTC' | |
93 | ||
94 | USE_I18N = True | |
95 | ||
96 | USE_L10N = True | |
97 | ||
98 | USE_TZ = True | |
99 | ||
100 | ||
101 | # Static files (CSS, JavaScript, Images) | |
102 | # https://docs.djangoproject.com/en/2.2/howto/static-files/ | |
103 | ||
104 | STATIC_URL = '/static/' |
0 | """testproject URL Configuration | |
1 | ||
2 | The `urlpatterns` list routes URLs to views. For more information please see: | |
3 | https://docs.djangoproject.com/en/2.2/topics/http/urls/ | |
4 | Examples: | |
5 | Function views | |
6 | 1. Add an import: from my_app import views | |
7 | 2. Add a URL to urlpatterns: path('', views.home, name='home') | |
8 | Class-based views | |
9 | 1. Add an import: from other_app.views import Home | |
10 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') | |
11 | Including another URLconf | |
12 | 1. Import the include() function: from django.urls import include, path | |
13 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) | |
14 | """ | |
15 | from django.contrib import admin | |
16 | from django.urls import path | |
17 | ||
18 | urlpatterns = [ | |
19 | path('admin/', admin.site.urls), | |
20 | ] |
0 | """ | |
1 | WSGI config for testproject project. | |
2 | ||
3 | It exposes the WSGI callable as a module-level variable named ``application``. | |
4 | ||
5 | For more information on this file, see | |
6 | https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/ | |
7 | """ | |
8 | ||
9 | import os | |
10 | ||
11 | from django.core.wsgi import get_wsgi_application | |
12 | ||
13 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testproject.settings') | |
14 | ||
15 | application = get_wsgi_application() |
0 | import os | |
1 | import shutil | |
2 | import subprocess | |
3 | import time | |
4 | import unittest | |
5 | ||
6 | from django_sass import find_static_paths, find_static_scss | |
7 | ||
8 | ||
9 | THIS_DIR = os.path.dirname(os.path.abspath(__file__)) | |
10 | ||
11 | ||
12 | class TestDjangoSass(unittest.TestCase): | |
13 | ||
14 | def setUp(self): | |
15 | self.outdir = os.path.join(THIS_DIR, "out") | |
16 | ||
17 | def tearDown(self): | |
18 | # Clean up output files | |
19 | shutil.rmtree(self.outdir, ignore_errors=True) | |
20 | ||
21 | def assert_output(self, real_outpath: str): | |
22 | # Verify that the output file exists. | |
23 | print(real_outpath) | |
24 | self.assertTrue(os.path.isfile(real_outpath)) | |
25 | ||
26 | # Verify that the file contains expected output from all sass files. | |
27 | with open(real_outpath, encoding="utf8") as f: | |
28 | 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) | |
33 | ||
34 | def test_find_static_paths(self): | |
35 | paths = find_static_paths() | |
36 | # Assert that it found both of our apps' static dirs. | |
37 | self.assertTrue(os.path.join(THIS_DIR, "app1", "static") in paths) | |
38 | self.assertTrue(os.path.join(THIS_DIR, "app2", "static") in paths) | |
39 | ||
40 | def test_find_static_sass(self): | |
41 | files = find_static_scss() | |
42 | # Assert that it found all of our scss files. | |
43 | self.assertTrue( | |
44 | os.path.join(THIS_DIR, "app1", "static", "app1", "scss", "_include.scss") in files) | |
45 | self.assertTrue( | |
46 | os.path.join(THIS_DIR, "app2", "static", "app2", "scss", "_samedir.scss") in files) | |
47 | self.assertTrue( | |
48 | os.path.join(THIS_DIR, "app2", "static", "app2", "scss", "test.scss") in files) | |
49 | self.assertTrue( | |
50 | os.path.join(THIS_DIR, "app2", "static", "app2", "scss", "subdir", "_subdir.scss") | |
51 | in files) | |
52 | ||
53 | def test_cli(self): | |
54 | # Input and output paths relative to django static dirs. | |
55 | inpath = os.path.join("app2", "static", "app2", "scss", "test.scss") | |
56 | 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) | |
65 | ||
66 | def test_cli_dir(self): | |
67 | # Input and output paths relative to django static dirs. | |
68 | inpath = os.path.join("app2", "static", "app2", "scss") | |
69 | outpath = self.outdir | |
70 | # Expected output path on filesystem. | |
71 | 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) | |
80 | ||
81 | def test_cli_srcmap(self): | |
82 | # Input and output paths relative to django static dirs. | |
83 | inpath = os.path.join("app2", "static", "app2", "scss", "test.scss") | |
84 | 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"))) | |
94 | ||
95 | @unittest.skip | |
96 | def test_cli_watch(self): | |
97 | # Input and output paths relative to django static dirs. | |
98 | inpath = os.path.join("app2", "static", "app2", "scss", "test.scss") | |
99 | outpath = os.path.join(self.outdir, "test.css") | |
100 | # Command to run | |
101 | cmd = ["python", "manage.py", "sass", "--watch", inpath, outpath] | |
102 | # Run the management command on testproject. | |
103 | proc = subprocess.Popen(cmd, cwd=THIS_DIR) | |
104 | time.sleep(0.5) | |
105 | # TODO: This test is not working. Do not know how to intentionally send | |
106 | # a KeyboardInterrupt to the subprocess without having unittest/pytest | |
107 | # immediately die when it sees the interrupt. | |
108 | try: | |
109 | proc.send_signal(subprocess.signal.CTRL_C_EVENT) | |
110 | except KeyboardInterrupt: | |
111 | # We actually want the keyboard interrupt. | |
112 | pass | |
113 | returncode = proc.wait() | |
114 | # Verify the process exited cleanly. | |
115 | self.assertEqual(returncode, 0) | |
116 | # Assert output is correct. | |
117 | self.assert_output(outpath) |