New Upstream Release - django-sass

Ready changes

Summary

Merged new upstream version: 1.1.0 (was: 1.0.0).

Resulting package

Built on 2022-12-31T01:03 (took 2m43s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases python3-django-sass

Lintian Result

Diff

diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..02896a1
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,17 @@
+# EditorConfig is awesome: https://EditorConfig.org
+
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_style = space
+insert_final_newline = true
+max_line_length = 80
+trim_trailing_whitespace = true
+
+[*.{html,css,js,json,xml,yaml,yml}]
+indent_size = 2
+
+[*.{md,ps1,sh,py,rst}]
+indent_size = 4
diff --git a/README.md b/README.md
index d677dcd..895df4f 100644
--- a/README.md
+++ b/README.md
@@ -12,8 +12,8 @@ Status
 
 |                        |                      |
 |------------------------|----------------------|
-| 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/) |
-| 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) |
+| 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/) |
+| 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) |
 
 
 Installation
@@ -93,7 +93,8 @@ In your Django HTML template, reference the CSS file as normal:
 
 ✨✨ **Congratulations, you are now a Django + Sass developer!** ✨✨
 
-Now you can commit those CSS files to version control, or run `collectstatic` and deploy them as normal.
+Now you can commit those CSS files to version control, or run `collectstatic`
+and deploy them as normal.
 
 For an example project layout, see `testproject/` in this repository.
 
@@ -101,8 +102,8 @@ For an example project layout, see `testproject/` in this repository.
 Watch Mode
 ----------
 
-To have `django-sass` watch files and recompile them as they change (useful in development),
-add the ``--watch`` flag.
+To have `django-sass` watch files and recompile them as they change (useful in
+development), add the ``--watch`` flag.
 
 ```
 python manage.py sass app2/static/app2/scss/ app2/static/app2/css/ --watch
@@ -133,10 +134,11 @@ And now proceed with deploying your files as normal.
 Limitations
 -----------
 
-* `@import` statements must reference a path relative to a path in `STATICFILES_FINDERS`
-  (which will usually be an app's `static/` directory or some other directory specified
-  in `STATICFILES_DIRS`). Or they can reference a relative path equal to or below the
-  current file. It does not support traversing up the filesystem (i.e. `../`).
+* `@import` statements must reference a path relative to a path in
+  `STATICFILES_FINDERS` (which will usually be an app's `static/` directory or
+  some other directory specified in `STATICFILES_DIRS`). Or they can reference a
+  relative path equal to or below the current file. It does not support
+  traversing up the filesystem (i.e. `../`).
 
   Legal imports:
   ```scss
@@ -149,33 +151,43 @@ Limitations
   @import '../file';
   ```
 
-* Only files ending in `.scss` are supported for now.
+* Only supports `-g`, `-p`, and `-t` options similar to `pysassc`. Ideally
+  `django-sass` will be as similar as possible to the `pysassc` command line
+  interface.
 
-* Only supports `-g`, `-p`, and `-t` options similar to `pysassc`. Ideally `django-sass` will
-  be as similar as possible to the `pysassc` command line interface.
-
-Feel free to file an issue or make a pull request to improve any of these limitations. 🐱‍💻
+Feel free to file an issue or make a pull request to improve any of these
+limitations. 🐱‍💻
 
 
 Why django-sass?
 ----------------
 
-Other packages such as [django-libsass](https://github.com/torchbox/django-libsass)
-and [django-sass-processor](https://github.com/jrief/django-sass-processor),
-while nice packages, require `django-compressor` which itself depends on several
-other packages that require compilation to install.
-
-* If you simply want to use Sass in development without installing a web of unwanted
-  dependencies, then `django-sass` is for you.
-* If you don't want to deploy any processors or compressors to your production server,
-  then `django-sass` is for you.
+Other packages such as
+[django-libsass](https://github.com/torchbox/django-libsass) and
+[django-sass-processor](https://github.com/jrief/django-sass-processor), while
+nice packages, require `django-compressor` which itself depends on several other
+packages that require compilation to install.
+
+Installing `django-compressor` in your production web server requires a LOT of
+extra bloat including a C compiler. It then will compile the Sass on-the-fly
+while rendering the HTML templates. This is a wasteful use of CPU on your web
+server.
+
+Instead, `django-sass` lets you compile the Sass locally on your machine
+*before* deploying, to reduce dependencies and CPU time on your production web
+server. This helps keep things fast and simple.
+
+* If you simply want to use Sass in development without installing a web of
+  unwanted dependencies, then `django-sass` is for you.
+* If you don't want to deploy any processors or compressors to your production
+  server, then `django-sass` is for you.
 * If you don't want to change the way you reference and serve static files,
   then `django-sass` is for you.
-* And if you want the absolute simplest installation and setup possible for doing Sass,
-  `django-sass` is for you too.
+* And if you want the absolute simplest installation and setup possible for
+  doing Sass, `django-sass` is for you too.
 
-django-sass only depends on libsass (which provides pre-built wheels for Windows, Mac,
-and Linux), and of course Django (any version).
+django-sass only depends on libsass (which provides pre-built wheels for
+Windows, Mac, and Linux), and of course Django (any version).
 
 
 Programmatically Compiling Sass
@@ -233,6 +245,7 @@ venv, then:
 Before committing, run static analysis tools:
 
 ```
