diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ffe23aa..97ec873 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.8' + python-version: '3.9' - name: Get pip cache dir id: pip-cache diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d024814..b28e2c4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,7 +9,7 @@ jobs: fail-fast: false max-parallel: 5 matrix: - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + python-version: ['3.9', '3.10', '3.11', '3.12'] services: postgres: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 286039b..8587e94 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,10 +4,10 @@ repos: rev: 24.4.2 hooks: - id: black - language_version: python3.8 + language_version: python3.9 args: - "--target-version" - - "py38" + - "py39" - repo: https://github.com/PyCQA/flake8 rev: "7.1.0" hooks: @@ -21,7 +21,7 @@ repos: rev: v3.16.0 hooks: - id: pyupgrade - args: [--py38-plus] + args: [--py39-plus] - repo: https://github.com/adamchainz/django-upgrade rev: 1.20.0 hooks: diff --git a/CHANGELOG.md b/CHANGELOG.md index 5063f3c..c72957c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ #### Improvements - feat: Added `LogEntry.remote_port` field. ([#671](https://github.com/jazzband/django-auditlog/pull/671)) +- Drop Python 3.8 support. ([#678](https://github.com/jazzband/django-auditlog/pull/678)) #### Fixes diff --git a/auditlog/migrations/0015_alter_logentry_changes.py b/auditlog/migrations/0015_alter_logentry_changes.py index fb52a6b..2c827c2 100644 --- a/auditlog/migrations/0015_alter_logentry_changes.py +++ b/auditlog/migrations/0015_alter_logentry_changes.py @@ -1,11 +1,9 @@ # Generated by Django 4.0 on 2022-08-04 15:41 -from typing import List - from django.conf import settings from django.db import migrations, models -def two_step_migrations() -> List: +def two_step_migrations() -> list: if settings.AUDITLOG_TWO_STEP_MIGRATION: return [ migrations.RenameField( diff --git a/auditlog/models.py b/auditlog/models.py index a0b402c..3d10633 100644 --- a/auditlog/models.py +++ b/auditlog/models.py @@ -3,7 +3,7 @@ import contextlib import json from copy import deepcopy from datetime import timezone -from typing import Any, Callable, Dict, List, Union +from typing import Any, Callable, Union from dateutil import parser from dateutil.tz import gettz @@ -275,8 +275,8 @@ class LogEntryManager(models.Manager): return instance_copy def _get_applicable_model_fields( - self, instance, model_fields: Dict[str, List[str]] - ) -> List[str]: + self, instance, model_fields: dict[str, list[str]] + ) -> list[str]: include_fields = model_fields["include_fields"] exclude_fields = model_fields["exclude_fields"] all_field_names = [field.name for field in instance._meta.fields] @@ -287,8 +287,8 @@ class LogEntryManager(models.Manager): return list(set(include_fields or all_field_names).difference(exclude_fields)) def _mask_serialized_fields( - self, data: Dict[str, Any], mask_fields: List[str] - ) -> Dict[str, Any]: + self, data: dict[str, Any], mask_fields: list[str] + ) -> dict[str, Any]: all_field_data = data.pop("fields") masked_field_data = {} @@ -595,8 +595,8 @@ class AuditlogHistoryField(GenericRelation): changes_func = None -def _changes_func() -> Callable[[LogEntry], Dict]: - def json_then_text(instance: LogEntry) -> Dict: +def _changes_func() -> Callable[[LogEntry], dict]: + def json_then_text(instance: LogEntry) -> dict: if instance.changes: return instance.changes elif instance.changes_text: @@ -604,7 +604,7 @@ def _changes_func() -> Callable[[LogEntry], Dict]: return json.loads(instance.changes_text) return {} - def default(instance: LogEntry) -> Dict: + def default(instance: LogEntry) -> dict: return instance.changes or {} if settings.AUDITLOG_USE_TEXT_CHANGES_IF_JSON_IS_NOT_PRESENT: diff --git a/auditlog/registry.py b/auditlog/registry.py index 3f1f8f3..0c9067b 100644 --- a/auditlog/registry.py +++ b/auditlog/registry.py @@ -1,16 +1,7 @@ import copy from collections import defaultdict -from typing import ( - Any, - Callable, - Collection, - Dict, - Iterable, - List, - Optional, - Tuple, - Union, -) +from collections.abc import Collection, Iterable +from typing import Any, Callable, Optional, Union from django.apps import apps from django.db.models import ManyToManyField, Model @@ -26,7 +17,7 @@ from django.db.models.signals import ( from auditlog.conf import settings from auditlog.signals import accessed -DispatchUID = Tuple[int, int, int] +DispatchUID = tuple[int, int, int] class AuditLogRegistrationError(Exception): @@ -47,7 +38,7 @@ class AuditlogModelRegistry: delete: bool = True, access: bool = True, m2m: bool = True, - custom: Optional[Dict[ModelSignal, Callable]] = None, + custom: Optional[dict[ModelSignal, Callable]] = None, ): from auditlog.receivers import log_access, log_create, log_delete, log_update @@ -71,13 +62,13 @@ 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, + 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, m2m_fields: Optional[Collection[str]] = None, serialize_data: bool = False, - serialize_kwargs: Optional[Dict[str, Any]] = None, + serialize_kwargs: Optional[dict[str, Any]] = None, serialize_auditlog_fields_only: bool = False, ): """ @@ -169,7 +160,7 @@ class AuditlogModelRegistry: else: self._disconnect_signals(model) - def get_models(self) -> List[ModelBase]: + def get_models(self) -> list[ModelBase]: return list(self._registry.keys()) def get_model_fields(self, model: ModelBase): @@ -235,7 +226,7 @@ class AuditlogModelRegistry: """Generate a dispatch_uid which is unique for a combination of self, signal, and receiver.""" return id(self), id(signal), id(receiver) - def _get_model_classes(self, app_model: str) -> List[ModelBase]: + def _get_model_classes(self, app_model: str) -> list[ModelBase]: try: try: app_label, model_name = app_model.split(".") @@ -247,7 +238,7 @@ class AuditlogModelRegistry: def _get_exclude_models( self, exclude_tracking_models: Iterable[str] - ) -> List[ModelBase]: + ) -> list[ModelBase]: exclude_models = [ model for app_model in tuple(exclude_tracking_models) @@ -256,7 +247,7 @@ class AuditlogModelRegistry: ] return exclude_models - def _register_models(self, models: Iterable[Union[str, Dict[str, Any]]]) -> None: + def _register_models(self, models: Iterable[Union[str, dict[str, Any]]]) -> None: models = copy.deepcopy(models) for model in models: if isinstance(model, str): diff --git a/docs/source/installation.rst b/docs/source/installation.rst index 6a3a86d..1a17eda 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -11,10 +11,10 @@ The repository can be found at https://github.com/jazzband/django-auditlog/. **Requirements** -- Python 3.8 or higher +- Python 3.9 or higher - Django 3.2, 4.2 and 5.0 -Auditlog is currently tested with Python 3.8+ and Django 3.2, 4.2 and 5.0. The latest test report can be found +Auditlog is currently tested with Python 3.9+ and Django 3.2, 4.2 and 5.0. The latest test report can be found at https://github.com/jazzband/django-auditlog/actions. Adding Auditlog to your Django application diff --git a/pyproject.toml b/pyproject.toml index e8dad4e..4c68d5f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [tool.black] -target-version = ["py38"] +target-version = ["py39"] [tool.isort] profile = "black" diff --git a/setup.py b/setup.py index 901ab66..644f84d 100644 --- a/setup.py +++ b/setup.py @@ -27,12 +27,11 @@ setup( description="Audit log app for Django", long_description=long_description, long_description_content_type="text/markdown", - python_requires=">=3.8", + python_requires=">=3.9", install_requires=["Django>=3.2", "python-dateutil>=2.7.0"], zip_safe=False, classifiers=[ "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", diff --git a/tox.ini b/tox.ini index dc97ca3..a14e996 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,10 @@ [tox] envlist = - {py38,py39,py310}-django32 - {py38,py39,py310,py311}-django42 + {py39,py310}-django32 + {py39,py310,py311}-django42 {py310,py311,py312}-django{50,main} - py38-docs - py38-lint + py39-docs + py39-lint [testenv] setenv = @@ -34,21 +34,19 @@ basepython = py311: python3.11 py310: python3.10 py39: python3.9 - py38: python3.8 -[testenv:py38-docs] +[testenv:py39-docs] changedir = docs/source deps = -rdocs/requirements.txt commands = sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html -[testenv:py38-lint] +[testenv:py39-lint] deps = pre-commit commands = pre-commit run --all-files [gh-actions] python = - 3.8: py38 3.9: py39 3.10: py310 3.11: py311