Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: test
on:
push:
branches: main
pull_request_target:
pull_request:

concurrency:
group: test-${{ github.head_ref }}
Expand All @@ -20,14 +20,14 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12-dev']
django-version: ['3.2', '4.0', '4.1', '4.2', 'main']
python-version: ['3.10', '3.11', '3.12', '3.13']
django-version: ['4.2', '5.2', '6.0', 'main']
psycopg-version: ['2', "3"]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}

Expand All @@ -54,12 +54,12 @@ jobs:
types:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6

- name: Set up Python 3.8
uses: actions/setup-python@v2
- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: 3.8
python-version: '3.12'

- name: Install dependencies
run: |
Expand All @@ -73,22 +73,22 @@ jobs:
coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6

- name: Set up Python 3.8
uses: actions/setup-python@v2
- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: 3.8
python-version: '3.12'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install '.[test]'
python -m pip install '.[test]' psycopg2-binary

# https://hynek.me/articles/ditch-codecov-python/
- name: Run tests
run: |
coverage run -m pytest
python -m coverage html --skip-covered --skip-empty
python -m coverage report | sed 's/^/ /' >> $GITHUB_STEP_SUMMARY
python -m coverage report --fail-under=100
python -m coverage report --fail-under=75
12 changes: 5 additions & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
default_language_version:
python: python3.8
python: python3.12

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
Expand All @@ -14,11 +14,10 @@ repos:
rev: 1.13.0
hooks:
- id: django-upgrade
language_version: python3.8
args: [--target-version, "3.2"]
args: [--target-version, "4.2"]

- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.262
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.7
hooks:
- id: ruff
alias: autoformat
Expand All @@ -28,8 +27,7 @@ repos:
rev: 23.3.0
hooks:
- id: black
language_version: python3.8
args: [--target-version, "py37"]
args: [--target-version, "py310"]

- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.0.0-alpha.9-for-vscode
Expand Down
36 changes: 16 additions & 20 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,21 @@

import nox

PY38 = "3.8"
PY39 = "3.9"
PY310 = "3.10"
PY311 = "3.11"
PY312 = "3.12"
PY_VERSIONS = [PY38, PY39, PY310, PY311, PY312]
PY_DEFAULT = PY38
PY313 = "3.13"
PY_VERSIONS = [PY310, PY311, PY312, PY313]
PY_DEFAULT = PY310

DJ32 = "3.2"
DJ40 = "4.0"
DJ41 = "4.1"
DJ42 = "4.2"
DJ52 = "5.2"
DJ60 = "6.0"
DJ60_MIN_PY = PY312
DJMAIN = "main"
DJMAIN_MIN_PY = PY310
DJ_VERSIONS = [DJ32, DJ40, DJ41, DJ42, DJMAIN]
DJ_DEFAULT = DJ32
DJMAIN_MIN_PY = PY312
DJ_VERSIONS = [DJ42, DJ52, DJ60, DJMAIN]
DJ_DEFAULT = DJ42

PSYCOPG2 = "2"
PSYCOPG3 = "3"
Expand All @@ -30,24 +29,21 @@ def version(ver: str) -> tuple[int, ...]:
return tuple(map(int, ver.split(".")))


def should_skip(python: str, django: str, psycopg: str) -> tuple(bool, str | None):
def should_skip(python: str, django: str, psycopg: str) -> tuple[bool, str | None]: # noqa: ARG001
"""Return True if the test should be skipped"""
if django == DJMAIN and version(python) < version(DJMAIN_MIN_PY):
return True, f"Django {DJMAIN} requires Python {DJMAIN_MIN_PY}+"

if django == DJ32 and version(python) >= version(PY312):
return True, f"Django {DJ32} requires Python < {PY312}"

if psycopg == PSYCOPG3 and version(python) >= version(PY312):
return True, f"psycopg3 requires Python < {PY312}"
if django == DJ60 and version(python) < version(DJ60_MIN_PY):
return True, f"Django {DJ60} requires Python {DJ60_MIN_PY}+"

return False, None


