diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index d019ad72..280fcaa2 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -16,26 +16,34 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] # latest release minus two + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] # latest release minus two requirements-file: [ dj42_cms311.txt, dj42_cms41.txt, dj50_cms41.txt, dj51_cms41.txt, dj52_cms50.txt, + dj60_cms50.txt, ] os: [ ubuntu-latest, ] exclude: - - python-version: "3.9" + # Python 3.14 only works with Django 5.2+ + - python-version: "3.14" + requirements-file: dj42_cms311.txt + - python-version: "3.14" + requirements-file: dj42_cms41.txt + - python-version: "3.14" requirements-file: dj50_cms41.txt - - python-version: "3.9" + - python-version: "3.14" requirements-file: dj51_cms41.txt - - python-version: "3.9" - requirements-file: dj52_cms50.txt + # Python 3.10 and 3.11 only supported until Django 5.2 - python-version: "3.10" - requirements-file: dj52_cms50.txt + requirements-file: dj60_cms50.txt + - python-version: "3.11" + requirements-file: dj60_cms50.txt + # Python 3.12 and 3.13 don't support Django 4.2 with CMS 3.11 - python-version: "3.12" requirements-file: dj42_cms311.txt - python-version: "3.13" @@ -53,7 +61,8 @@ jobs: - name: Install dependencies run: | sudo apt install libcairo2-dev pkg-config python3-dev - pip install -U -r tests/requirements/${{ matrix.requirements-file }} + python -m pip install --upgrade pip uv + uv pip install --system -r tests/requirements/${{ matrix.requirements-file }} - name: Run coverage run: | coverage run -m pytest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2dcc7fe8..4f4fa29d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,7 +12,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" @@ -33,6 +33,6 @@ repos: - flake8-logging-format - repo: https://github.com/tox-dev/pyproject-fmt - rev: v2.7.0 + rev: v2.11.0 hooks: - id: pyproject-fmt diff --git a/djangocms_frontend/component_base.py b/djangocms_frontend/component_base.py index 4fbdd6da..a5ef74fd 100644 --- a/djangocms_frontend/component_base.py +++ b/djangocms_frontend/component_base.py @@ -1,5 +1,4 @@ import importlib -import typing from django import forms from django.apps import apps @@ -212,11 +211,11 @@ def get_registration(cls) -> tuple[type, type, list[type]]: ) @classproperty - def _component_meta(cls) -> typing.Optional[type]: + def _component_meta(cls) -> type | None: return getattr(cls, "Meta", None) @classmethod - def _generate_fieldset(cls) -> list[tuple[typing.Optional[str], dict]]: + def _generate_fieldset(cls) -> list[tuple[str | None, dict]]: return [(None, {"fields": cls.declared_fields.keys()})] def get_short_description(self) -> str: diff --git a/djangocms_frontend/templatetags/frontend.py b/djangocms_frontend/templatetags/frontend.py index d72f4acf..1fd0677b 100644 --- a/djangocms_frontend/templatetags/frontend.py +++ b/djangocms_frontend/templatetags/frontend.py @@ -156,7 +156,7 @@ def render_tag(self, context, slot_name, nodelist): class DummyPlugin: - def __init__(self, nodelist, plugin_type, slot_name: typing.Optional[str] = None) -> "DummyPlugin": + def __init__(self, nodelist, plugin_type, slot_name: str | None = None) -> "DummyPlugin": self.nodelist = nodelist self.plugin_type = (f"{plugin_type}{slot_name.capitalize()}Plugin") if slot_name else "DummyPlugin" if slot_name is None: diff --git a/pyproject.toml b/pyproject.toml index 02a569f3..e63c1f2e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ license = "BSD-3-Clause" authors = [ { name = "Fabian Braun", email = "fsbraun@gmx.de" }, ] -requires-python = ">=3.9" +requires-python = ">=3.10" classifiers = [ "Development Status :: 5 - Production/Stable", "Framework :: Django", @@ -24,11 +24,11 @@ classifiers = [ "Framework :: Django CMS :: 4.1", "Framework :: Django CMS :: 5.0", "Programming Language :: Python :: 3 :: Only", - "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", + "Programming Language :: Python :: 3.14", ] dynamic = [ "version" ] dependencies = [ diff --git a/tests/fixtures.py b/tests/fixtures.py index 3f7ccde7..4bf8a555 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -79,7 +79,7 @@ def create_page(self, title, **kwargs): return create_page(title=title, **kwargs) def get_placeholders(self, page): - from cms.models import Placeholder, PageContent + from cms.models import PageContent, Placeholder page_content = PageContent.admin_manager.latest_content().get(language=self.language, page=self.page) return Placeholder.objects.get_for_obj(page_content) diff --git a/tests/grid/test_forms.py b/tests/grid/test_forms.py index 30c444fb..4dd169db 100644 --- a/tests/grid/test_forms.py +++ b/tests/grid/test_forms.py @@ -1,9 +1,6 @@ from django.test import TestCase -from djangocms_frontend.contrib.grid.forms import ( - GridColumnForm, - GridRowForm, - GridContainerForm) +from djangocms_frontend.contrib.grid.forms import GridColumnForm, GridContainerForm, GridRowForm class GridFormTestCase(TestCase): diff --git a/tests/requirements/dj60_cms50.txt b/tests/requirements/dj60_cms50.txt new file mode 100644 index 00000000..e8192007 --- /dev/null +++ b/tests/requirements/dj60_cms50.txt @@ -0,0 +1,6 @@ +-r base.txt + +Django>=6.0a1,<6.1 +django-cms>=5.0,<5.1 +djangocms-versioning>=2.4 + diff --git a/tests/test_fields.py b/tests/test_fields.py index 77b40a7a..0401d14c 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1,10 +1,14 @@ from django import forms from django.test import TestCase -from djangocms_frontend.settings import DEVICE_CHOICES from djangocms_frontend.fields import ( - AttributesField, TagTypeField, - TagTypeFormField, DeviceChoiceField, OptionalDeviceChoiceField) + AttributesField, + DeviceChoiceField, + OptionalDeviceChoiceField, + TagTypeField, + TagTypeFormField, +) +from djangocms_frontend.settings import DEVICE_CHOICES class FieldsTestCase(TestCase): diff --git a/tests/test_plugin_tag.py b/tests/test_plugin_tag.py index d2ab8089..81fd3c18 100644 --- a/tests/test_plugin_tag.py +++ b/tests/test_plugin_tag.py @@ -129,6 +129,7 @@ def test_non_frontend_plugin(self): def test_autohero_component_registered_for_plugin_tag(self): from cms.plugin_pool import plugin_pool + from djangocms_frontend.plugin_tag import plugin_tag_pool # Check that the AutoHero plugin is registered diff --git a/tests/test_settings.py b/tests/test_settings.py index 658c852e..70ba2a73 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -1,6 +1,5 @@ import os - INSTALLED_APPS = [ "django.contrib.contenttypes", "django.contrib.auth", diff --git a/tests/utilities/test_plugins.py b/tests/utilities/test_plugins.py index 75bfc722..144da767 100644 --- a/tests/utilities/test_plugins.py +++ b/tests/utilities/test_plugins.py @@ -1,9 +1,9 @@ from unittest import skipIf + from cms import __version__ as cms_version from cms.api import add_plugin from cms.test_utils.testcases import CMSTestCase from cms.utils.urlutils import admin_reverse - from djangocms_text.cms_plugins import TextPlugin from djangocms_frontend.contrib.utilities.cms_plugins import (