+(myvenv)$ black .
 (myvenv)$ flake8
 (myvenv)$ mypy
 ```
@@ -247,17 +260,29 @@ Then run the unit tests:
 Changelog
 ---------
 
+#### 1.1.0
+* New: Now compiles `.sass` files as well as `.scss` files.
+* Fix bug when input path is a file and output path does not exist.
+
+#### 1.0.1
+* Maintanence release, no functional changes.
+* Add additional type hints within the codebase.
+* Tested against Django 3.1
+* Formatted code with `black`.
+
 #### 1.0.0
 * New: You can now use `django_sass` APIs directly in Python.
 * Added unit tests.
 * Code quality improvements.
 
 #### 0.2.0
-* New feature: `-g` option to build a source map (when input is a file, not a directory).
+* New feature: `-g` option to build a source map (when input is a file, not a
+  directory).
 
 #### 0.1.2
 * Fix: Write compiled CSS files as UTF-8.
-* Change: Default `-p` precision from 5 to 8 for better support building Bootstrap CSS.
+* Change: Default `-p` precision from 5 to 8 for better support building
+  Bootstrap CSS.
 
 #### 0.1.1
 * Fix: Create full file path if not exists when specifying a file output.
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 78ba6c6..5d5784f 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -15,7 +15,7 @@
 
 
 trigger:
-  - master
+  - main
 
 
 stages:
@@ -29,14 +29,14 @@ stages:
       vmImage: 'ubuntu-latest'
     strategy:
       matrix:
-        py3.5:
-          PYTHON_VERSION: '3.5'
         py3.6:
           PYTHON_VERSION: '3.6'
         py3.7:
           PYTHON_VERSION: '3.7'
         py3.8:
           PYTHON_VERSION: '3.8'
+        py3.9:
+          PYTHON_VERSION: '3.9'
 
     steps:
     - task: UsePythonVersion@0
@@ -80,7 +80,7 @@ stages:
     - task: UsePythonVersion@0
       displayName: 'Use Python version'
       inputs:
-        versionSpec: '3.8'
+        versionSpec: '3.9'
         architecture: 'x64'
 
     - script: python -m pip install -r requirements-dev.txt
@@ -89,6 +89,9 @@ stages:
     - script: flake8 .
       displayName: 'CR-QC: Static analysis (flake8)'
 
+    - script: black --check .
+      displayName: 'CR-QC: Format check'
+
     - script: mypy .
       displayName: 'CR-QC: Type check (mypy)'
 
diff --git a/ci/compare-codecov.ps1 b/ci/compare-codecov.ps1
index b1950b0..b1237b4 100644
--- a/ci/compare-codecov.ps1
+++ b/ci/compare-codecov.ps1
@@ -2,7 +2,7 @@
 
 <#
 .SYNOPSIS
-Compares code coverage percent of local coverage.xml file to master branch
+Compares code coverage percent of local coverage.xml file to main branch
 (Azure Pipeline API).
 
 .PARAMETER wd
@@ -26,7 +26,7 @@ with multiple pipelines.
 param(
     [string] $wd = (Get-Item (Split-Path $PSCommandPath -Parent)).Parent,
     [string] $org = "coderedcorp",
-    [string] $project = "coderedcms",
+    [string] $project = "cr-github",
     [string] $pipeline_name = "django-sass"
 )
 
@@ -41,25 +41,25 @@ $ApiBase = "https://dev.azure.com/$org/$project"
 
 
 # Get list of all recent builds.
-$masterBuildJson = (
-    Invoke-WebRequest "$ApiBase/_apis/build/builds?branchName=refs/heads/master&api-version=5.1"
+$mainBuildJson = (
+    Invoke-WebRequest "$ApiBase/_apis/build/builds?branchName=refs/heads/main&api-version=5.1"
 ).Content | ConvertFrom-Json
 
 # Get the latest matching build ID from the list of builds.
-foreach ($build in $masterBuildJson.value) {
+foreach ($build in $mainBuildJson.value) {
     if ($build.definition.name -eq $pipeline_name) {
-        $masterLatestId = $build.id
+        $mainLatestId = $build.id
         break
     }
 }
 
 # Retrieve code coverage for this build ID.
-$masterCoverageJson = (
-    Invoke-WebRequest "$ApiBase/_apis/test/codecoverage?buildId=$masterLatestId&api-version=5.1-preview.1"
+$mainCoverageJson = (
+    Invoke-WebRequest "$ApiBase/_apis/test/codecoverage?buildId=$mainLatestId&api-version=5.1-preview.1"
 ).Content | ConvertFrom-Json
-foreach ($cov in $masterCoverageJson.coverageData.coverageStats) {
+foreach ($cov in $mainCoverageJson.coverageData.coverageStats) {
     if ($cov.label -eq "Lines") {
-        $masterlinerate = [math]::Round(($cov.covered / $cov.total) * 100, 2)
+        $mainlinerate = [math]::Round(($cov.covered / $cov.total) * 100, 2)
     }
 }
 
@@ -84,21 +84,21 @@ $branchlinerate = [math]::Round([decimal]$BranchXML.coverage.'line-rate' * 100,
 
 
 Write-Output ""
-Write-Output "Master line coverage rate:  $masterlinerate%"
-Write-Output "Branch line coverage rate:  $branchlinerate%"
+Write-Output "Main coverage rate:    $mainlinerate%"
+Write-Output "Branch coverage rate:  $branchlinerate%"
 
-if ($masterlinerate -eq 0) {
+if ($mainlinerate -eq 0) {
     $change = "Infinite"
 }
 else {
-    $change = [math]::Abs($branchlinerate - $masterlinerate)
+    $change = [math]::Abs($branchlinerate - $mainlinerate)
 }
 
-if ($branchlinerate -gt $masterlinerate) {
+if ($branchlinerate -gt $mainlinerate) {
     Write-Host "Coverage increased by $change% 😀" -ForegroundColor Green
     exit 0
 }
-elseif ($branchlinerate -eq $masterlinerate) {
+elseif ($branchlinerate -eq $mainlinerate) {
     Write-Host "Coverage has not changed." -ForegroundColor Green
     exit 0
 }
diff --git a/debian/changelog b/debian/changelog
index c0188d2..c0cc7c6 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+django-sass (1.1.0-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Sat, 31 Dec 2022 01:00:46 -0000
+
 django-sass (1.0.0-3) unstable; urgency=medium
 
   [ Debian Janitor ]
diff --git a/django_sass/__init__.py b/django_sass/__init__.py
index df40a21..2811849 100644
--- a/django_sass/__init__.py
+++ b/django_sass/__init__.py
@@ -24,28 +24,34 @@ def find_static_paths() -> List[str]:
 
 def find_static_scss() -> List[str]:
     """
-    Finds all static scss files available in this Django project.
+    Finds all static scss/sass files available in this Django project.
 
     :returns:
-        List of paths of static scss files.
+        List of paths of static scss/sass files.
     """
     scss_files = []
     for finder in get_finders():
         for path, storage in finder.list([]):
-            if path.endswith(".scss"):
+            if path.endswith(".scss") or path.endswith(".sass"):
                 fullpath = finder.find(path)
                 scss_files.append(fullpath)
     return scss_files
 
 
-def compile_sass(inpath: str, outpath: str, output_style: str = None, precision: int = None,
-                 source_map: bool = False, include_paths: List[str] = None) -> None:
+def compile_sass(
+    inpath: str,
+    outpath: str,
+    output_style: str = None,
+    precision: int = None,
+    source_map: bool = False,
+    include_paths: List[str] = None,
+) -> None:
     """
     Calls sass.compile() within context of Django's known static file paths,
     and writes output CSS and/or sourcemaps to file.
 
     :param str inpath:
-        Path to SCSS file or directory of SCSS files.
+        Path to SCSS/Sass file or directory of SCSS/Sass files.
     :param str outpath:
         Path to a CSS file or directory in which to write output. The path will
         be created if it does not exist.
@@ -81,13 +87,27 @@ def compile_sass(inpath: str, outpath: str, output_style: str = None, precision:
     # Handle input files.
     outfile = None
     if os.path.isfile(inpath):
+
         sassargs.update({"filename": inpath})
-        if os.path.isdir(outpath):
+
+        # If outpath does not exist, guess if it should be a dir and create it.
+        if not os.path.exists(outpath):
+            if not outpath.endswith(".css"):
+                os.makedirs(outpath)
+
+        # If outpath is a directory, create a child file.
+        # Otherwise use provided file path.
+        if os.path.exists(outpath) and os.path.isdir(outpath):
             outfile = os.path.join(
-                outpath, os.path.basename(inpath.replace(".scss", ".css"))
+                outpath,
+                os.path.basename(
+                    inpath.replace(".scss", ".css").replace(".sass", ".css")
+                ),
             )
         else:
             outfile = outpath
+
+        # Create source map if specified.
         if source_map:
             sassargs.update({"source_map_filename": outfile + ".map"})
 
diff --git a/django_sass/apps.py b/django_sass/apps.py
index d0c0d32..c14ae0f 100644
--- a/django_sass/apps.py
+++ b/django_sass/apps.py
@@ -2,4 +2,4 @@ from django.apps import AppConfig
 
 
 class DjangoSassConfig(AppConfig):
-    name = 'django_sass'
+    name = "django_sass"
diff --git a/django_sass/management/commands/sass.py b/django_sass/management/commands/sass.py
index f19d67c..566376a 100644
--- a/django_sass/management/commands/sass.py
+++ b/django_sass/management/commands/sass.py
@@ -1,3 +1,4 @@
+from typing import Dict
 import os
 import sys
 import time
@@ -71,7 +72,7 @@ class Command(BaseCommand):
                 self.stdout.write("Watching...")
 
                 # Track list of files to watch and their modified time.
-                watchfiles = {}
+                watchfiles = {}  # type: Dict[str, float]
                 while True:
                     needs_updated = False
 
@@ -94,7 +95,9 @@ class Command(BaseCommand):
                                 precision=o_precision,
                                 source_map=o_srcmap,
                             )
-                            self.stdout.write("Updated files at %s" % time.time())
+                            self.stdout.write(
+                                "Updated files at %s" % time.time()
+                            )
                         except sass.CompileError as exc:
                             self.stdout.write(str(exc))
 
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..b1627ff
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,11 @@
+[tool.black]
+line-length = 80
+target-version = ['py36', 'py37', 'py38']
+# Regular expression of files to exclude.
+exclude = '''
+/(
+    .venv
+    | venv
+    | migrations
+)/
+'''
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 04040a6..42a3c6e 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,4 +1,5 @@
 -e ./
+black
 flake8
 mypy
 pytest
diff --git a/setup.cfg b/setup.cfg
index 183fd4d..35aea07 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,9 +1,10 @@
 [flake8]
 max-line-length = 100
-exclude = migrations
+exclude = .venv,venv,migrations
 
 [mypy]
 ignore_missing_imports = True
+namespace_packages = True
 
 [tool:pytest]
 DJANGO_SETTINGS_MODULE = testproject.settings
diff --git a/setup.py b/setup.py
index 01cb41a..8e4622f 100644
--- a/setup.py
+++ b/setup.py
@@ -2,17 +2,21 @@ import os
 from setuptools import setup, find_packages
 
 
-with open(os.path.join(os.path.dirname(__file__), "README.md"), encoding="utf8") as readme:
+with open(
+    os.path.join(os.path.dirname(__file__), "README.md"), encoding="utf8"
+) as readme:
     README = readme.read()
 
 setup(
     name="django-sass",
-    version="1.0.0",
+    version="1.1.0",
     author="CodeRed LLC",
     author_email="info@coderedcorp.com",
     url="https://github.com/coderedcorp/django-sass",
-    description=("The absolute simplest way to use Sass with Django. Pure Python, "
-                 "minimal dependencies, and no special configuration required!"),
+    description=(
+        "The absolute simplest way to use Sass with Django. Pure Python, "
+        "minimal dependencies, and no special configuration required!"
+    ),
     long_description=README,
     long_description_content_type="text/markdown",
     license="BSD license",
@@ -20,19 +24,24 @@ setup(
     packages=find_packages(),
     install_requires=[
         "django",
-        "libsass"
+        "libsass",
     ],
     classifiers=[
-        "Intended Audience :: Developers",
-        "License :: OSI Approved :: BSD License",
-        "Natural Language :: English",
-        "Programming Language :: Python :: 3",
-        "Programming Language :: Python :: 3 :: Only",
-        "Framework :: Django",
+        "Environment :: Web Environment",
         "Framework :: Django :: 2.0",
         "Framework :: Django :: 2.1",
         "Framework :: Django :: 2.2",
         "Framework :: Django :: 3.0",
-        "Environment :: Web Environment",
+        "Framework :: Django :: 3.1",
+        "Framework :: Django",
+        "Intended Audience :: Developers",
+        "License :: OSI Approved :: BSD License",
+        "Natural Language :: English",
+        "Programming Language :: Python :: 3 :: Only",
+        "Programming Language :: Python :: 3",
+        "Programming Language :: Python :: 3.6",
+        "Programming Language :: Python :: 3.7",
+        "Programming Language :: Python :: 3.8",
+        "Programming Language :: Python :: 3.9",
     ],
 )
diff --git a/testproject/app1/apps.py b/testproject/app1/apps.py
index 1824a7b..708ee94 100644
--- a/testproject/app1/apps.py
+++ b/testproject/app1/apps.py
@@ -2,4 +2,4 @@ from django.apps import AppConfig
 
 
 class App1Config(AppConfig):
-    name = 'app1'
+    name = "app1"
diff --git a/testproject/app2/apps.py b/testproject/app2/apps.py
index 008e3c5..8ecc489 100644
--- a/testproject/app2/apps.py
+++ b/testproject/app2/apps.py
@@ -2,4 +2,4 @@ from django.apps import AppConfig
 
 
 class App2Config(AppConfig):
-    name = 'app2'
+    name = "app2"
diff --git a/testproject/app3/__init__.py b/testproject/app3/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/testproject/app3/apps.py b/testproject/app3/apps.py
new file mode 100644
index 0000000..a5369ea
--- /dev/null
+++ b/testproject/app3/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class App3Config(AppConfig):
+    name = "app3"
diff --git a/testproject/app3/static/app3/sass/indent_test.sass b/testproject/app3/static/app3/sass/indent_test.sass
new file mode 100644
index 0000000..d6a9233
--- /dev/null
+++ b/testproject/app3/static/app3/sass/indent_test.sass
@@ -0,0 +1,4 @@
+/* Tests: app3/sass/indent_test.sass */
+
+.test_item
+    border: 1px solid red
diff --git a/testproject/manage.py b/testproject/manage.py
index fb52456..ee87d59 100644
--- a/testproject/manage.py
+++ b/testproject/manage.py
@@ -5,7 +5,7 @@ import sys
 
 
 def main():
-    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testproject.settings')
+    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "testproject.settings")
     try:
         from django.core.management import execute_from_command_line
     except ImportError as exc:
@@ -17,5 +17,5 @@ def main():
     execute_from_command_line(sys.argv)
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
diff --git a/testproject/testproject/settings.py b/testproject/testproject/settings.py
index 088ec85..591afee 100644
--- a/testproject/testproject/settings.py
+++ b/testproject/testproject/settings.py
@@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/2.2/ref/settings/
 """
 
 import os
+from typing import List
 
 # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -20,67 +21,67 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
 
 # SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = '-_wl=tq26(*wyvfza+ncg_436c53pu81d=07j62+vm5y2pc)f^'
+SECRET_KEY = "-_wl=tq26(*wyvfza+ncg_436c53pu81d=07j62+vm5y2pc)f^"
 
 # SECURITY WARNING: don't run with debug turned on in production!
 DEBUG = True
 
-ALLOWED_HOSTS = []
+ALLOWED_HOSTS = []  # type: List[str]
 
 
 # Application definition
 
 INSTALLED_APPS = [
-    'app1',
-    'app2',
-    'django_sass',
-
-    'django.contrib.admin',
-    'django.contrib.auth',
-    'django.contrib.contenttypes',
-    'django.contrib.sessions',
-    'django.contrib.messages',
-    'django.contrib.staticfiles',
+    "app1",
+    "app2",
+    "app3",
+    "django_sass",
+    "django.contrib.admin",
+    "django.contrib.auth",
+    "django.contrib.contenttypes",
+    "django.contrib.sessions",
+    "django.contrib.messages",
+    "django.contrib.staticfiles",
 ]
 
 MIDDLEWARE = [
-    'django.middleware.security.SecurityMiddleware',
-    'django.contrib.sessions.middleware.SessionMiddleware',
-    'django.middleware.common.CommonMiddleware',
-    'django.middleware.csrf.CsrfViewMiddleware',
-    'django.contrib.auth.middleware.AuthenticationMiddleware',
-    'django.contrib.messages.middleware.MessageMiddleware',
-    'django.middleware.clickjacking.XFrameOptionsMiddleware',
+    "django.middleware.security.SecurityMiddleware",
+    "django.contrib.sessions.middleware.SessionMiddleware",
+    "django.middleware.common.CommonMiddleware",
+    "django.middleware.csrf.CsrfViewMiddleware",
+    "django.contrib.auth.middleware.AuthenticationMiddleware",
+    "django.contrib.messages.middleware.MessageMiddleware",
+    "django.middleware.clickjacking.XFrameOptionsMiddleware",
 ]
 
-ROOT_URLCONF = 'testproject.urls'
+ROOT_URLCONF = "testproject.urls"
 
 TEMPLATES = [
     {
-        'BACKEND': 'django.template.backends.django.DjangoTemplates',
-        'DIRS': [],
-        'APP_DIRS': True,
-        'OPTIONS': {
-            'context_processors': [
-                'django.template.context_processors.debug',
-                'django.template.context_processors.request',
-                'django.contrib.auth.context_processors.auth',
-                'django.contrib.messages.context_processors.messages',
+        "BACKEND": "django.template.backends.django.DjangoTemplates",
+        "DIRS": [],
+        "APP_DIRS": True,
+        "OPTIONS": {
+            "context_processors": [
+                "django.template.context_processors.debug",
+                "django.template.context_processors.request",
+                "django.contrib.auth.context_processors.auth",
+                "django.contrib.messages.context_processors.messages",
             ],
         },
     },
 ]
 
-WSGI_APPLICATION = 'testproject.wsgi.application'
+WSGI_APPLICATION = "testproject.wsgi.application"
 
 
 # Database
 # https://docs.djangoproject.com/en/2.2/ref/settings/#databases
 
 DATABASES = {
-    'default': {
-        'ENGINE': 'django.db.backends.sqlite3',
-        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+    "default": {
+        "ENGINE": "django.db.backends.sqlite3",
+        "NAME": os.path.join(BASE_DIR, "db.sqlite3"),
     }
 }
 
@@ -88,9 +89,9 @@ DATABASES = {
 # Internationalization
 # https://docs.djangoproject.com/en/2.2/topics/i18n/
 
-LANGUAGE_CODE = 'en-us'
+LANGUAGE_CODE = "en-us"
 
-TIME_ZONE = 'UTC'
+TIME_ZONE = "UTC"
 
 USE_I18N = True
 
@@ -102,4 +103,4 @@ USE_TZ = True
 # Static files (CSS, JavaScript, Images)
 # https://docs.djangoproject.com/en/2.2/howto/static-files/
 
-STATIC_URL = '/static/'
+STATIC_URL = "/static/"
diff --git a/testproject/testproject/urls.py b/testproject/testproject/urls.py
index 0c86156..919da34 100644
--- a/testproject/testproject/urls.py
+++ b/testproject/testproject/urls.py
@@ -17,5 +17,5 @@ from django.contrib import admin
 from django.urls import path
 
 urlpatterns = [
-    path('admin/', admin.site.urls),
+    path("admin/", admin.site.urls),
 ]
diff --git a/testproject/testproject/wsgi.py b/testproject/testproject/wsgi.py
index bee623a..2b24860 100644
--- a/testproject/testproject/wsgi.py
+++ b/testproject/testproject/wsgi.py
@@ -11,6 +11,6 @@ import os
 
 from django.core.wsgi import get_wsgi_application
 
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testproject.settings')
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "testproject.settings")
 
 application = get_wsgi_application()
diff --git a/testproject/tests.py b/testproject/tests.py
index b113064..892d5db 100644
--- a/testproject/tests.py
+++ b/testproject/tests.py
@@ -3,15 +3,22 @@ import shutil
 import subprocess
 import time
 import unittest
+from typing import List
 
 from django_sass import find_static_paths, find_static_scss
 
 
 THIS_DIR = os.path.dirname(os.path.abspath(__file__))
 
+SCSS_CONTAINS = [
+    "/* Tests: app1/scss/_include.scss */",
+    "/* Tests: app2/scss/_samedir.scss */",
+    "/* Tests: app2/scss/subdir/_subdir.scss */",
+    "/* Tests: app2/scss/test.scss */",
+]
 
-class TestDjangoSass(unittest.TestCase):
 
+class TestDjangoSass(unittest.TestCase):
     def setUp(self):
         self.outdir = os.path.join(THIS_DIR, "out")
 
@@ -19,18 +26,29 @@ class TestDjangoSass(unittest.TestCase):
         # Clean up output files
         shutil.rmtree(self.outdir, ignore_errors=True)
 
-    def assert_output(self, real_outpath: str):
+    def assert_output(
+        self,
+        inpath: str,
+        outpath: str,
+        real_outpath: str,
+        contains: List[str],
+        args: List[str] = None,
+    ):
+        # Command to run
+        args = args or []
+        cmd = ["python", "manage.py", "sass", *args, inpath, outpath]
+        # Run the management command on testproject.
+        proc = subprocess.run(cmd, cwd=THIS_DIR)
+        # Verify the process exited cleanly.
+        self.assertEqual(proc.returncode, 0)
         # Verify that the output file exists.
-        print(real_outpath)
-        self.assertTrue(os.path.isfile(real_outpath))
+        # self.assertTrue(os.path.isfile(real_outpath))
 
         # Verify that the file contains expected output from all sass files.
         with open(real_outpath, encoding="utf8") as f:
             contents = f.read()
-            self.assertTrue("/* Tests: app1/scss/_include.scss */" in contents)
-            self.assertTrue("/* Tests: app2/scss/_samedir.scss */" in contents)
-            self.assertTrue("/* Tests: app2/scss/subdir/_subdir.scss */" in contents)
-            self.assertTrue("/* Tests: app2/scss/test.scss */" in contents)
+            for compiled_data in contains:
+                self.assertTrue(compiled_data in contents)
 
     def test_find_static_paths(self):
         paths = find_static_paths()
@@ -42,58 +60,106 @@ class TestDjangoSass(unittest.TestCase):
         files = find_static_scss()
         # Assert that it found all of our scss files.
         self.assertTrue(
-            os.path.join(THIS_DIR, "app1", "static", "app1", "scss", "_include.scss") in files)
+            os.path.join(
+                THIS_DIR, "app1", "static", "app1", "scss", "_include.scss"
+            )
+            in files
+        )
         self.assertTrue(
-            os.path.join(THIS_DIR, "app2", "static", "app2", "scss", "_samedir.scss") in files)
+            os.path.join(
+                THIS_DIR, "app2", "static", "app2", "scss", "_samedir.scss"
+            )
+            in files
+        )
         self.assertTrue(
-            os.path.join(THIS_DIR, "app2", "static", "app2", "scss", "test.scss") in files)
+            os.path.join(
+                THIS_DIR, "app2", "static", "app2", "scss", "test.scss"
+            )
+            in files
+        )
         self.assertTrue(
-            os.path.join(THIS_DIR, "app2", "static", "app2", "scss", "subdir", "_subdir.scss")
-            in files)
+            os.path.join(
+                THIS_DIR,
+                "app2",
+                "static",
+                "app2",
+                "scss",
+                "subdir",
+                "_subdir.scss",
+            )
+            in files
+        )
+        self.assertTrue(
+            os.path.join(
+                THIS_DIR, "app3", "static", "app3", "sass", "indent_test.sass"
+            )
+            in files
+        )
 
     def test_cli(self):
         # Input and output paths relative to django static dirs.
         inpath = os.path.join("app2", "static", "app2", "scss", "test.scss")
         outpath = os.path.join(self.outdir, "test_file.css")
-        # Command to run
-        cmd = ["python", "manage.py", "sass", inpath, outpath]
-        # Run the management command on testproject.
-        proc = subprocess.run(cmd, cwd=THIS_DIR)
-        # Verify the process exited cleanly.
-        self.assertEqual(proc.returncode, 0)
-        # Assert output is correct.
-        self.assert_output(outpath)
+        self.assert_output(
+            inpath=inpath,
+            outpath=outpath,
+            real_outpath=outpath,
+            contains=SCSS_CONTAINS,
+        )
 
     def test_cli_dir(self):
         # Input and output paths relative to django static dirs.
         inpath = os.path.join("app2", "static", "app2", "scss")
-        outpath = self.outdir
         # Expected output path on filesystem.
         real_outpath = os.path.join(self.outdir, "test.css")
-        # Command to run
-        cmd = ["python", "manage.py", "sass", inpath, outpath]
-        # Run the management command on testproject.
-        proc = subprocess.run(cmd, cwd=THIS_DIR)
-        # Verify the process exited cleanly.
-        self.assertEqual(proc.returncode, 0)
-        # Assert output is correct.
-        self.assert_output(real_outpath)
+        self.assert_output(
+            inpath=inpath,
+            outpath=self.outdir,
+            real_outpath=real_outpath,
+            contains=SCSS_CONTAINS,
+        )
+
+    def test_cli_infile_outdir(self):
+        # Input is a file; output is non-existant path (without .css extension).
+        inpath = os.path.join("app2", "static", "app2", "scss", "test.scss")
+        outpath = os.path.join(self.outdir, "does-not-exist")
+        # Expected output path on filesystem.
+        real_outpath = os.path.join(outpath, "test.css")
+        self.assert_output(
+            inpath=inpath,
+            outpath=outpath,
+            real_outpath=real_outpath,
+            contains=SCSS_CONTAINS,
+        )
+
+    def test_sass_compiles(self):
+        # Input and output paths relative to django static dirs.
+        inpath = os.path.join("app3", "static", "app3", "sass")
+        # Expected output path on filesystem.
+        real_outpath = os.path.join(self.outdir, "indent_test.css")
+        self.assert_output(
+            inpath=inpath,
+            outpath=self.outdir,
+            real_outpath=real_outpath,
+            contains=["/* Tests: app3/sass/indent_test.sass */"],
+        )
 
     def test_cli_srcmap(self):
         # Input and output paths relative to django static dirs.
         inpath = os.path.join("app2", "static", "app2", "scss", "test.scss")
         outpath = os.path.join(self.outdir, "test.css")
-        # Command to run
-        cmd = ["python", "manage.py", "sass", "-g", inpath, outpath]
-        # Run the management command on testproject.
-        proc = subprocess.run(cmd, cwd=THIS_DIR)
-        # Verify the process exited cleanly.
-        self.assertEqual(proc.returncode, 0)
-        # Assert output is correct.
-        self.assert_output(outpath)
-        self.assertTrue(os.path.isfile(os.path.join(self.outdir, "test.css.map")))
+        self.assert_output(
+            inpath=inpath,
+            outpath=outpath,
+            real_outpath=outpath,
+            contains=SCSS_CONTAINS,
+            args=["-g"],
+        )
+        self.assertTrue(
+            os.path.isfile(os.path.join(self.outdir, "test.css.map"))
+        )
 
-    @unittest.skip
+    @unittest.skip("Test needs fixed...")
     def test_cli_watch(self):
         # Input and output paths relative to django static dirs.
         inpath = os.path.join("app2", "static", "app2", "scss", "test.scss")

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/lib/python3/dist-packages/django_sass-1.1.0.egg-info/PKG-INFO
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/django_sass-1.1.0.egg-info/dependency_links.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/django_sass-1.1.0.egg-info/requires.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/django_sass-1.1.0.egg-info/top_level.txt

Files in first set of .debs but not in second

-rw-r--r--  root/root   /usr/lib/python3/dist-packages/django_sass-1.0.0.egg-info/PKG-INFO
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/django_sass-1.0.0.egg-info/dependency_links.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/django_sass-1.0.0.egg-info/requires.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/django_sass-1.0.0.egg-info/top_level.txt

No differences were encountered in the control files

More details

Full run details