Skip to content

Commit 14bf6da

Browse files
committed
Python 3.8 support and better tooling.
- Reintroduced support for Python 3.8. Seems as I have to support than I want to. - Added justfile to ease management of package and dev environment.
1 parent 00eb222 commit 14bf6da

File tree

9 files changed

+117
-106
lines changed

9 files changed

+117
-106
lines changed

.github/workflows/publish.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: Publish
22

33
on:
4-
release:
4+
tag:
55
types:
66
- created
77

@@ -19,11 +19,17 @@ jobs:
1919
- name: Install dependencies
2020
run: |
2121
python -m pip install --upgrade pip
22-
python -m pip install flit
22+
python -m pip install flit mkdocs-material
2323
2424
- name: Publish
2525
run: |
2626
flit publish
2727
env:
2828
FLIT_USERNAME: __token__
2929
FLIT_PASSWORD: ${{ secrets.PYPI_TOKEN }}
30+
31+
- name: Build documentation
32+
run: mkdocs build
33+
34+
- name: Publish documentation
35+
run: mkdocs gh-deploy --force

.github/workflows/publish_docs.yml

Lines changed: 0 additions & 28 deletions
This file was deleted.

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
runs-on: ubuntu-latest
1212
strategy:
1313
matrix:
14-
python-version: ["3.9", "3.10", "3.11", "3.12"]
14+
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
1515

1616
steps:
1717
- uses: actions/checkout@v4

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## 2.12.0
4+
5+
- Reintroduced support for Python 3.8. Seems as I have to support than I want to.
6+
- Added justfile to ease management of package and dev environment.
7+
38
## 2.11.1
49

510
- Changed project tooling to uv, nox and flit.

justfile

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,29 @@ VENV_DIRNAME := ".venv"
3232

3333
# upgrade/install all dependencies defined in pyproject.toml
3434
@upgrade: create_venv
35-
$VENV_DIRNAME/bin/python3 -m pip install --upgrade pip uv; \
36-
$VENV_DIRNAME/bin/python3 -m uv pip install --upgrade \
35+
$VENV_DIRNAME/bin/python -m pip install --upgrade pip uv; \
36+
$VENV_DIRNAME/bin/python -m uv pip install --upgrade \
3737
--requirement pyproject.toml --all-extras -e .;
3838

3939
# run pre-commit rules on all files
4040
@lint *ARGS: create_venv
41-
$VENV_DIRNAME/bin/python3 -m pre_commit run {{ ARGS }} --all-files
41+
$VENV_DIRNAME/bin/python -m pre_commit run {{ ARGS }} --all-files
4242

4343
# run test suite
4444
@test *ARGS: create_venv
45-
$VENV_DIRNAME/bin/python3 -m coverage erase
46-
$VENV_DIRNAME/bin/python3 -m nox --force-venv-backend uv {{ ARGS }}
47-
$VENV_DIRNAME/bin/python3 -m coverage report
48-
$VENV_DIRNAME/bin/python3 -m coverage html
45+
$VENV_DIRNAME/bin/python -m coverage erase
46+
$VENV_DIRNAME/bin/python -m nox --force-venv-backend uv {{ ARGS }}
47+
$VENV_DIRNAME/bin/python -m coverage report
48+
$VENV_DIRNAME/bin/python -m coverage html
49+
50+
@build-docs:
51+
$VENV_DIRNAME/bin/python -m mkdocs build
52+
53+
@publish-docs: build-docs
54+
$VENV_DIRNAME/bin/python -m mkdocs gh-deploy --force
55+
56+
@build:
57+
$VENV_DIRNAME/bin/python -m flit build
58+
59+
@publish: build
60+
$VENV_DIRNAME/bin/python -m flit publish

noxfile.py

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,44 @@
11
# type: ignore
22

