From c7c46ce59e90e9eaa16db58e56eedd080c402a67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Sj=C3=B6green?= Date: Thu, 20 Feb 2025 11:39:13 +0100 Subject: [PATCH 1/8] Update to support django >5 and python >3.12 --- .github/workflows/main.yml | 2 ++ README.rst | 8 +++++--- pyproject.toml | 2 +- tox.ini | 20 +++++++++++++------- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 743844b..2d8c627 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,6 +19,8 @@ jobs: - "3.9" - "3.10" - "3.11" + - "3.12" + - "3.13" steps: - uses: actions/checkout@v2 diff --git a/README.rst b/README.rst index a77263f..c4c2366 100644 --- a/README.rst +++ b/README.rst @@ -32,10 +32,12 @@ with the ``drf`` extra to keep those in sync: Currently tested only for -- Django 3.2 under Python 3.7-3.9 +- Django 3.2 under Python 3.7-3.10 - Django 4.0 under Python 3.8-3.10 -- Django 4.1 under Python 3.8-3.10 -- Django 4.2 under Python 3.8-3.10 +- Django 4.1 under Python 3.8-3.13 +- Django 4.2 under Python 3.8-3.13 +- Django 5.0 under Python 3.10-3.13 +- Django 5.1 under Python 3.10-3.13 Pull requests welcome! diff --git a/pyproject.toml b/pyproject.toml index 5429f32..79ca68d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools>=57.0.0", "wheel"] build-backend = "setuptools.build_meta" [tool.black] -target-version = ["py38", "py39", "py310", "py311"] +target-version = ["py38", "py39", "py310", "py311", "py312", "py313"] [tool.ruff] src = ["src"] diff --git a/tox.ini b/tox.ini index d234a80..9101271 100644 --- a/tox.ini +++ b/tox.ini @@ -7,10 +7,12 @@ # Currently `pip install tox==3.0.0rc2` [tox] -envlist = py38-django{ 32, 40, 41, 42 } - py39-django{ 32, 40, 41, 42 } - py310-django{ 32, 40, 41, 42 } - py311-django{ 41, 42 } +envlist = py38-django{ 32, 40, 41, 42 } + py39-django{ 32, 40, 41, 42 } + py310-django{ 32, 40, 41, 42, 50, 51 } + py311-django{ 41, 42, 50, 51 } + py312-django{ 41, 42, 50, 51 } + py313-django{ 41, 42, 50, 51 } # Shouldn't be any need to run with no DRF for each python py311-no-rest-framework type-check @@ -36,11 +38,15 @@ deps = django40: Django<4.1 django41: Django<4.2 django42: Django<4.3 + django50: Django<5.1 + django51: Django<5.2 django32: djangorestframework>=3.12.0,<3.13 django40: djangorestframework>=3.14.0,<3.15 django41: djangorestframework>=3.14.0,<3.15 - # TODO: Change to proper version when a version with this commit is available. - django42: https://github.com/encode/django-rest-framework/archive/ea03e95174f46003e7e917b623c5316247b8b316.zip + django42: djangorestframework>=3.15.0,<3.16 + django50: djangorestframework>=3.15.0,<3.16 + django51: djangorestframework>=3.15.0,<3.16 + [testenv:py311-no-rest-framework] @@ -48,7 +54,7 @@ commands = pytest {posargs} deps = . - Django>=2.2,<=4.2 + Django>=2.2,<=5.2 pytest pytest-cov pytest-django From db0a3c63f0d65fb355a3e64fe09bde9f99e57db9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Sj=C3=B6green?= Date: Thu, 20 Feb 2025 12:01:40 +0100 Subject: [PATCH 2/8] Use python 3.13 for pre-commit and other tasks --- .github/workflows/main.yml | 6 +++--- .pre-commit-config.yaml | 2 +- LICENSE | 2 +- README.rst | 2 +- setup.cfg | 12 ++++++++++-- tox.ini | 6 +++--- 6 files changed, 19 insertions(+), 11 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2d8c627..9ca6932 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -36,7 +36,7 @@ jobs: ENV_PREFIX=$(tr -C -d "0-9" <<< "${{ matrix.python-version }}") TOXENV=$(tox --listenvs | grep "^py$ENV_PREFIX" | tr '\n' ',') python -m tox - name: Report coverage - if: matrix.python-version == '3.10' && github.event_name == 'pull_request' && (github.event.action == 'opened' || github.event.action == 'reopened' || github.event.action == 'synchronize') + if: matrix.python-version == '3.13' && github.event_name == 'pull_request' && (github.event.action == 'opened' || github.event.action == 'reopened' || github.event.action == 'synchronize') uses: 5monkeys/cobertura-action@master with: path: coverage.xml @@ -56,7 +56,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v2 with: - python-version: "3.10" + python-version: "3.13" - name: Upgrade packaging tools run: python -m pip install --upgrade pip setuptools virtualenv - name: Install dependencies @@ -68,7 +68,7 @@ jobs: name: Lint uses: less-action/reusables/.github/workflows/pre-commit.yaml@v2 with: - python-version: "3.10" + python-version: "3.13" check-build: name: Check packaging diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 449c417..bfd7b39 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,5 @@ default_language_version: - python: python3.10 + python: python3.13 repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: "v4.5.0" diff --git a/LICENSE b/LICENSE index aeace84..a3ca2e7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014-2021 5 Monkeys +Copyright (c) 2014-2025 5 Monkeys Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.rst b/README.rst index c4c2366..d19935f 100644 --- a/README.rst +++ b/README.rst @@ -32,7 +32,7 @@ with the ``drf`` extra to keep those in sync: Currently tested only for -- Django 3.2 under Python 3.7-3.10 +- Django 3.2 under Python 3.8-3.10 - Django 4.0 under Python 3.8-3.10 - Django 4.1 under Python 3.8-3.13 - Django 4.2 under Python 3.8-3.13 diff --git a/setup.cfg b/setup.cfg index fb6703b..538389c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,11 +9,19 @@ classifiers = Development Status :: 5 - Production/Stable Programming Language :: Python Programming Language :: Python :: 3 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 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 + Framework :: Django + Framework :: Django :: 3.2 + Framework :: Django :: 4.0 + Framework :: Django :: 4.1 + Framework :: Django :: 4.2 + Framework :: Django :: 5.0 + Framework :: Django :: 5.1 Intended Audience :: Developers License :: OSI Approved :: MIT License Operating System :: OS Independent diff --git a/tox.ini b/tox.ini index 9101271..e1663de 100644 --- a/tox.ini +++ b/tox.ini @@ -14,7 +14,7 @@ envlist = py38-django{ 32, 40, 41, 42 } py312-django{ 41, 42, 50, 51 } py313-django{ 41, 42, 50, 51 } # Shouldn't be any need to run with no DRF for each python - py311-no-rest-framework + py313-no-rest-framework type-check skipsdist = true @@ -49,7 +49,7 @@ deps = -[testenv:py311-no-rest-framework] +[testenv:py313-no-rest-framework] commands = pytest {posargs} deps = @@ -61,7 +61,7 @@ deps = [testenv:type-check] -basepython = python3.10 +basepython = python3.13 skip_install = true commands = mypy From c983e0efd75321be3b8fbda8b58d26a81ae67f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Sj=C3=B6green?= Date: Thu, 20 Feb 2025 12:31:15 +0100 Subject: [PATCH 3/8] Fix mypy errors --- src/bananas/admin/api/mixins.py | 4 ++-- src/bananas/admin/api/views.py | 2 +- src/bananas/admin/extension.py | 6 +++--- src/bananas/environment.py | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/bananas/admin/api/mixins.py b/src/bananas/admin/api/mixins.py index 3dc6fdc..a605751 100644 --- a/src/bananas/admin/api/mixins.py +++ b/src/bananas/admin/api/mixins.py @@ -63,9 +63,9 @@ def get_admin_meta(cls) -> ModelDict: if admin is not None: meta.update( { - key: getattr(admin, key) # type: ignore[misc, call-overload] + key: getattr(admin, key) for key in filter( - lambda key: key in meta, # type: ignore[arg-type] + lambda key: key in meta, admin.__dict__.keys(), ) } diff --git a/src/bananas/admin/api/views.py b/src/bananas/admin/api/views.py index 86655bc..7c25fea 100644 --- a/src/bananas/admin/api/views.py +++ b/src/bananas/admin/api/views.py @@ -114,7 +114,7 @@ def create(self, request: Request) -> Response: raise serializers.ValidationError(password_form.errors) # type: ignore[arg-type] password_form.save() - update_session_auth_hash(request, password_form.user) + update_session_auth_hash(request, password_form.user) # type: ignore[arg-type] return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/src/bananas/admin/extension.py b/src/bananas/admin/extension.py index d298f56..d8420af 100644 --- a/src/bananas/admin/extension.py +++ b/src/bananas/admin/extension.py @@ -121,7 +121,7 @@ def admin_view( admin_login_url = reverse_lazy("admin:login") view = user_passes_test( - lambda u: u.is_active and hasattr(u, "is_staff") and u.is_staff, login_url=admin_login_url # type: ignore[arg-type, return-value] + lambda u: u.is_active and hasattr(u, "is_staff") and u.is_staff, login_url=admin_login_url )(view) view = permission_required(perm, login_url=admin_login_url)(view) return view @@ -355,8 +355,8 @@ def get_view_tools(self) -> List[ViewTool]: # Mypy doesn't change type on a len(...) call # See: https://github.com/python/mypy/issues/1178 if len(tool) == 3: - tool, perm = cast(Tuple[str, str, str], tool)[:-1], tool[-1] - text, link = cast(Tuple[str, str], tool) + tool, perm = tool[:-1], tool[-1] + text, link = tool tool = ViewTool(text, link, perm=perm) else: # Assume ViewTool diff --git a/src/bananas/environment.py b/src/bananas/environment.py index 8ca8709..0728de9 100644 --- a/src/bananas/environment.py +++ b/src/bananas/environment.py @@ -199,12 +199,12 @@ def get_settings() -> Dict[str, Any]: if default_value is not UNDEFINED: if default_value is None and key in SETTINGS_TYPES.keys(): # Handle typed django settings defaulting to None - parse = get_parser(SETTINGS_TYPES[key]) + parse = get_parser(SETTINGS_TYPES[key]) # type: ignore[type-var] else: # Determine parser by django setting type parse = get_parser(type(default_value)) - value = parse(value) + value = parse(value) # type: ignore[assignment] settings[key] = value From 4bd0a8db7f314441e399a4358aa67be21d2b9262 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Sj=C3=B6green?= Date: Thu, 20 Feb 2025 12:31:59 +0100 Subject: [PATCH 4/8] Fix black target versions --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 79ca68d..d1ae6f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools>=57.0.0", "wheel"] build-backend = "setuptools.build_meta" [tool.black] -target-version = ["py38", "py39", "py310", "py311", "py312", "py313"] +target-version = ["py38", "py39", "py310", "py311", "py312"] [tool.ruff] src = ["src"] From ee49b12c759c97058a8772dfa156abd2ce3a7972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Sj=C3=B6green?= Date: Thu, 20 Feb 2025 12:44:08 +0100 Subject: [PATCH 5/8] Fix mypy again --- src/bananas/environment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bananas/environment.py b/src/bananas/environment.py index 0728de9..c8235dd 100644 --- a/src/bananas/environment.py +++ b/src/bananas/environment.py @@ -199,10 +199,10 @@ def get_settings() -> Dict[str, Any]: if default_value is not UNDEFINED: if default_value is None and key in SETTINGS_TYPES.keys(): # Handle typed django settings defaulting to None - parse = get_parser(SETTINGS_TYPES[key]) # type: ignore[type-var] + parse = get_parser(SETTINGS_TYPES[key]) else: # Determine parser by django setting type - parse = get_parser(type(default_value)) + parse = get_parser(type(default_value)) # type: ignore[type-var] value = parse(value) # type: ignore[assignment] From e9a8a6439df21d58c2f685958d9958bb4d8d978e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Sj=C3=B6green?= Date: Thu, 20 Feb 2025 12:44:59 +0100 Subject: [PATCH 6/8] Fix formatting --- src/bananas/admin/extension.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bananas/admin/extension.py b/src/bananas/admin/extension.py index d8420af..4f014e7 100644 --- a/src/bananas/admin/extension.py +++ b/src/bananas/admin/extension.py @@ -121,7 +121,8 @@ def admin_view( admin_login_url = reverse_lazy("admin:login") view = user_passes_test( - lambda u: u.is_active and hasattr(u, "is_staff") and u.is_staff, login_url=admin_login_url + lambda u: u.is_active and hasattr(u, "is_staff") and u.is_staff, + login_url=admin_login_url, )(view) view = permission_required(perm, login_url=admin_login_url)(view) return view From 5ad5245b69b044b13e0670802502483ea8bc3dc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Sj=C3=B6green?= Date: Thu, 20 Feb 2025 14:16:47 +0100 Subject: [PATCH 7/8] Fix check-manifest --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bfd7b39..ca3ea7e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,7 +36,7 @@ repos: - id: check-manifest # See https://github.com/mgedmin/check-manifest/issues/141 args: ["--no-build-isolation"] - additional_dependencies: [setuptools-scm] + additional_dependencies: [setuptools, wheel, setuptools-scm] exclude: | (?x)( From b62db983ac7280207f2b444ac14969d5c9328641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Sj=C3=B6green?= Date: Thu, 20 Feb 2025 14:46:09 +0100 Subject: [PATCH 8/8] Update release workflow to use trusted publishing --- .github/workflows/main.yml | 8 +++---- .github/workflows/release.yaml | 42 +++++++++++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9ca6932..7380b22 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,8 +23,8 @@ jobs: - "3.13" steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Upgrade packaging tools @@ -52,9 +52,9 @@ jobs: name: Run type checks runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: "3.13" - name: Upgrade packaging tools diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c748efc..9776ac9 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -6,6 +6,42 @@ on: jobs: build-and-publish: - uses: less-action/reusables/.github/workflows/python-publish.yaml@v6 - secrets: - pypi_api_token: ${{ secrets.PYPI_API_TOKEN }} + name: Build and publish + runs-on: ubuntu-latest + permissions: + # IMPORTANT: this permission is mandatory for trusted publishing + id-token: write + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.13" + cache: pip + cache-dependency-path: ${{ inputs.requirements_file }} + check-latest: true + - name: Install dependencies + run: python3 -m pip install --upgrade build pkginfo + - name: Build + run: python3 -m build --sdist --wheel . + # NOTE: Copied and modified from less-action/reusables + - name: Verify release version + run: | + pkg_info_version=$( + python3 <(cat << EOF + from pathlib import Path + from pkginfo import Wheel + wheel = Wheel(next(Path("dist").glob("*.whl"))) + print(wheel.version) + EOF + ) + ) + if [[ "$pkg_info_version" != "${{ github.event.release.tag_name }}" ]]; then + echo "💥 The version of the built wheel doesn't match the release tag." + echo + echo "Release tag: '${{ github.event.release.tag_name }}'" + echo "Packaged version: '$pkg_info_version'" + exit 1 + fi + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1