From 0ec991f18816f5a416292b07442b7bd60050bdc7 Mon Sep 17 00:00:00 2001 From: Tim Schilling Date: Tue, 19 Nov 2024 13:50:49 -0600 Subject: [PATCH 1/3] Dropped support for Python 3.8 Its EOL was recently passed - see https://devguide.python.org/versions/#unsupported-versions Made the new minimum Python version 3.9. --------- Co-authored-by: Anders <6058745+ddabble@users.noreply.github.com> --- .github/workflows/test.yml | 10 +++------- .pre-commit-config.yaml | 4 ++-- CHANGES.rst | 1 + README.rst | 6 +++--- docs/index.rst | 6 +++--- pyproject.toml | 10 ++++------ tox.ini | 3 +-- 7 files changed, 17 insertions(+), 23 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5b95a9e6e..0408df7c3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,17 +11,13 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13-dev'] + python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] django-version: ['4.2', '5.0', 'main'] exclude: - # Exclude py3.8 and py3.9 for Django main and 5.0 - - python-version: '3.8' - django-version: '5.0' + # Exclude py3.9 for Django main and 5.0 - python-version: '3.9' django-version: '5.0' - - python-version: '3.8' - django-version: 'main' - python-version: '3.9' django-version: 'main' @@ -101,7 +97,7 @@ jobs: - name: Set up newest stable Python version uses: actions/setup-python@v5 with: - python-version: 3.11 + python-version: 3.13 cache: 'pip' # Invalidate the cache when this file updates, as the dependencies' versions # are pinned in the step below diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0e5536233..97cd6775d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -40,7 +40,7 @@ repos: - id: detect-private-key - repo: https://github.com/tox-dev/pyproject-fmt - rev: 2.2.3 + rev: v2.5.0 hooks: - id: pyproject-fmt - repo: https://github.com/abravalheri/validate-pyproject @@ -59,4 +59,4 @@ repos: rev: v3.17.0 hooks: - id: pyupgrade - args: [--py38-plus] + args: [--py39-plus] diff --git a/CHANGES.rst b/CHANGES.rst index 58ed7cfa9..188c48d77 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,7 @@ Unreleased - Fixed issue with deferred fields causing DoesNotExist error (gh-678) - Added HistoricOneToOneField (gh-1394) - Updated all djangoproject.com links to reference the stable version (gh-1420) +- Dropped support for Python 3.8, which reached end-of-life on 2024-10-07 (gh-1421) 3.7.0 (2024-05-29) ------------------ diff --git a/README.rst b/README.rst index f054be6e8..b6df57e7d 100644 --- a/README.rst +++ b/README.rst @@ -45,9 +45,9 @@ This app supports the following combinations of Django and Python: ========== ======================== Django Python ========== ======================== -4.2 3.8, 3.9, 3.10, 3.11, 3.12, 3.13-dev -5.0 3.10, 3.11, 3.12, 3.13-dev -main 3.10, 3.11, 3.12, 3.13-dev +4.2 3.9, 3.10, 3.11, 3.12, 3.13 +5.0 3.10, 3.11, 3.12, 3.13 +main 3.10, 3.11, 3.12, 3.13 ========== ======================== Getting Help diff --git a/docs/index.rst b/docs/index.rst index 2276ba9f6..b98380706 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -41,9 +41,9 @@ This app supports the following combinations of Django and Python: ========== ======================= Django Python ========== ======================= -4.2 3.8, 3.9, 3.10, 3.11, 3.12, 3.13-dev -5.0 3.10, 3.11, 3.12, 3.13-dev -main 3.10, 3.11, 3.12, 3.13-dev +4.2 3.9, 3.10, 3.11, 3.12, 3.13 +5.0 3.10, 3.11, 3.12, 3.13 +main 3.10, 3.11, 3.12, 3.13 ========== ======================= Contribute diff --git a/pyproject.toml b/pyproject.toml index 319078fce..f4c6ea811 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ maintainers = [ authors = [ { name = "Corey Bertram", email = "corey@qr7.com" }, ] -requires-python = ">=3.8" +requires-python = ">=3.9" classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Web Environment", @@ -26,13 +26,11 @@ classifiers = [ "License :: OSI Approved :: BSD License", "Programming Language :: Python", "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", - # DEV: uncomment this when the `pyproject-fmt` pre-commit hook stops removing it - #"Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.13", ] dynamic = [ "readme", @@ -83,12 +81,12 @@ fragments = [ [tool.black] line-length = 88 target-version = [ - "py38", + "py39", ] [tool.isort] profile = "black" -py_version = "38" +py_version = "39" [tool.coverage.run] parallel = true diff --git a/tox.ini b/tox.ini index 6d845e47b..3d2095f4f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py{38,39,310,311,312,313}-dj42-{sqlite3,postgres,mysql,mariadb}, + py{39,310,311,312,313}-dj42-{sqlite3,postgres,mysql,mariadb}, py{310,311,312,313}-dj50-{sqlite3,postgres,mysql,mariadb}, py{310,311,312,313}-djmain-{sqlite3,postgres,mysql,mariadb}, docs, @@ -8,7 +8,6 @@ envlist = [gh-actions] python = - 3.8: py38 3.9: py39 3.10: py310 3.11: py311, docs, lint From da4c2f2568419a04ccbcc5c425fe406e0f0d97f6 Mon Sep 17 00:00:00 2001 From: Tim Schilling Date: Tue, 19 Nov 2024 13:50:49 -0600 Subject: [PATCH 2/3] Added support for Django 5.1 Also handled the deprecation of `get_cache_name()` - see https://docs.djangoproject.com/en/5.1/releases/5.1/#features-deprecated-in-5-1 --- .github/workflows/test.yml | 8 +++++--- CHANGES.rst | 1 + README.rst | 1 + docs/index.rst | 1 + simple_history/models.py | 10 +++++++--- tox.ini | 5 +++-- 6 files changed, 18 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0408df7c3..a3c3b1d1d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,12 +12,14 @@ jobs: fail-fast: false matrix: python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] - django-version: ['4.2', '5.0', 'main'] + django-version: ['4.2', '5.0', '5.1', 'main'] exclude: - # Exclude py3.9 for Django main and 5.0 + # Exclude py3.9 for Django main and 5+ - python-version: '3.9' django-version: '5.0' + - python-version: '3.9' + django-version: '5.1' - python-version: '3.9' django-version: 'main' @@ -109,7 +111,7 @@ jobs: # Install this project in editable mode, so that its package metadata can be queried pip install -e . # Install the latest minor version of Django we support - pip install Django==5.0 + pip install Django==5.1 - name: Check translation files are updated run: python -m simple_history.tests.generated_file_checks.check_translations diff --git a/CHANGES.rst b/CHANGES.rst index 188c48d77..6a01c92e7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -11,6 +11,7 @@ Unreleased - Added HistoricOneToOneField (gh-1394) - Updated all djangoproject.com links to reference the stable version (gh-1420) - Dropped support for Python 3.8, which reached end-of-life on 2024-10-07 (gh-1421) +- Added support for Django 5.1 (gh-1388) 3.7.0 (2024-05-29) ------------------ diff --git a/README.rst b/README.rst index b6df57e7d..6b044182c 100644 --- a/README.rst +++ b/README.rst @@ -47,6 +47,7 @@ This app supports the following combinations of Django and Python: ========== ======================== 4.2 3.9, 3.10, 3.11, 3.12, 3.13 5.0 3.10, 3.11, 3.12, 3.13 +5.1 3.10, 3.11, 3.12, 3.13 main 3.10, 3.11, 3.12, 3.13 ========== ======================== diff --git a/docs/index.rst b/docs/index.rst index b98380706..56ef8ea69 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -43,6 +43,7 @@ This app supports the following combinations of Django and Python: ========== ======================= 4.2 3.9, 3.10, 3.11, 3.12, 3.13 5.0 3.10, 3.11, 3.12, 3.13 +5.1 3.10, 3.11, 3.12, 3.13 main 3.10, 3.11, 3.12, 3.13 ========== ======================= diff --git a/simple_history/models.py b/simple_history/models.py index 02b845784..3652c5e40 100644 --- a/simple_history/models.py +++ b/simple_history/models.py @@ -899,10 +899,14 @@ def related_manager_cls(self): class HistoricRelationModelManager(related_model._default_manager.__class__): def get_queryset(self): + cache_name = ( + # DEV: Remove this when support for Django 5.0 has been dropped + self.field.remote_field.get_cache_name() + if django.VERSION < (5, 1) + else self.field.remote_field.cache_name + ) try: - return self.instance._prefetched_objects_cache[ - self.field.remote_field.get_cache_name() - ] + return self.instance._prefetched_objects_cache[cache_name] except (AttributeError, KeyError): history = getattr( self.instance, SIMPLE_HISTORY_REVERSE_ATTR_NAME, None diff --git a/tox.ini b/tox.ini index 3d2095f4f..f088cf7f0 100644 --- a/tox.ini +++ b/tox.ini @@ -1,8 +1,7 @@ [tox] envlist = py{39,310,311,312,313}-dj42-{sqlite3,postgres,mysql,mariadb}, - py{310,311,312,313}-dj50-{sqlite3,postgres,mysql,mariadb}, - py{310,311,312,313}-djmain-{sqlite3,postgres,mysql,mariadb}, + py{310,311,312,313}-dj{50,51,main}-{sqlite3,postgres,mysql,mariadb}, docs, lint @@ -18,6 +17,7 @@ python = DJANGO = 4.2: dj42 5.0: dj50 + 5.1: dj51 main: djmain [flake8] @@ -31,6 +31,7 @@ deps = -rrequirements/test.txt dj42: Django>=4.2,<4.3 dj50: Django>=5.0,<5.1 + dj51: Django>=5.1,<5.2 djmain: https://github.com/django/django/tarball/main postgres: -rrequirements/postgres.txt mysql: -rrequirements/mysql.txt From e0d672313dde4490b518b7c1953542576d46d879 Mon Sep 17 00:00:00 2001 From: Tim Schilling Date: Tue, 19 Nov 2024 13:58:17 -0600 Subject: [PATCH 3/3] Updated pre-commit versions and applied lint changes. --- .pre-commit-config.yaml | 10 +++++----- simple_history/admin.py | 3 ++- simple_history/models.py | 11 ++++++----- simple_history/template_utils.py | 10 +++++----- simple_history/tests/tests/test_template_utils.py | 3 +-- simple_history/tests/tests/utils.py | 3 +-- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 97cd6775d..0af416b92 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,13 +1,13 @@ --- repos: - repo: https://github.com/PyCQA/bandit - rev: 1.7.9 + rev: 1.7.10 hooks: - id: bandit exclude: /.*tests/ - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.8.0 + rev: 24.10.0 hooks: - id: black language_version: python3.9 @@ -25,7 +25,7 @@ repos: - id: isort - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: requirements-txt-fixer files: requirements/.*\.txt$ @@ -44,7 +44,7 @@ repos: hooks: - id: pyproject-fmt - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.19 + rev: v0.23 hooks: - id: validate-pyproject @@ -56,7 +56,7 @@ repos: - "--strict" - repo: https://github.com/asottile/pyupgrade - rev: v3.17.0 + rev: v3.19.0 hooks: - id: pyupgrade args: [--py39-plus] diff --git a/simple_history/admin.py b/simple_history/admin.py index 6a55e2e1c..0c4b470c5 100644 --- a/simple_history/admin.py +++ b/simple_history/admin.py @@ -1,4 +1,5 @@ -from typing import Any, Sequence +from collections.abc import Sequence +from typing import Any from django import http from django.apps import apps as django_apps diff --git a/simple_history/models.py b/simple_history/models.py index 3652c5e40..b007efbfa 100644 --- a/simple_history/models.py +++ b/simple_history/models.py @@ -2,9 +2,10 @@ import importlib import uuid import warnings +from collections.abc import Iterable, Sequence from dataclasses import dataclass from functools import partial -from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Sequence, Type, Union +from typing import TYPE_CHECKING, Any, Union import django from django.apps import apps @@ -1092,7 +1093,7 @@ def _get_field_changes_for_diff( old_history: "HistoricalChanges", fields: Iterable[str], foreign_keys_are_objs: bool, - ) -> List["ModelChange"]: + ) -> list["ModelChange"]: """Helper method for ``diff_against()``.""" changes = [] @@ -1133,7 +1134,7 @@ def _get_m2m_field_changes_for_diff( old_history: "HistoricalChanges", m2m_fields: Iterable[str], foreign_keys_are_objs: bool, - ) -> List["ModelChange"]: + ) -> list["ModelChange"]: """Helper method for ``diff_against()``.""" changes = [] @@ -1202,7 +1203,7 @@ def get_value(obj, through_field): @dataclass(frozen=True) class DeletedObject: - model: Type[models.Model] + model: type[models.Model] pk: Any def __str__(self): @@ -1227,7 +1228,7 @@ def __str__(self): # The PK of the through model's related objects. # # - Any of the other possible values of a model field. -ModelChangeValue = Union[Any, DeletedObject, List[Dict[str, Union[Any, DeletedObject]]]] +ModelChangeValue = Union[Any, DeletedObject, list[dict[str, Union[Any, DeletedObject]]]] @dataclass(frozen=True) diff --git a/simple_history/template_utils.py b/simple_history/template_utils.py index ae02d02e9..722edf1b1 100644 --- a/simple_history/template_utils.py +++ b/simple_history/template_utils.py @@ -1,6 +1,6 @@ import dataclasses from os.path import commonprefix -from typing import Any, Dict, Final, List, Tuple, Type, Union +from typing import Any, Final, Union from django.db.models import ManyToManyField, Model from django.utils.html import conditional_escape @@ -40,7 +40,7 @@ class HistoricalRecordContextHelper: def __init__( self, - model: Type[Model], + model: type[Model], historical_record: HistoricalChanges, *, max_displayed_delta_change_chars=DEFAULT_MAX_DISPLAYED_DELTA_CHANGE_CHARS, @@ -50,7 +50,7 @@ def __init__( self.max_displayed_delta_change_chars = max_displayed_delta_change_chars - def context_for_delta_changes(self, delta: ModelDelta) -> List[Dict[str, Any]]: + def context_for_delta_changes(self, delta: ModelDelta) -> list[dict[str, Any]]: """ Return the template context for ``delta.changes``. By default, this is a list of dicts with the keys ``"field"``, @@ -119,7 +119,7 @@ def prepare_delta_change_value( def stringify_delta_change_values( self, change: ModelChange, old: Any, new: Any - ) -> Tuple[SafeString, SafeString]: + ) -> tuple[SafeString, SafeString]: """ Called by ``format_delta_change()`` after ``old`` and ``new`` have been prepared by ``prepare_delta_change_value()``. @@ -196,7 +196,7 @@ def __init__( ) assert self.min_diff_len >= 0 # nosec - def common_shorten_repr(self, *args: Any) -> Tuple[str, ...]: + def common_shorten_repr(self, *args: Any) -> tuple[str, ...]: """ Returns ``args`` with each element converted into a string representation. If any of the strings are longer than ``self.max_length``, they're all shortened diff --git a/simple_history/tests/tests/test_template_utils.py b/simple_history/tests/tests/test_template_utils.py index b094588f9..9a4e8b5c3 100644 --- a/simple_history/tests/tests/test_template_utils.py +++ b/simple_history/tests/tests/test_template_utils.py @@ -1,5 +1,4 @@ from datetime import datetime -from typing import Tuple from django.test import TestCase from django.utils.dateparse import parse_datetime @@ -225,7 +224,7 @@ def test_context_dict( ) def test__context_for_delta_changes__preserves_html_safe_strings(self): - def get_context_dict_old_and_new(old_value, new_value) -> Tuple[str, str]: + def get_context_dict_old_and_new(old_value, new_value) -> tuple[str, str]: # The field doesn't really matter, as long as it exists on the model # passed to `HistoricalRecordContextHelper` change = ModelChange("question", old_value, new_value) diff --git a/simple_history/tests/tests/utils.py b/simple_history/tests/tests/utils.py index ae6fe949c..16a7e6b7b 100644 --- a/simple_history/tests/tests/utils.py +++ b/simple_history/tests/tests/utils.py @@ -1,5 +1,4 @@ from enum import Enum -from typing import Type from django.conf import settings from django.db.models import Model @@ -15,7 +14,7 @@ class HistoricalTestCase(TestCase): - def assertRecordValues(self, record, klass: Type[Model], values_dict: dict): + def assertRecordValues(self, record, klass: type[Model], values_dict: dict): """ Fail if ``record`` doesn't contain the field values in ``values_dict``. ``record.history_object`` is also checked.