@nox.session(python=PY_VERSIONS)
@nox.parametrize("django", DJ_VERSIONS)
@nox.parametrize("psycopg", PSYCOPG_VERSIONS)
def tests(session, django, psycopg):
@nox.session(python=PY_VERSIONS) # type: ignore[untyped-decorator]
@nox.parametrize("django", DJ_VERSIONS) # type: ignore[untyped-decorator]
@nox.parametrize("psycopg", PSYCOPG_VERSIONS) # type: ignore[untyped-decorator]
def tests(session: nox.Session, django: str, psycopg: str) -> None:
skip = should_skip(session.python, django, psycopg)
if skip[0]:
session.skip(skip[1])
Expand Down
22 changes: 12 additions & 10 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@ classifiers = [
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: Implementation :: CPython",
]
dependencies = ["django>=3.2", "dj_database_url"]
dependencies = ["django>=4.2", "dj_database_url"]
description = 'A set of simple utilities for Django apps running on Fly.io'
dynamic = ["version"]
keywords = []
license = "MIT"
name = "django-flyio"
readme = "README.md"
requires-python = ">=3.8"
requires-python = ">=3.10"

[project.optional-dependencies]
dev = ["black", "hatch", "ruff"]
Expand All @@ -44,7 +44,7 @@ path = "src/django_flyio/__init__.py"
[tool.black]
line-length = 120
skip-string-normalization = true
target-version = ["py38"]
target-version = ["py310"]

[tool.coverage.report]
exclude_lines = ["no cov", "if __name__ == .__main__.:", "if TYPE_CHECKING:"]
Expand All @@ -67,6 +67,10 @@ django_find_project = false
pythonpath = ". src"

[tool.ruff]
line-length = 120
target-version = "py310"

[tool.ruff.lint]
ignore = [
# Allow non-abstract empty methods in abstract base classes
"B027", # Allow boolean positional values in function calls, like `dict.get(... True)`
Expand All @@ -80,7 +84,6 @@ ignore = [
"PLR0913",
"PLR0915",
]
line-length = 120
select = [
"A",
"ARG",
Expand Down Expand Up @@ -108,20 +111,19 @@ select = [
"W",
"YTT",
]
target-version = "py38"
unfixable = [
# Don't touch unused imports
"F401",
]

[tool.ruff.isort]
[tool.ruff.lint.isort]
force-single-line = true
known-first-party = ["django_flyio"]
required-imports = ["from __future__ import annotations"]

[tool.ruff.flake8-tidy-imports]
[tool.ruff.lint.flake8-tidy-imports]
ban-relative-imports = "all"

[tool.ruff.per-file-ignores]
[tool.ruff.lint.per-file-ignores]
# Tests can use magic values, assertions, and relative imports
"tests/**/*" = ["PLR2004", "S101", "TID252"]
7 changes: 4 additions & 3 deletions src/django_flyio/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

import asyncio
import os
from collections.abc import Awaitable
from collections.abc import Callable
from http import HTTPStatus
from typing import Awaitable
from typing import Callable

from django.http import HttpRequest
from django.http.response import HttpResponse
Expand Down Expand Up @@ -94,11 +94,12 @@ async def __acall__(self, request: HttpRequest) -> HttpResponseBase:
assert not isinstance(aresponse, HttpResponseBase) # noqa: S101
return await aresponse

def process_exception(self, request: HttpRequest, exception: Exception) -> HttpResponseBase: # noqa: ARG002
def process_exception(self, request: HttpRequest, exception: Exception) -> HttpResponseBase | None: # noqa: ARG002
if isinstance(exception, ReadOnlySqlTransaction):
primary_region = os.getenv("PRIMARY_REGION", None)
response = HttpResponse()
response.content = response.make_bytes(f"retry in region {primary_region}")
response.status_code = HTTPStatus.CONFLICT
response[FLY_REPLAY] = f"region={primary_region}"
return response
return None
4 changes: 2 additions & 2 deletions src/django_flyio/routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ def db_for_read(self, model, **hints) -> str: # type: ignore [no-untyped-def]
def db_for_write(self, model, **hints) -> str: # type: ignore [no-untyped-def] # noqa: ARG002
return "default"

def allow_relation(self, obj1, obj2, **hints) -> bool: # type: ignore [no-untyped-def] # pragma: no cover # noqa: ARG002, E501
def allow_relation(self, obj1, obj2, **hints) -> bool: # type: ignore [no-untyped-def] # pragma: no cover # noqa: ARG002
return True

def allow_migrate(self, db, app_label, **hints) -> bool: # type: ignore [no-untyped-def] # pragma: no cover # noqa: ARG002, E501
def allow_migrate(self, db, app_label, **hints) -> bool: # type: ignore [no-untyped-def] # pragma: no cover # noqa: ARG002
return True
Loading