Drop 'Python 3.9' support (#773)

* Drop Python 3.9 support, set minimum version to 3.10

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update CHANGELOG.md

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix lint error

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Youngkwang Yang 2025-10-18 00:51:53 +09:00 committed by GitHub
parent bd03eb6199
commit d417f30142
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 43 additions and 44 deletions

View file

@ -18,7 +18,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.9'
python-version: '3.10'
- name: Get pip cache dir
id: pip-cache

View file

@ -9,7 +9,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v5
@ -35,7 +35,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.10", "3.11", "3.12", "3.13"]
services:
postgres:
image: postgres:15
@ -81,7 +81,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.10", "3.11", "3.12", "3.13"]
services:
mysql:
image: mysql:8.0

View file

@ -4,10 +4,10 @@ repos:
rev: 25.9.0
hooks:
- id: black
language_version: python3.9
language_version: python3.10
args:
- "--target-version"
- "py39"
- "py310"
- repo: https://github.com/PyCQA/flake8
rev: "7.3.0"
hooks:
@ -21,7 +21,7 @@ repos:
rev: v3.20.0
hooks:
- id: pyupgrade
args: [--py39-plus]
args: [--py310-plus]
- repo: https://github.com/adamchainz/django-upgrade
rev: 1.29.0
hooks:

View file

@ -2,6 +2,10 @@
## Next Release
#### Improvements
- Drop 'Python 3.9' support ([#773](https://github.com/jazzband/django-auditlog/pull/773))
## 3.3.0 (2025-09-18)
#### Improvements

View file

@ -1,5 +1,4 @@
from contextvars import ContextVar
from typing import Optional
from django.conf import settings
from django.http import HttpRequest
@ -8,7 +7,7 @@ from django.utils.module_loading import import_string
correlation_id = ContextVar("auditlog_correlation_id", default=None)
def set_cid(request: Optional[HttpRequest] = None) -> None:
def set_cid(request: HttpRequest | None = None) -> None:
"""
A function to read the cid from a request.
If the header is not in the request, then we set it to `None`.
@ -40,11 +39,11 @@ def set_cid(request: Optional[HttpRequest] = None) -> None:
correlation_id.set(cid)
def _get_cid() -> Optional[str]:
def _get_cid() -> str | None:
return correlation_id.get()
def get_cid() -> Optional[str]:
def get_cid() -> str | None:
"""
Calls the cid getter function based on `settings.AUDITLOG_CID_GETTER`

View file

@ -1,6 +1,6 @@
import json
from collections.abc import Callable
from datetime import timezone
from typing import Callable, Optional
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
@ -131,7 +131,7 @@ def is_primitive(obj) -> bool:
return isinstance(obj, primitive_types)
def get_mask_function(mask_callable: Optional[str] = None) -> Callable[[str], str]:
def get_mask_function(mask_callable: str | None = None) -> Callable[[str], str]:
"""
Get the masking function to use based on the following priority:
1. Model-specific mask_callable if provided
@ -168,8 +168,8 @@ def mask_str(value: str) -> str:
def model_instance_diff(
old: Optional[Model],
new: Optional[Model],
old: Model | None,
new: Model | None,
fields_to_check=None,
use_json_for_changes=False,
):

View file

@ -1,5 +1,3 @@
from typing import Optional
from django.conf import settings
from django.contrib.auth import get_user_model
@ -39,7 +37,7 @@ class AuditlogMiddleware:
return remote_addr
@staticmethod
def _get_remote_port(request) -> Optional[int]:
def _get_remote_port(request) -> int | None:
remote_port = request.headers.get("X-Forwarded-Port", "")
try:

View file

@ -1,9 +1,10 @@
import ast
import contextlib
import json
from collections.abc import Callable
from copy import deepcopy
from datetime import timezone
from typing import Any, Callable, Union
from typing import Any
from dateutil import parser
from dateutil.tz import gettz
@ -534,7 +535,7 @@ class LogEntry(models.Model):
return changes_display_dict
def _get_changes_display_for_fk_field(
self, field: Union[models.ForeignKey, models.OneToOneField], value: Any
self, field: models.ForeignKey | models.OneToOneField, value: Any
) -> str:
"""
:return: A string representing a given FK value and the field to which it belongs

View file

@ -1,7 +1,7 @@
import copy
from collections import defaultdict
from collections.abc import Collection, Iterable
from typing import Any, Callable, Optional, Union
from collections.abc import Callable, Collection, Iterable
from typing import Any
from django.apps import apps
from django.db.models import ManyToManyField, Model
@ -38,7 +38,7 @@ class AuditlogModelRegistry:
delete: bool = True,
access: bool = True,
m2m: bool = True,
custom: Optional[dict[ModelSignal, Callable]] = None,
custom: dict[ModelSignal, Callable] | None = None,
):
from auditlog.receivers import log_access, log_create, log_delete, log_update
@ -62,14 +62,14 @@ class AuditlogModelRegistry:
def register(
self,
model: ModelBase = None,
include_fields: Optional[list[str]] = None,
exclude_fields: Optional[list[str]] = None,
mapping_fields: Optional[dict[str, str]] = None,
mask_fields: Optional[list[str]] = None,
mask_callable: Optional[str] = None,
m2m_fields: Optional[Collection[str]] = None,
include_fields: list[str] | None = None,
exclude_fields: list[str] | None = None,
mapping_fields: dict[str, str] | None = None,
mask_fields: list[str] | None = None,
mask_callable: str | None = None,
m2m_fields: Collection[str] | None = None,
serialize_data: bool = False,
serialize_kwargs: Optional[dict[str, Any]] = None,
serialize_kwargs: dict[str, Any] | None = None,
serialize_auditlog_fields_only: bool = False,
):
"""
@ -259,7 +259,7 @@ class AuditlogModelRegistry:
]
return exclude_models
def _register_models(self, models: Iterable[Union[str, dict[str, Any]]]) -> None:
def _register_models(self, models: Iterable[str | dict[str, Any]]) -> None:
models = copy.deepcopy(models)
for model in models:
if isinstance(model, str):

View file

@ -11,10 +11,10 @@ The repository can be found at https://github.com/jazzband/django-auditlog/.
**Requirements**
- Python 3.9 or higher
- Python 3.10 or higher
- Django 4.2, 5.0, 5.1, and 5.2
Auditlog is currently tested with Python 3.9+ and Django 4.2, 5.0, 5.1, and 5.2. The latest test report can be found
Auditlog is currently tested with Python 3.10+ and Django 4.2, 5.0, 5.1, and 5.2. The latest test report can be found
at https://github.com/jazzband/django-auditlog/actions.
Adding Auditlog to your Django application

View file

@ -29,12 +29,11 @@ setup(
description="Audit log app for Django",
long_description=long_description,
long_description_content_type="text/markdown",
python_requires=">=3.9",
python_requires=">=3.10",
install_requires=["Django>=4.2", "python-dateutil>=2.7.0"],
zip_safe=False,
classifiers=[
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",

16
tox.ini
View file

@ -1,13 +1,13 @@
[tox]
envlist =
{py39,py310,py311}-django42
{py310,py311}-django42
{py310,py311,py312}-django50
{py310,py311,py312,py313}-django51
{py310,py311,py312,py313}-django52
{py312,py313}-djangomain
py39-docs
py39-lint
py39-checkmigrations
py310-docs
py310-lint
py310-checkmigrations
[testenv]
setenv =
@ -42,19 +42,18 @@ basepython =
py312: python3.12
py311: python3.11
py310: python3.10
py39: python3.9
[testenv:py39-docs]
[testenv:py310-docs]
changedir = docs/source
deps = -rdocs/requirements.txt
commands = sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html
[testenv:py39-lint]
[testenv:py310-lint]
deps = pre-commit
commands =
pre-commit run --all-files
[testenv:py39-checkmigrations]
[testenv:py310-checkmigrations]
description = Check for missing migrations
changedir = auditlog_tests
deps =
@ -73,7 +72,6 @@ commands =
[gh-actions]
python =
3.9: py39
3.10: py310
3.11: py311
3.12: py312