Skip to content

Commit

Permalink
Python 3.8 support and better tooling.
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
oliverandrich committed May 13, 2024
1 parent 00eb222 commit 14bf6da
Show file tree
Hide file tree
Showing 9 changed files with 117 additions and 106 deletions.
10 changes: 8 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Publish

on:
release:
tag:
types:
- created

Expand All @@ -19,11 +19,17 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install flit
python -m pip install flit mkdocs-material
- name: Publish
run: |
flit publish
env:
FLIT_USERNAME: __token__
FLIT_PASSWORD: ${{ secrets.PYPI_TOKEN }}

- name: Build documentation
run: mkdocs build

- name: Publish documentation
run: mkdocs gh-deploy --force
28 changes: 0 additions & 28 deletions .github/workflows/publish_docs.yml

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v4
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 2.12.0

- 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.

## 2.11.1

- Changed project tooling to uv, nox and flit.
Expand Down
26 changes: 19 additions & 7 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,29 @@ VENV_DIRNAME := ".venv"

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

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

# run test suite
@test *ARGS: create_venv
$VENV_DIRNAME/bin/python3 -m coverage erase
$VENV_DIRNAME/bin/python3 -m nox --force-venv-backend uv {{ ARGS }}
$VENV_DIRNAME/bin/python3 -m coverage report
$VENV_DIRNAME/bin/python3 -m coverage html
$VENV_DIRNAME/bin/python -m coverage erase
$VENV_DIRNAME/bin/python -m nox --force-venv-backend uv {{ ARGS }}
$VENV_DIRNAME/bin/python -m coverage report
$VENV_DIRNAME/bin/python -m coverage html

@build-docs:
$VENV_DIRNAME/bin/python -m mkdocs build

@publish-docs: build-docs
$VENV_DIRNAME/bin/python -m mkdocs gh-deploy --force

@build:
$VENV_DIRNAME/bin/python -m flit build

@publish: build
$VENV_DIRNAME/bin/python -m flit publish
36 changes: 27 additions & 9 deletions noxfile.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,44 @@
# type: ignore

import nox
import tomllib

with open("pyproject.toml", "rb") as f:
project_data = tomllib.load(f)["project"]

# Read supported Python versions from pyproject.toml
python_versions = []
for classifier in project_data["classifiers"]:
if "Programming Language" in classifier and "Python" in classifier:
split_classifier = classifier.split("::")
if len(split_classifier) == 3:
python_versions.append(split_classifier[2].strip())

# Read supported Django versions from pyproject.toml
django_versions = []
for classifier in project_data["classifiers"]:
if "Framework" in classifier and "Django" in classifier:
split_classifier = classifier.split("::")
if len(split_classifier) == 3:
django_versions.append(split_classifier[2].strip())

invalid_combinations = [("3.8", "5.0"), ("3.9", "5.0"), ("3.11", "3.2"), ("3.12", "3.2")]


