diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 502041e..1e4cf62 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,23 +8,45 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ "3.10", "3.11", "3.12"] + python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14"] requirements-file: [ dj42_cms41.txt, dj42_cms50.txt, dj50_cms50.txt, dj51_cms50.txt, - dj52_cms50.txt + dj52_cms50.txt, + dj60_cms50.txt, ] os: [ ubuntu-latest, ] + exclude: + - python-version: "3.14" + requirements-file: dj42_cms41.txt + os: ubuntu-latest + - python-version: "3.14" + requirements-file: dj42_cms50.txt + os: ubuntu-latest + - python-version: "3.14" + requirements-file: dj51_cms50.txt + os: ubuntu-latest + - python-version: "3.14" + requirements-file: dj50_cms50.txt + os: ubuntu-latest + - python-version: "3.10" + requirements-file: dj60_cms50.txt + os: ubuntu-latest + - python-version: "3.11" + requirements-file: dj60_cms50.txt + os: ubuntu-latest steps: - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} + - name: Install uv + run: curl -LsSf https://astral.sh/uv/install.sh | sh - name: Install system deps (cairo stack) run: | sudo apt-get update @@ -32,12 +54,15 @@ jobs: build-essential libcairo2-dev pkg-config python3-dev - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install -r tests/requirements/${{ matrix.requirements-file }} - pip install . + uv venv + source .venv/bin/activate + uv pip install -r tests/requirements/${{ matrix.requirements-file }} + uv pip install . - name: Run coverage - run: coverage run -m pytest + run: | + source .venv/bin/activate + coverage run -m pytest - name: Upload Coverage to Codecov uses: codecov/codecov-action@v5.5.1 diff --git a/djangocms_rest/middleware.py b/djangocms_rest/middleware.py index 0ccb222..ea218db 100644 --- a/djangocms_rest/middleware.py +++ b/djangocms_rest/middleware.py @@ -1,4 +1,4 @@ -from typing import Callable +from collections.abc import Callable from django.contrib.sites.shortcuts import get_current_site from django.contrib.sites.models import Site diff --git a/djangocms_rest/plugin_rendering.py b/djangocms_rest/plugin_rendering.py index cc89471..aba7d5a 100644 --- a/djangocms_rest/plugin_rendering.py +++ b/djangocms_rest/plugin_rendering.py @@ -1,5 +1,6 @@ import json -from typing import Any, Iterable, Optional, TypeVar +from typing import Any, TypeVar +from collections.abc import Iterable from django.contrib.sites.shortcuts import get_current_site from django.core.exceptions import ValidationError @@ -50,8 +51,8 @@ def get_auto_model_serializer(model_class: type[ModelType]) -> type: def serialize_cms_plugin( - instance: Optional[Any], context: dict[str, Any] -) -> Optional[dict[str, Any]]: + instance: Any | None, context: dict[str, Any] +) -> dict[str, Any] | None: if not instance or not hasattr(instance, "get_plugin_instance"): return None plugin_instance, plugin = instance.get_plugin_instance() diff --git a/djangocms_rest/serializers/plugins.py b/djangocms_rest/serializers/plugins.py index da4c499..856d79d 100644 --- a/djangocms_rest/serializers/plugins.py +++ b/djangocms_rest/serializers/plugins.py @@ -1,4 +1,4 @@ -from typing import Any, Optional +from typing import Any from django.apps import apps from django.db.models import Field, Model @@ -17,7 +17,7 @@ def serialize_fk( request: HttpRequest, related_model: type[CMSPlugin], pk: Any, - obj: Optional[Model] = None, + obj: Model | None = None, ) -> dict[str, Any]: """ Serializes a foreign key reference to a related model as a URL or identifier. @@ -157,7 +157,7 @@ class PluginDefinitionSerializer(serializers.Serializer): properties = serializers.DictField(help_text="Property definitions") @staticmethod - def get_field_format(field: Field) -> Optional[str]: + def get_field_format(field: Field) -> str | None: """ Get the format for specific field types. diff --git a/djangocms_rest/views.py b/djangocms_rest/views.py index 3e6c291..9bfc413 100644 --- a/djangocms_rest/views.py +++ b/djangocms_rest/views.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import Any, Callable, ParamSpec, TypeVar +from typing import Any, ParamSpec, TypeVar +from collections.abc import Callable from django.contrib.sites.shortcuts import get_current_site from django.urls import reverse from django.utils.functional import lazy diff --git a/pyproject.toml b/pyproject.toml index 3dab6e4..8ea5eca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,12 +14,13 @@ maintainers = [ ] license = "BSD-3-Clause" readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.10" dependencies = [ "django-cms>=5.0.0a1", "djangorestframework", "djangocms-link>=5.0.0", "djangocms-text>=0.8.0", + "djangocms-versioning>=2.1.0,<2.4.0", "pytest-cov>=6.0.0", "pytest-django>=4.10.0", ] @@ -31,6 +32,7 @@ classifiers = [ "Framework :: Django :: 5.0", "Framework :: Django :: 5.1", "Framework :: Django :: 5.2", + "Framework :: Django :: 6.0", "Framework :: Django CMS", "Framework :: Django CMS :: 4.0", "Framework :: Django CMS :: 4.1", @@ -42,6 +44,8 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development", @@ -73,4 +77,4 @@ where = ["."] include = ["djangocms_rest*"] [tool.ruff] -line-length = 119 \ No newline at end of file +line-length = 119 diff --git a/tests/base.py b/tests/base.py index cc718aa..2bd67fe 100644 --- a/tests/base.py +++ b/tests/base.py @@ -1,4 +1,4 @@ -from typing import Optional, Union +from typing import Optional from cms.api import create_page from cms.models import Page @@ -16,7 +16,7 @@ class BaseCMSRestTestCase(RESTTestCase): @classmethod def _create_pages( cls, - page_list: Union[int, list[Union[int, tuple[int, int]]]], + page_list: int | list[int | tuple[int, int]], parent: Optional["Page"] = None, is_first: bool = True, ): diff --git a/tests/requirements/dj60_cms50.txt b/tests/requirements/dj60_cms50.txt new file mode 100644 index 0000000..a6959c1 --- /dev/null +++ b/tests/requirements/dj60_cms50.txt @@ -0,0 +1,4 @@ +-r base.txt + +Django>=6.0a1,<6.1 +django-cms>=5.0.0a1,<5.1 diff --git a/tests/utils.py b/tests/utils.py index 2f772f3..c078dcf 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,4 +1,4 @@ -from typing import Any, Union +from typing import Any from unittest import TestCase @@ -6,7 +6,7 @@ def assert_field_types( test_case: TestCase, obj: dict[str, Any], field: str, - expected_type: Union[type, tuple[type, ...], list, dict], + expected_type: type | tuple[type, ...] | list | dict, obj_type: str = "object", ): """