33
import nox
4+
import tomllib
5+
6+
with open("pyproject.toml", "rb") as f:
7+
project_data = tomllib.load(f)["project"]
8+
9+
# Read supported Python versions from pyproject.toml
10+
python_versions = []
11+
for classifier in project_data["classifiers"]:
12+
if "Programming Language" in classifier and "Python" in classifier:
13+
split_classifier = classifier.split("::")
14+
if len(split_classifier) == 3:
15+
python_versions.append(split_classifier[2].strip())
16+
17+
# Read supported Django versions from pyproject.toml
18+
django_versions = []
19+
for classifier in project_data["classifiers"]:
20+
if "Framework" in classifier and "Django" in classifier:
21+
split_classifier = classifier.split("::")
22+
if len(split_classifier) == 3:
23+
django_versions.append(split_classifier[2].strip())
24+
25+
invalid_combinations = [("3.8", "5.0"), ("3.9", "5.0"), ("3.11", "3.2"), ("3.12", "3.2")]
426

527

628
@nox.session()
729
@nox.parametrize(
830
"python, django",
931
[
1032
(python, tox_version)
11-
for python in ("3.9", "3.10", "3.11", "3.12")
12-
for tox_version in ("3.2", "4.2", "5.0")
13-
if (
14-
(python, tox_version) != ("3.9", "5.0")
15-
and (python, tox_version) != ("3.11", "3.2")
16-
and (python, tox_version) != ("3.12", "3.2")
17-
)
33+
for python in python_versions
34+
for tox_version in django_versions
35+
if (python, tox_version) not in invalid_combinations
1836
],
1937
)
2038
def run_testsuite(session, django):
2139
if session.venv_backend == "uv":
22-
session.install("-r", "pyproject.toml", "--extra", "django-extensions", "--extra", "dev", ".")
40+
session.install("-r", "pyproject.toml", "--extra", "test", ".")
2341
else:
24-
session.install(".[django-extensions,dev]")
42+
session.install(".[test]")
2543
session.install(f"django~={django}")
2644
session.run("pytest", "--cov", "--cov-append", "--cov-report=")