@nox.session()
@nox.parametrize(
"python, django",
[
(python, tox_version)
for python in ("3.9", "3.10", "3.11", "3.12")
for tox_version in ("3.2", "4.2", "5.0")
if (
(python, tox_version) != ("3.9", "5.0")
and (python, tox_version) != ("3.11", "3.2")
and (python, tox_version) != ("3.12", "3.2")
)
for python in python_versions
for tox_version in django_versions
if (python, tox_version) not in invalid_combinations
],
)
def run_testsuite(session, django):
if session.venv_backend == "uv":
session.install("-r", "pyproject.toml", "--extra", "django-extensions", "--extra", "dev", ".")
session.install("-r", "pyproject.toml", "--extra", "test", ".")
else:
session.install(".[django-extensions,dev]")
session.install(".[test]")
session.install(f"django~={django}")
session.run("pytest", "--cov", "--cov-append", "--cov-report=")
28 changes: 16 additions & 12 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
Expand All @@ -26,25 +27,30 @@ classifiers = [
"Framework :: Django :: 5.0",
]
dynamic = ["version"]
requires-python = ">=3.9"
requires-python = ">=3.8"
dependencies = [
"django>=3.2",
"certifi>=2023.7.22,<2025.0.0",
"django-typer>=1.1.2",
]
[project.optional-dependencies]
dev = [
"django-types",
"coverage[toml]",
django-extensions = ["django-extensions>=3.2", "werkzeug>=3.0"]
test = [
"pytest",
"pytest-django",
"pytest-cov",
"pytest-mock",
"pytest-randomly",
"django_tailwind_cli[django-extensions]",
]
dev = [
"django-types",
"nox",
"pre-commit",
"mkdocs-material",
"flit",
"django_tailwind_cli[test]",
]
docs = ["mkdocs-material"]
django-extensions = ["django-extensions>=3.2", "werkzeug>=3.0"]

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

# Black
[tool.black]
target-version = ["py39"]
target-version = ["py38"]
line-length = 120
skip-string-normalization = true
exclude = '''
/(
\.git
| \.nox
| \.tox
| \.venv
| _build
Expand All @@ -77,7 +84,7 @@ ignore = ["./tests/**/*"]

# Ruff
[tool.ruff]
target-version = "py39"
target-version = "py38"
line-length = 120
select = [
"A", # flake8-builtins
Expand Down Expand Up @@ -128,11 +135,8 @@ markers = ["mock_network_and_subprocess"]

# Coverage
[tool.coverage.run]
source = ["django_tailwind_cli", "tests"]
source = ["django_tailwind_cli"]
branch = true

[tool.coverage.paths]
source = ["src", ".tox/**/site-packages"]

[tool.coverage.report]
exclude_lines = ["no cov", "if __name__ == .__main__.:", "if TYPE_CHECKING:"]
2 changes: 1 addition & 1 deletion src/django_tailwind_cli/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "2.11.1"
__version__ = "2.12.0"
86 changes: 40 additions & 46 deletions src/django_tailwind_cli/management/commands/tailwind.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
import urllib.request
from multiprocessing import Process
from pathlib import Path
from typing import Annotated, List, Optional, Union
from typing import List, Optional, Union

import certifi
import typer
from django.conf import settings
from django.core.management.base import CommandError
from django.template.utils import get_app_template_dirs
from django_typer import TyperCommand, command, initialize
from typer import Argument, Option

from django_tailwind_cli.utils import Config

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

Expand All @@ -137,40 +137,36 @@ def runserver(
)
def runserver_plus(
self,
addrport: Annotated[Optional[str], Argument(help="Optional port number, or ipaddr:port")] = None,
addrport: Optional[str] = typer.Argument(None, help="Optional port number, or ipaddr:port"),
*,
use_ipv6: Annotated[bool, Option("--ipv6", "-6", help="Tells Django to use an IPv6 address.")] = False,
no_threading: Annotated[bool, Option("--nothreading", help="Tells Django to NOT use threading.")] = False,
no_static: Annotated[
bool, Option("--nostatic", help="Tells Django to NOT automatically serve static files at STATIC_URL.")
] = False,
no_reloader: Annotated[bool, Option("--noreload", help="Tells Django to NOT use the auto-reloader.")] = False,
skip_checks: Annotated[bool, Option("--skip-checks", help="Skip system checks.")] = False,
pdb: Annotated[bool, Option("--pdb", help="Drop into pdb shell at the start of any view.")] = False,
ipdb: Annotated[bool, Option("--ipdb", help="Drop into ipdb shell at the start of any view.")] = False,
pm: Annotated[bool, Option("--pm", help="Drop into (i)pdb shell if an exception is raised in a view.")] = False,
print_sql: Annotated[bool, Option("--print-sql", help="Print SQL queries as they're executed.")] = False,
print_sql_location: Annotated[
bool, Option("--print-sql-location", help="Show location in code where SQL query generated from.")
] = False,
cert_file: Annotated[
Optional[str],
Option(
help=(
"SSL .crt file path. If not provided path from --key-file will be selected. Either --cert-file or "
"--key-file must be provided to use SSL."
)
use_ipv6: bool = typer.Option(False, "--ipv6", "-6", help="Tells Django to use an IPv6 address."),
no_threading: bool = typer.Option(False, "--nothreading", help="Tells Django to NOT use threading."),
no_static: bool = typer.Option(
False, "--nostatic", help="Tells Django to NOT automatically serve static files at STATIC_URL."
),
no_reloader: bool = typer.Option(False, "--noreload", help="Tells Django to NOT use the auto-reloader."),
skip_checks: bool = typer.Option(False, "--skip-checks", help="Skip system checks."),
pdb: bool = typer.Option(False, "--pdb", help="Drop into pdb shell at the start of any view."),
ipdb: bool = typer.Option(False, "--ipdb", help="Drop into ipdb shell at the start of any view."),
pm: bool = typer.Option(False, "--pm", help="Drop into (i)pdb shell if an exception is raised in a view."),
print_sql: bool = typer.Option(False, "--print-sql", help="Print SQL queries as they're executed."),
print_sql_location: bool = typer.Option(
False, "--print-sql-location", help="Show location in code where SQL query generated from."
),
cert_file: Optional[str] = typer.Option(
None,
help=(
"SSL .crt file path. If not provided path from --key-file will be selected. Either --cert-file or "
"--key-file must be provided to use SSL."
),
] = None,
key_file: Annotated[
Optional[str],
Option(
help=(
"SSL .key file path. If not provided path from --cert-file will be "
"selected. Either --cert-file or --key-file must be provided to use SSL."
)
),
key_file: Optional[str] = typer.Option(
None,
help=(
"SSL .key file path. If not provided path from --cert-file will be "
"selected. Either --cert-file or --key-file must be provided to use SSL."
),
] = None,
),
):
if not importlib.util.find_spec("django_extensions") and not importlib.util.find_spec("werkzeug"):
msg = (
Expand Down Expand Up @@ -254,11 +250,9 @@ def download_cli(self) -> None:
self._write_success(f"Downloading Tailwind CSS CLI from '{download_url}'")
dest_file.parent.mkdir(parents=True, exist_ok=True)
certifi_context = ssl.create_default_context(cafile=certifi.where())
with (
urllib.request.urlopen(download_url, context=certifi_context) as source,
dest_file.open(mode="wb") as dest, # noqa: S310
):
shutil.copyfileobj(source, dest)
with urllib.request.urlopen(download_url, context=certifi_context) as source:
with dest_file.open(mode="wb") as dest:
shutil.copyfileobj(source, dest)
# make cli executable
dest_file.chmod(0o755)
self._write_success(f"Downloaded Tailwind CSS CLI to '{dest_file}'")
Expand Down

0 comments on commit 14bf6da

Please sign in to comment.