pyproject.toml

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ classifiers = [
1313
"Development Status :: 5 - Production/Stable",
1414
"Intended Audience :: Developers",
1515
"License :: OSI Approved :: MIT License",
16+
"Programming Language :: Python :: 3.8",
1617
"Programming Language :: Python :: 3.9",
1718
"Programming Language :: Python :: 3.10",
1819
"Programming Language :: Python :: 3.11",
@@ -26,25 +27,30 @@ classifiers = [
2627
"Framework :: Django :: 5.0",
2728
]
2829
dynamic = ["version"]
29-
requires-python = ">=3.9"
30+
requires-python = ">=3.8"
3031
dependencies = [
3132
"django>=3.2",
3233
"certifi>=2023.7.22,<2025.0.0",
3334
"django-typer>=1.1.2",
3435
]
3536
[project.optional-dependencies]
36-
dev = [
37-
"django-types",
38-
"coverage[toml]",
37+
django-extensions = ["django-extensions>=3.2", "werkzeug>=3.0"]
38+
test = [
3939
"pytest",
4040
"pytest-django",
4141
"pytest-cov",
4242
"pytest-mock",
43+
"pytest-randomly",
44+
"django_tailwind_cli[django-extensions]",
45+
]
46+
dev = [
47+
"django-types",
4348
"nox",
4449
"pre-commit",
50+
"mkdocs-material",
51+
"flit",
52+
"django_tailwind_cli[test]",
4553
]
46-
docs = ["mkdocs-material"]
47-
django-extensions = ["django-extensions>=3.2", "werkzeug>=3.0"]
4854

4955
[project.urls]
5056
Home = "https://django-tailwind-cli.andrich.me/"
@@ -53,12 +59,13 @@ Repository = "https://github.com/oliverandrich/django-tailwind-cli"
5359

5460
# Black
5561
[tool.black]
56-
target-version = ["py39"]
62+
target-version = ["py38"]
5763
line-length = 120
5864
skip-string-normalization = true
5965
exclude = '''
6066
/(
6167
\.git
68+
| \.nox
6269
| \.tox
6370
| \.venv
6471
| _build
@@ -77,7 +84,7 @@ ignore = ["./tests/**/*"]
7784

7885
# Ruff
7986
[tool.ruff]
80-
target-version = "py39"
87+
target-version = "py38"
8188
line-length = 120
8289
select = [
8390
"A", # flake8-builtins
@@ -128,11 +135,8 @@ markers = ["mock_network_and_subprocess"]
128135

129136
# Coverage
130137
[tool.coverage.run]
131-
source = ["django_tailwind_cli", "tests"]
138+
source = ["django_tailwind_cli"]
132139
branch = true
133140

134-
[tool.coverage.paths]
135-
source = ["src", ".tox/**/site-packages"]
136-
137141
[tool.coverage.report]
138142
exclude_lines = ["no cov", "if __name__ == .__main__.:", "if TYPE_CHECKING:"]

src/django_tailwind_cli/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "2.11.1"
1+
__version__ = "2.12.0"

src/django_tailwind_cli/management/commands/tailwind.py

Lines changed: 40 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
import urllib.request
1010
from multiprocessing import Process
1111
from pathlib import Path
12-
from typing import Annotated, List, Optional, Union
12+
from typing import List, Optional, Union
1313

1414
import certifi
15+
import typer
1516
from django.conf import settings
1617
from django.core.management.base import CommandError
1718
from django.template.utils import get_app_template_dirs
1819
from django_typer import TyperCommand, command, initialize
19-
from typer import Argument, Option
2020

2121
from django_tailwind_cli.utils import Config
2222

@@ -104,15 +104,15 @@ def list_templates(self):
104104
@command(help="Start the Django development server and the Tailwind CLI in watch mode.")
105105
def runserver(
106106
self,
107-
addrport: Annotated[Optional[str], Argument(help="Optional port number, or ipaddr:port")] = None,
107+
addrport: Optional[str] = typer.Argument(None, help="Optional port number, or ipaddr:port"),
108108
*,
109-
use_ipv6: Annotated[bool, Option("--ipv6", "-6", help="Tells Django to use an IPv6 address.")] = False,
110-
no_threading: Annotated[bool, Option("--nothreading", help="Tells Django to NOT use threading.")] = False,
111-
no_static: Annotated[
112-
bool, Option("--nostatic", help="Tells Django to NOT automatically serve static files at STATIC_URL.")
113-
] = False,
114-
no_reloader: Annotated[bool, Option("--noreload", help="Tells Django to NOT use the auto-reloader.")] = False,
115-
skip_checks: Annotated[bool, Option("--skip-checks", help="Skip system checks.")] = False,
109+
use_ipv6: bool = typer.Option(False, "--ipv6", "-6", help="Tells Django to use an IPv6 address."),
110+
no_threading: bool = typer.Option(False, "--nothreading", help="Tells Django to NOT use threading."),
111+
no_static: bool = typer.Option(
112+
False, "--nostatic", help="Tells Django to NOT automatically serve static files at STATIC_URL."
113+
),
114+
no_reloader: bool = typer.Option(False, "--noreload", help="Tells Django to NOT use the auto-reloader."),
115+
skip_checks: bool = typer.Option(False, "--skip-checks", help="Skip system checks."),
116116
):
117117
debug_server_cmd = [sys.executable, "manage.py", "runserver"]
118118

@@ -137,40 +137,36 @@ def runserver(
137137
)
138138
def runserver_plus(
139139
self,
140-
addrport: Annotated[Optional[str], Argument(help="Optional port number, or ipaddr:port")] = None,
140+
addrport: Optional[str] = typer.Argument(None, help="Optional port number, or ipaddr:port"),
141141
*,
142-
use_ipv6: Annotated[bool, Option("--ipv6", "-6", help="Tells Django to use an IPv6 address.")] = False,
143-
no_threading: Annotated[bool, Option("--nothreading", help="Tells Django to NOT use threading.")] = False,
144-
no_static: Annotated[
145-
bool, Option("--nostatic", help="Tells Django to NOT automatically serve static files at STATIC_URL.")
146-
] = False,
147-
no_reloader: Annotated[bool, Option("--noreload", help="Tells Django to NOT use the auto-reloader.")] = False,
148-
skip_checks: Annotated[bool, Option("--skip-checks", help="Skip system checks.")] = False,
149-
pdb: Annotated[bool, Option("--pdb", help="Drop into pdb shell at the start of any view.")] = False,
150-
ipdb: Annotated[bool, Option("--ipdb", help="Drop into ipdb shell at the start of any view.")] = False,
151-
pm: Annotated[bool, Option("--pm", help="Drop into (i)pdb shell if an exception is raised in a view.")] = False,
152-
print_sql: Annotated[bool, Option("--print-sql", help="Print SQL queries as they're executed.")] = False,
153-
print_sql_location: Annotated[
154-
bool, Option("--print-sql-location", help="Show location in code where SQL query generated from.")
155-
] = False,
156-
cert_file: Annotated[
157-
Optional[str],
158-
Option(
159-
help=(
160-
"SSL .crt file path. If not provided path from --key-file will be selected. Either --cert-file or "
161-
"--key-file must be provided to use SSL."
162-
)
142+
use_ipv6: bool = typer.Option(False, "--ipv6", "-6", help="Tells Django to use an IPv6 address."),
143+
no_threading: bool = typer.Option(False, "--nothreading", help="Tells Django to NOT use threading."),
144+
no_static: bool = typer.Option(
145+
False, "--nostatic", help="Tells Django to NOT automatically serve static files at STATIC_URL."
146+
),
147+
no_reloader: bool = typer.Option(False, "--noreload", help="Tells Django to NOT use the auto-reloader."),
148+
skip_checks: bool = typer.Option(False, "--skip-checks", help="Skip system checks."),
149+
pdb: bool = typer.Option(False, "--pdb", help="Drop into pdb shell at the start of any view."),
150+
ipdb: bool = typer.Option(False, "--ipdb", help="Drop into ipdb shell at the start of any view."),
151+
pm: bool = typer.Option(False, "--pm", help="Drop into (i)pdb shell if an exception is raised in a view."),
152+
print_sql: bool = typer.Option(False, "--print-sql", help="Print SQL queries as they're executed."),
153+
print_sql_location: bool = typer.Option(
154+
False, "--print-sql-location", help="Show location in code where SQL query generated from."
155+
),
156+
cert_file: Optional[str] = typer.Option(
157+
None,
158+
help=(
159+
"SSL .crt file path. If not provided path from --key-file will be selected. Either --cert-file or "
160+
"--key-file must be provided to use SSL."
163161
),
164-
] = None,
165-
key_file: Annotated[
166-
Optional[str],
167-
Option(
168-
help=(
169-
"SSL .key file path. If not provided path from --cert-file will be "
170-
"selected. Either --cert-file or --key-file must be provided to use SSL."
171-
)
162+
),
163+
key_file: Optional[str] = typer.Option(
164+
None,
165+
help=(
166+
"SSL .key file path. If not provided path from --cert-file will be "
167+
"selected. Either --cert-file or --key-file must be provided to use SSL."
172168
),
173-
] = None,
169+
),
174170
):
175171
if not importlib.util.find_spec("django_extensions") and not importlib.util.find_spec("werkzeug"):
176172
msg = (
@@ -254,11 +250,9 @@ def download_cli(self) -> None:
254250
self._write_success(f"Downloading Tailwind CSS CLI from '{download_url}'")
255251
dest_file.parent.mkdir(parents=True, exist_ok=True)
256252
certifi_context = ssl.create_default_context(cafile=certifi.where())
257-
with (
258-
urllib.request.urlopen(download_url, context=certifi_context) as source,
259-
dest_file.open(mode="wb") as dest, # noqa: S310
260-
):
261-
shutil.copyfileobj(source, dest)
253+
with urllib.request.urlopen(download_url, context=certifi_context) as source:
254+
with dest_file.open(mode="wb") as dest:
255+
shutil.copyfileobj(source, dest)
262256
# make cli executable
263257
dest_file.chmod(0o755)
264258
self._write_success(f"Downloaded Tailwind CSS CLI to '{dest_file}'")

0 commit comments

Comments
 (0)