From d2b117e90a15ccc87b4ce2b14c738ea70199c3d8 Mon Sep 17 00:00:00 2001 From: Vinit kumar Date: Sat, 25 Oct 2025 03:30:43 +0530 Subject: [PATCH 01/14] Drop Python 3.9 support, add Python 3.14 and Django 6.0 support - Update pyproject.toml: requires-python >= 3.10, add classifiers for Python 3.10-3.14 and Django 6.0 - Update tox.ini: remove py39, add py310, py312, py313, py314 - Update GitHub Actions workflows: add Python 3.14 and Django 6.0 test configurations - Add dj60_cms50.txt requirements file for Django 6.0 testing - Add exclusions to prevent Python 3.14 from running against older Django versions Amp-Thread-ID: https://ampcode.com/threads/T-cdf01cfa-6da3-4851-9a9b-0c7a2c181c37 Co-authored-by: Amp --- .github/workflows/test.yml | 54 +++++++++++++++++++++---------- pyproject.toml | 9 +++++- tests/requirements/dj60_cms50.txt | 8 +++++ tox.ini | 15 +++++---- 4 files changed, 62 insertions(+), 24 deletions(-) create mode 100644 tests/requirements/dj60_cms50.txt diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 04ec6787..6fe3497a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,27 +12,36 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ 3.9, "3.10", "3.11", "3.12" ] + python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14" ] requirements-file: [ dj42_cms41.txt, dj50_cms41.txt, dj51_cms41.txt, dj52_cms41.txt, dj52_cms50.txt, + dj60_cms50.txt, ] exclude: + - requirements-file: dj52_cms41.txt + python-version: "3.10" + - requirements-file: dj52_cms50.txt + python-version: "3.10" + - requirements-file: dj60_cms50.txt + python-version: "3.10" + - requirements-file: dj60_cms50.txt + python-version: "3.11" + - requirements-file: dj60_cms50.txt + python-version: "3.12" + - 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_cms41.txt - python-version: 3.9 + python-version: "3.14" - requirements-file: dj52_cms41.txt - python-version: 3.10 + python-version: "3.14" - requirements-file: dj52_cms50.txt - python-version: 3.9 - - requirements-file: dj52_cms50.txt - python-version: 3.10 + python-version: "3.14" steps: - uses: actions/checkout@v5 @@ -58,12 +67,20 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ "3.11", "3.12", "3.13" ] + python-version: [ "3.11", "3.12", "3.13", "3.14" ] requirements-file: [ dj42_cms41.txt, dj52_cms41.txt, dj52_cms50.txt, + dj60_cms50.txt, ] + exclude: + - requirements-file: dj42_cms41.txt + python-version: "3.14" + - requirements-file: dj52_cms41.txt + python-version: "3.14" + - requirements-file: dj52_cms50.txt + python-version: "3.14" services: postgres: @@ -103,17 +120,20 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ "3.11", "3.12", "3.13" ] + python-version: [ "3.11", "3.12", "3.13", "3.14" ] requirements-file: [ dj42_cms41.txt, dj52_cms41.txt, dj52_cms50.txt, + dj60_cms50.txt, ] exclude: - - requirements-file: dj50_cms41.txt - python-version: 3.9 - - requirements-file: dj51_cms41.txt - python-version: 3.9 + - requirements-file: dj42_cms41.txt + python-version: "3.14" + - requirements-file: dj52_cms41.txt + python-version: "3.14" + - requirements-file: dj52_cms50.txt + python-version: "3.14" services: mysql: @@ -186,14 +206,14 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ "3.12" ] + python-version: [ "3.13" ] cms-version: [ 'https://github.com/django-cms/django-cms/archive/main.tar.gz' ] django-version: [ 'https://github.com/django/django/archive/main.tar.gz' ] - requirements-file: ['dj52_cms50.txt'] + requirements-file: ['dj60_cms50.txt'] steps: - uses: actions/checkout@v5 diff --git a/pyproject.toml b/pyproject.toml index 251df154..81aa8ac6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" name = "djangocms-versioning" description = "Versioning for django CMS" # Dies muss manuell aktualisiert werden, da pyproject.toml keine dynamische Beschreibung unterstützt readme = "README.rst" -requires-python = ">=3.6" +requires-python = ">=3.10" license = {text = "BSD License"} authors = [ {name = "Divio AG", email = "info@divio.ch"}, @@ -20,10 +20,17 @@ classifiers = [ "Framework :: Django :: 5.0", "Framework :: Django :: 5.1", "Framework :: Django :: 5.2", + "Framework :: Django :: 6.0", "Framework :: Django CMS :: 4.1", "Framework :: Django CMS :: 5.0", "Intended Audience :: Developers", "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "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 :: Software Development", ] dependencies = [ diff --git a/tests/requirements/dj60_cms50.txt b/tests/requirements/dj60_cms50.txt new file mode 100644 index 00000000..fe5bc2b3 --- /dev/null +++ b/tests/requirements/dj60_cms50.txt @@ -0,0 +1,8 @@ +-r requirements_base.txt + +django-cms>=5.0,<5.1 + +Django>=6.0,<6.1 +django-classy-tags +django-fsm>=2.6,<3 +django-sekizai diff --git a/tox.ini b/tox.ini index 52c40c51..435fb8a3 100644 --- a/tox.ini +++ b/tox.ini @@ -1,8 +1,8 @@ [tox] envlist = ruff - py{39.310,311}-dj{32,40,41,42}-sqlite - py{311,312}-djmain-cms-develop4-sqlite + py{310,311,312,313,314}-dj{42,50,51,52}-sqlite + py{312,313}-djmain-cms-develop4-sqlite skip_missing_interpreters=True @@ -10,17 +10,20 @@ skip_missing_interpreters=True deps = -r{toxinidir}/tests/requirements/requirements_base.txt - dj32: -r{toxinidir}/tests/requirements/dj32_cms41.txt - dj40: -r{toxinidir}/tests/requirements/dj40_cms41.txt - dj41: -r{toxinidir}/tests/requirements/dj41_cms41.txt dj42: -r{toxinidir}/tests/requirements/dj42_cms41.txt + dj50: -r{toxinidir}/tests/requirements/dj50_cms41.txt + dj51: -r{toxinidir}/tests/requirements/dj51_cms41.txt + dj52: -r{toxinidir}/tests/requirements/dj52_cms50.txt + dj60: -r{toxinidir}/tests/requirements/dj60_cms50.txt djmain: https://github.com/django/django/archive/main.tar.gz develop4: https://github.com/django-cms/django-cms/archive/develop-4.tar.gz basepython = - py39: python3.9 py310: python3.10 py311: python3.11 + py312: python3.12 + py313: python3.13 + py314: python3.14 commands = {envpython} --version From a39b03b2cf3baaa53771a69ed19c357874f7c4ac Mon Sep 17 00:00:00 2001 From: Vinit kumar Date: Sat, 25 Oct 2025 03:32:56 +0530 Subject: [PATCH 02/14] Modernize type annotations for Python 3.10+ - Replace typing.Union[X, Y] with X | Y syntax (PEP 604) - Replace typing.Optional[X] with X | None syntax - Remove unnecessary typing imports where Optional/Union were the only imports - Updated files: admin.py, emails.py, conditions.py, helpers.py, cms_toolbars.py, indicators.py, datastructures.py Amp-Thread-ID: https://ampcode.com/threads/T-cdf01cfa-6da3-4851-9a9b-0c7a2c181c37 Co-authored-by: Amp --- AGENTS.md | 183 +++++++++++++++++++++++++ djangocms_versioning/admin.py | 6 +- djangocms_versioning/cms_toolbars.py | 5 +- djangocms_versioning/conditions.py | 2 +- djangocms_versioning/datastructures.py | 16 +-- djangocms_versioning/emails.py | 2 +- djangocms_versioning/helpers.py | 7 +- djangocms_versioning/indicators.py | 4 +- 8 files changed, 203 insertions(+), 22 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..db80e45c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,183 @@ +# AGENTS.md + +This file contains information for AI assistants working with the djangocms-versioning codebase. + + +Use ast-grep and gh cli tools freely. + +## Project Overview + +django CMS Versioning is a Django package that provides versioning capabilities for django CMS 4.0+. It's a hybrid Python/JavaScript project with a Django backend and frontend assets built with webpack and gulp. + +## Frequently Used Commands + +### Testing +```bash +# Install test dependencies +pip install -r tests/requirements.txt + +# Run all tests +python setup.py test + +# Run tests with coverage +coverage erase +coverage run setup.py test +coverage report + +# Run tests with tox (multiple Python/Django versions) +tox + +# Run specific tox environment +tox -e py311-dj42-sqlite +``` + +### Linting and Code Quality +```bash +# Run ruff linter (this is the primary linter) +ruff djangocms_versioning +ruff tests + +# Run ruff with auto-fix +ruff --fix djangocms_versioning tests + +# Run pre-commit hooks (automatically runs on commit) +pre-commit run --all-files + +# Run ESLint for JavaScript +npm run lint # or gulp lint +``` + +### Frontend Development +```bash +# Install JavaScript dependencies +npm install + +# Build frontend assets +npm run build # or use gulp/webpack directly +``` + +### Database Migrations +```bash +# Run migrations +python manage.py migrate djangocms_versioning + +# Create versions for existing database (only for migration from non-versioned setup) +python manage.py create_versions --userid + +# Compile translations +python manage.py compilemessages +``` + +### Documentation +```bash +# Generate HTML documentation +cd docs/ +make html +# Output will be in docs/_build/html/ +``` + +### Translations +```bash +# Update translations from Transifex (requires transifex CLI) +tx pull + +# Compile message files +python manage.py compilemessages +``` + +## Code Style and Conventions + +### Python +- **Linter**: ruff (configured in pyproject.toml) +- **Line length**: 120 characters +- **Import style**: Use `isort` via ruff (combine-as-imports = true) +- **Code quality**: Follows ruff rules including pycodestyle, pyflakes, flake8-bugbear, and pyupgrade +- **Type hints**: Prefer modern Python type annotations +- **Django version**: Support Django 4.2, 5.0, 5.1, 5.2 +- **Python version**: Python 3.9+ + +### JavaScript +- **Linter**: ESLint (configured in .eslintrc.js) +- **Bundler**: webpack 3.x +- **Task runner**: gulp +- **Transpiler**: Babel with env preset +- **Dependencies**: jQuery, lodash (debounce, memoize), nprogress + +### General +- Use pre-commit hooks for automatic code quality checks +- Follow existing patterns in the codebase +- Migrations go in `djangocms_versioning/migrations/` +- Templates go in `djangocms_versioning/templates/` +- Static files go in `djangocms_versioning/static/` + +## Project Structure + +``` +djangocms-versioning/ +├── djangocms_versioning/ # Main package +│ ├── admin.py # Django admin configuration +│ ├── models.py # Database models +│ ├── forms.py # Django forms +│ ├── managers.py # Custom model managers +│ ├── signals.py # Django signals +│ ├── handlers.py # Signal handlers +│ ├── versionables.py # Versionable configuration +│ ├── cms_config.py # CMS app configuration +│ ├── cms_toolbars.py # Toolbar customization +│ ├── management/ # Management commands +│ ├── templates/ # Django templates +│ ├── static/ # CSS, JS, images +│ ├── locale/ # Translations +│ ├── test_utils/ # Test utilities and example app (polls) +│ └── migrations/ # Database migrations +├── tests/ # Test suite +│ └── requirements/ # Test dependency specifications +├── docs/ # Documentation (Sphinx) +├── pyproject.toml # Python package configuration +├── setup.py # Legacy setup file +├── test_settings.py # Django settings for tests +├── tox.ini # Tox configuration +├── package.json # JavaScript dependencies +├── webpack.config.js # Webpack configuration +├── gulpfile.js # Gulp tasks +└── .pre-commit-config.yaml # Pre-commit hooks +``` + +## Key Dependencies + +### Python +- Django >= 4.2 +- django-cms >= 4.1.1 +- django-fsm < 3 +- packaging + +### JavaScript +- jquery ^3.3.1 +- webpack ^3.0.0 +- babel ^6.x +- gulp 3.9.1 + +## Testing Strategy + +- Tests are in the `tests/` directory +- Use Django's test framework +- Test against multiple Python versions (3.9, 3.10, 3.11) +- Test against multiple Django versions (4.2, 5.0, 5.1, 5.2) +- Example implementation in `djangocms_versioning/test_utils/polls/` +- Coverage configured to omit migrations, test utils, and tests themselves + +## Integration Notes + +- Versioning integration docs: `docs/versioning_integration.rst` +- Example implementation: `djangocms_versioning/test_utils/polls/cms_config.py` +- Requires django CMS 4.0 or higher +- Uses django-fsm for state machine functionality +- Frontend integrates with django CMS toolbar system + +## Common Patterns + +- **Versionable models**: See `test_utils/polls/models.py` and `test_utils/polls/cms_config.py` +- **Admin customization**: Versioning-specific admin in `admin.py` +- **State management**: Uses django-fsm for version states +- **Signals**: Version lifecycle signals in `signals.py` +- **Templates**: Override in `templates/djangocms_versioning/` diff --git a/djangocms_versioning/admin.py b/djangocms_versioning/admin.py index e008b202..5478e988 100644 --- a/djangocms_versioning/admin.py +++ b/djangocms_versioning/admin.py @@ -308,7 +308,7 @@ def get_queryset(self, request: HttpRequest) -> models.QuerySet: description=_("State"), ordering="content_state", ) - def get_versioning_state(self, obj: models.Model) -> typing.Union[str, None]: + def get_versioning_state(self, obj: models.Model) -> str | None: """Returns verbose text of objects versioning state. This is a text column without user interaction. Typically, either ``get_versioning_state`` or ``state_indicator`` (provided by the :class:`~djangocms_versioning.admin.StateIndicatorMixin`) is used. The state indicator @@ -322,7 +322,7 @@ def get_versioning_state(self, obj: models.Model) -> typing.Union[str, None]: description=_("Author"), ordering="content_created_by_sort", ) - def get_author(self, obj: models.Model) -> typing.Union[str, None]: + def get_author(self, obj: models.Model) -> str | None: """ Return the author who created a version :param obj: Versioned grouper model instance annotated with its author username @@ -336,7 +336,7 @@ def get_author(self, obj: models.Model) -> typing.Union[str, None]: description=_("Modified"), ordering="content_modified", ) - def get_modified_date(self, obj: models.Model) -> typing.Union[str, None]: + def get_modified_date(self, obj: models.Model) -> str | None: """ Get the last modified date of a version :param obj: Versioned grouper model instance annotated with its modified datetime diff --git a/djangocms_versioning/cms_toolbars.py b/djangocms_versioning/cms_toolbars.py index f4a29df0..2ccc73d8 100644 --- a/djangocms_versioning/cms_toolbars.py +++ b/djangocms_versioning/cms_toolbars.py @@ -1,6 +1,5 @@ from collections import OrderedDict from copy import copy -from typing import Optional from cms import __version__ as cms_version from cms.cms_toolbars import ( @@ -297,10 +296,10 @@ class VersioningPageToolbar(PageToolbar): """ def __init__(self, *args, **kwargs): - self.page_content: Optional[PageContent] = None + self.page_content: PageContent | None = None super().__init__(*args, **kwargs) - def get_page_content(self, language: Optional[str] = None) -> PageContent: + def get_page_content(self, language: str | None = None) -> PageContent: # This method overwrites the method in django CMS core. Not necessary # for django CMS 4.2+ if not language: diff --git a/djangocms_versioning/conditions.py b/djangocms_versioning/conditions.py index 5e5fbabd..a664305e 100644 --- a/djangocms_versioning/conditions.py +++ b/djangocms_versioning/conditions.py @@ -11,7 +11,7 @@ class Conditions(list): def __add__(self, other: list) -> "Conditions": return Conditions(super().__add__(other)) - def __get__(self, instance: object, cls) -> typing.Union["Conditions", "BoundConditions"]: + def __get__(self, instance: object, cls) -> "Conditions | BoundConditions": if instance: return BoundConditions(self, instance) return self diff --git a/djangocms_versioning/datastructures.py b/djangocms_versioning/datastructures.py index ba672825..f4e875a4 100644 --- a/djangocms_versioning/datastructures.py +++ b/djangocms_versioning/datastructures.py @@ -1,6 +1,6 @@ from collections.abc import Iterable from itertools import chain -from typing import Any, Optional +from typing import Any from cms.extensions.models import BaseExtension from cms.models import Placeholder, PlaceholderRelationField @@ -16,7 +16,7 @@ class BaseVersionableItem: concrete = False - def __init__(self, content_model: type[models.Model], content_admin_mixin: Optional[type] = None): + def __init__(self, content_model: type[models.Model], content_admin_mixin: type | None = None): self.content_model = content_model self.content_admin_mixin = content_admin_mixin or VersioningAdminMixin @@ -29,15 +29,15 @@ def __init__( content_model: type[models.Model], grouper_field_name: str, copy_function: callable, - extra_grouping_fields: Optional[Iterable[str]] = None, - version_list_filter_lookups: Optional[dict[str, Any]] = None, + extra_grouping_fields: Iterable[str] | None = None, + version_list_filter_lookups: dict[str, Any] | None = None, on_publish=None, on_unpublish=None, on_draft_create=None, on_archive=None, grouper_selector_option_label=False, - grouper_admin_mixin: Optional[type] = None, - content_admin_mixin: Optional[type] = None, + grouper_admin_mixin: type | None = None, + content_admin_mixin: type | None = None, preview_url=None, ): super().__init__(content_model, content_admin_mixin) @@ -161,7 +161,7 @@ def grouper_choices_queryset(self) -> models.QuerySet: cache_name = self.grouper_field.remote_field.get_accessor_name() return self.grouper_model._base_manager.prefetch_related(models.Prefetch(cache_name, queryset=content_objects)) - def get_grouper_with_fallbacks(self, grouper_id) -> Optional[models.Model]: + def get_grouper_with_fallbacks(self, grouper_id) -> models.Model | None: return self.grouper_choices_queryset().filter(pk=grouper_id).first() def _get_content_types(self) -> set[int]: @@ -193,7 +193,7 @@ class VersionableItemAlias(BaseVersionableItem): """ def __init__( - self, content_model: type[models.Model], to: BaseVersionableItem, content_admin_mixin: Optional[type] = None + self, content_model: type[models.Model], to: BaseVersionableItem, content_admin_mixin: type | None = None ): super().__init__(content_model, content_admin_mixin) self.to = to diff --git a/djangocms_versioning/emails.py b/djangocms_versioning/emails.py index 58cb9981..0c4659f4 100644 --- a/djangocms_versioning/emails.py +++ b/djangocms_versioning/emails.py @@ -11,7 +11,7 @@ from djangocms_versioning.helpers import send_email -def get_full_url(location: str, site: typing.Union[Site, None] = None) -> str: +def get_full_url(location: str, site: Site | None = None) -> str: if not site: site = Site.objects.get_current() diff --git a/djangocms_versioning/helpers.py b/djangocms_versioning/helpers.py index 03e804b9..ed163f3a 100644 --- a/djangocms_versioning/helpers.py +++ b/djangocms_versioning/helpers.py @@ -2,7 +2,6 @@ import warnings from collections.abc import Iterable from contextlib import contextmanager -from typing import Optional from cms.models import Page, PageContent, Placeholder from cms.toolbar.utils import get_object_edit_url, get_object_preview_url @@ -71,7 +70,7 @@ def _replace_admin_for_model(modeladmin: type[admin.ModelAdmin], mixin: type, ad admin_site.register(modeladmin.model, new_admin_class) -def replace_admin_for_models(pairs: tuple[type[models.Model], type], admin_site: Optional[admin.AdminSite] = None): +def replace_admin_for_models(pairs: tuple[type[models.Model], type], admin_site: admin.AdminSite | None = None): """ :param models: List of (model class, admin mixin class) tuples :param admin_site: AdminSite instance @@ -86,7 +85,7 @@ def replace_admin_for_models(pairs: tuple[type[models.Model], type], admin_site: _replace_admin_for_model(modeladmin, mixin, admin_site) -def register_versionadmin_proxy(versionable, admin_site: Optional[admin.AdminSite] = None): +def register_versionadmin_proxy(versionable, admin_site: admin.AdminSite | None = None): """Creates a model admin class based on `VersionAdmin` and registers it with `admin_site` for `versionable.version_model_proxy`. @@ -281,7 +280,7 @@ def get_content_types_with_subclasses(models: Iterable[type[models.Model]], usin def get_preview_url( - content_obj: models.Model, language: Optional[str] = None + content_obj: models.Model, language: str | None = None ) -> str: """If the object is editable the cms preview view should be used, with the toolbar. This method provides the URL for it. It falls back the standard change view diff --git a/djangocms_versioning/indicators.py b/djangocms_versioning/indicators.py index a23ebd13..e9a03484 100644 --- a/djangocms_versioning/indicators.py +++ b/djangocms_versioning/indicators.py @@ -92,8 +92,8 @@ def content_indicator_menu(request, status, versions, back=""): def content_indicator( content_obj: models.Model, - versions: typing.Optional[list[Version]] = None -) -> typing.Optional[str]: + versions: list[Version] | None = None +) -> str | None: """Translates available versions into status to be reflected by the indicator. Function caches the result in the page_content object""" From b72a02ce903f349ba864622b082183739000c2b1 Mon Sep 17 00:00:00 2001 From: Vinit kumar Date: Sat, 25 Oct 2025 03:33:16 +0530 Subject: [PATCH 03/14] Remove unused typing imports after modernization - Removed unused typing imports from admin.py, conditions.py, emails.py, and indicators.py - These imports became unused after migrating to PEP 604 union syntax --- djangocms_versioning/admin.py | 1 - djangocms_versioning/conditions.py | 1 - djangocms_versioning/emails.py | 1 - djangocms_versioning/indicators.py | 1 - 4 files changed, 4 deletions(-) diff --git a/djangocms_versioning/admin.py b/djangocms_versioning/admin.py index 5478e988..56aa282e 100644 --- a/djangocms_versioning/admin.py +++ b/djangocms_versioning/admin.py @@ -1,5 +1,4 @@ import json -import typing import warnings from collections import OrderedDict from urllib.parse import urlparse diff --git a/djangocms_versioning/conditions.py b/djangocms_versioning/conditions.py index a664305e..bca421c0 100644 --- a/djangocms_versioning/conditions.py +++ b/djangocms_versioning/conditions.py @@ -1,4 +1,3 @@ -import typing from django.conf import settings diff --git a/djangocms_versioning/emails.py b/djangocms_versioning/emails.py index 0c4659f4..56a50e6e 100644 --- a/djangocms_versioning/emails.py +++ b/djangocms_versioning/emails.py @@ -1,4 +1,3 @@ -import typing from urllib.parse import urljoin from cms.toolbar.utils import get_object_preview_url diff --git a/djangocms_versioning/indicators.py b/djangocms_versioning/indicators.py index e9a03484..6c6dfe5c 100644 --- a/djangocms_versioning/indicators.py +++ b/djangocms_versioning/indicators.py @@ -1,4 +1,3 @@ -import typing from cms.utils.urlutils import admin_reverse from django.contrib.auth import get_permission_codename From 2a62681ceaf1849d3299ae2cbe4d58fb3e1f9144 Mon Sep 17 00:00:00 2001 From: Vinit kumar Date: Sat, 25 Oct 2025 03:33:45 +0530 Subject: [PATCH 04/14] Update AGENTS.md with new Python and Django version requirements - Updated Python version requirement from 3.9+ to 3.10+ - Added Django 6.0 to supported versions - Updated test matrix to reflect Python 3.10-3.14 - Added note about PEP 604 union syntax in type hints --- AGENTS.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index db80e45c..900c0795 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -92,9 +92,9 @@ python manage.py compilemessages - **Line length**: 120 characters - **Import style**: Use `isort` via ruff (combine-as-imports = true) - **Code quality**: Follows ruff rules including pycodestyle, pyflakes, flake8-bugbear, and pyupgrade -- **Type hints**: Prefer modern Python type annotations -- **Django version**: Support Django 4.2, 5.0, 5.1, 5.2 -- **Python version**: Python 3.9+ +- **Type hints**: Prefer modern Python type annotations (PEP 604 union syntax: `X | Y` instead of `Union[X, Y]`) +- **Django version**: Support Django 4.2, 5.0, 5.1, 5.2, 6.0 +- **Python version**: Python 3.10+ ### JavaScript - **Linter**: ESLint (configured in .eslintrc.js) @@ -161,8 +161,8 @@ djangocms-versioning/ - Tests are in the `tests/` directory - Use Django's test framework -- Test against multiple Python versions (3.9, 3.10, 3.11) -- Test against multiple Django versions (4.2, 5.0, 5.1, 5.2) +- Test against multiple Python versions (3.10, 3.11, 3.12, 3.13, 3.14) +- Test against multiple Django versions (4.2, 5.0, 5.1, 5.2, 6.0) - Example implementation in `djangocms_versioning/test_utils/polls/` - Coverage configured to omit migrations, test utils, and tests themselves From df87c88c822bbf5cda6976656eb537687f4bde34 Mon Sep 17 00:00:00 2001 From: Vinit kumar Date: Sat, 25 Oct 2025 03:39:36 +0530 Subject: [PATCH 05/14] Use uv for package management in CI workflows - Replace pip with uv for faster dependency installation - Add astral-sh/setup-uv@v5 action to all workflows - Use 'uv pip install --system' for package installation - Use 'uvx ruff' for linting workflow - Updated all test jobs: sqlite, postgres, mysql, cms-develop, django-main Amp-Thread-ID: https://ampcode.com/threads/T-cdf01cfa-6da3-4851-9a9b-0c7a2c181c37 Co-authored-by: Amp --- .github/workflows/lint.yml | 8 +++++-- .github/workflows/test.yml | 48 +++++++++++++++++++------------------- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e54e2822..f85da70a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -11,7 +11,11 @@ jobs: steps: - uses: actions/checkout@v5 - - run: python -Im pip install --user ruff + - name: Install uv + uses: astral-sh/setup-uv@v5 + + - name: Install ruff + run: uv tool install ruff - name: Run ruff - run: ruff check --output-format=github djangocms_versioning tests + run: uvx ruff check --output-format=github djangocms_versioning tests diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6fe3497a..12abcc56 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -46,15 +46,15 @@ jobs: steps: - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + - name: Install uv + uses: astral-sh/setup-uv@v5 - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install -r tests/requirements/${{ matrix.requirements-file }} - pip install -e . + uv pip install --system -r tests/requirements/${{ matrix.requirements-file }} + uv pip install --system -e . - name: Run coverage run: coverage run ./test_settings.py @@ -97,15 +97,15 @@ jobs: steps: - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + - name: Install uv + uses: astral-sh/setup-uv@v5 - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install -r tests/requirements/${{ matrix.requirements-file }} - python setup.py install + uv pip install --system -r tests/requirements/${{ matrix.requirements-file }} + uv pip install --system . - name: Run coverage run: coverage run ./test_settings.py @@ -148,15 +148,15 @@ jobs: steps: - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + - name: Install uv + uses: astral-sh/setup-uv@v5 - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install -r tests/requirements/${{ matrix.requirements-file }} - python setup.py install + uv pip install --system -r tests/requirements/${{ matrix.requirements-file }} + uv pip install --system . - name: Run coverage run: coverage run ./test_settings.py @@ -183,17 +183,17 @@ jobs: steps: - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + - name: Install uv + uses: astral-sh/setup-uv@v5 - name: Install dependencies run: | - python -m pip install --upgrade pip - python -m pip install -r tests/requirements/${{ matrix.requirements-file }} - python -m pip uninstall -y django-cms - python -m pip install ${{ matrix.cms-version }} - python setup.py install + uv pip install --system -r tests/requirements/${{ matrix.requirements-file }} + uv pip uninstall --system django-cms + uv pip install --system ${{ matrix.cms-version }} + uv pip install --system . - name: Run coverage run: coverage run ./test_settings.py @@ -218,17 +218,17 @@ jobs: steps: - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + - name: Install uv + uses: astral-sh/setup-uv@v5 - name: Install dependencies run: | - python -m pip install --upgrade pip - python -m pip install -r tests/requirements/${{ matrix.requirements-file }} - python -m pip uninstall -y Django django-cms - python -m pip install ${{ matrix.cms-version }} ${{ matrix.django-version }} - python setup.py install + uv pip install --system -r tests/requirements/${{ matrix.requirements-file }} + uv pip uninstall --system Django django-cms + uv pip install --system ${{ matrix.cms-version }} ${{ matrix.django-version }} + uv pip install --system . - name: Run coverage run: coverage run ./test_settings.py From 83dbd5e2d5d54bf6ba17dc0ce4e09d9c15e37a8e Mon Sep 17 00:00:00 2001 From: Vinit kumar Date: Sat, 25 Oct 2025 03:41:43 +0530 Subject: [PATCH 06/14] Update AGENTS.md to reflect usage of uv [skip ci] - Add note about using uv for faster dependency installation - Update testing command to use 'uv pip install' - Document uv as the package manager in Code Style section --- AGENTS.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 900c0795..56033bea 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,8 +13,8 @@ django CMS Versioning is a Django package that provides versioning capabilities ### Testing ```bash -# Install test dependencies -pip install -r tests/requirements.txt +# Install test dependencies (using uv for faster installation) +uv pip install -r tests/requirements.txt # Run all tests python setup.py test @@ -89,6 +89,7 @@ python manage.py compilemessages ### Python - **Linter**: ruff (configured in pyproject.toml) +- **Package manager**: uv (for faster dependency installation in CI and local development) - **Line length**: 120 characters - **Import style**: Use `isort` via ruff (combine-as-imports = true) - **Code quality**: Follows ruff rules including pycodestyle, pyflakes, flake8-bugbear, and pyupgrade From 763cb5cd754a5abfc938731143cfe477bb29d851 Mon Sep 17 00:00:00 2001 From: Vinit kumar Date: Sat, 25 Oct 2025 03:44:59 +0530 Subject: [PATCH 07/14] Fix type hint for replace_admin_for_models pairs parameter - Change from 'tuple[type[models.Model], type]' to 'Iterable[tuple[type[models.Model], type]]' - The function expects multiple pairs, not a single tuple - Also fixed docstring parameter name from 'models' to 'pairs' --- djangocms_versioning/helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/djangocms_versioning/helpers.py b/djangocms_versioning/helpers.py index ed163f3a..8271be44 100644 --- a/djangocms_versioning/helpers.py +++ b/djangocms_versioning/helpers.py @@ -70,9 +70,9 @@ def _replace_admin_for_model(modeladmin: type[admin.ModelAdmin], mixin: type, ad admin_site.register(modeladmin.model, new_admin_class) -def replace_admin_for_models(pairs: tuple[type[models.Model], type], admin_site: admin.AdminSite | None = None): +def replace_admin_for_models(pairs: Iterable[tuple[type[models.Model], type]], admin_site: admin.AdminSite | None = None): """ - :param models: List of (model class, admin mixin class) tuples + :param pairs: Iterable of (model class, admin mixin class) tuples :param admin_site: AdminSite instance """ if admin_site is None: From 4dc8c2d1236a8b42fc3d09ca3bc49645892453b4 Mon Sep 17 00:00:00 2001 From: Vinit kumar Date: Sat, 25 Oct 2025 03:46:44 +0530 Subject: [PATCH 08/14] Fix line length in replace_admin_for_models signature - Break function signature across multiple lines to comply with 120 char limit - Fixes ruff E501 error --- djangocms_versioning/helpers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/djangocms_versioning/helpers.py b/djangocms_versioning/helpers.py index 8271be44..78028ae0 100644 --- a/djangocms_versioning/helpers.py +++ b/djangocms_versioning/helpers.py @@ -70,7 +70,9 @@ def _replace_admin_for_model(modeladmin: type[admin.ModelAdmin], mixin: type, ad admin_site.register(modeladmin.model, new_admin_class) -def replace_admin_for_models(pairs: Iterable[tuple[type[models.Model], type]], admin_site: admin.AdminSite | None = None): +def replace_admin_for_models( + pairs: Iterable[tuple[type[models.Model], type]], admin_site: admin.AdminSite | None = None +): """ :param pairs: Iterable of (model class, admin mixin class) tuples :param admin_site: AdminSite instance From 36bcde4e0ba4b143183193f13b47b868f171b575 Mon Sep 17 00:00:00 2001 From: Vinit kumar Date: Sat, 25 Oct 2025 03:54:11 +0530 Subject: [PATCH 09/14] Fix test matrix: Django 6 now only runs with Python 3.12+ Amp-Thread-ID: https://ampcode.com/threads/T-6e53f75e-8813-4ef9-bcd2-61db1b0e0cc6 Co-authored-by: Amp --- .github/workflows/test.yml | 2 -- tox.ini | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 12abcc56..c6b98213 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,8 +30,6 @@ jobs: python-version: "3.10" - requirements-file: dj60_cms50.txt python-version: "3.11" - - requirements-file: dj60_cms50.txt - python-version: "3.12" - requirements-file: dj42_cms41.txt python-version: "3.14" - requirements-file: dj50_cms41.txt diff --git a/tox.ini b/tox.ini index 435fb8a3..649deab0 100644 --- a/tox.ini +++ b/tox.ini @@ -2,6 +2,7 @@ envlist = ruff py{310,311,312,313,314}-dj{42,50,51,52}-sqlite + py{312,313,314}-dj60-sqlite py{312,313}-djmain-cms-develop4-sqlite skip_missing_interpreters=True From bbef0c513ebba1694cc0bc5bf2531d1497090100 Mon Sep 17 00:00:00 2001 From: Vinit kumar Date: Sat, 25 Oct 2025 03:56:43 +0530 Subject: [PATCH 10/14] Update Django requirement to >=6.0a1,<6.1 Amp-Thread-ID: https://ampcode.com/threads/T-7c01575b-a01e-4212-bfb3-f4981bd1583b Co-authored-by: Amp --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 81aa8ac6..bb5352d3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,7 @@ classifiers = [ "Topic :: Software Development", ] dependencies = [ - "Django>=4.2", + "Django>=6.0a1,<6.1", "django-cms>=4.1.1", "django-fsm<3", "packaging", From d44015be8871156195f5552ed34881127fd8dcaa Mon Sep 17 00:00:00 2001 From: Vinit kumar Date: Sat, 25 Oct 2025 03:58:50 +0530 Subject: [PATCH 11/14] Update Django requirement to >=6.0a1,<6.1 Amp-Thread-ID: https://ampcode.com/threads/T-1ae0efd4-afab-468c-b53b-30d0573c635e Co-authored-by: Amp --- tests/requirements/dj60_cms50.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/requirements/dj60_cms50.txt b/tests/requirements/dj60_cms50.txt index fe5bc2b3..44bb2ae1 100644 --- a/tests/requirements/dj60_cms50.txt +++ b/tests/requirements/dj60_cms50.txt @@ -2,7 +2,7 @@ django-cms>=5.0,<5.1 -Django>=6.0,<6.1 +Django>=6.0a1,<6.1 django-classy-tags django-fsm>=2.6,<3 django-sekizai From 1fc04ac774b8cfcf02611f3f4636393de84254e7 Mon Sep 17 00:00:00 2001 From: Vinit kumar Date: Sat, 25 Oct 2025 17:07:44 +0530 Subject: [PATCH 12/14] fix: issues with matrix --- .github/workflows/test.yml | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c6b98213..0a9f3b23 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -65,16 +65,30 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ "3.11", "3.12", "3.13", "3.14" ] + python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14" ] requirements-file: [ dj42_cms41.txt, + dj50_cms41.txt, + dj51_cms41.txt, dj52_cms41.txt, dj52_cms50.txt, dj60_cms50.txt, - ] + ] exclude: + - requirements-file: dj52_cms41.txt + python-version: "3.10" + - requirements-file: dj52_cms50.txt + python-version: "3.10" + - requirements-file: dj60_cms50.txt + python-version: "3.10" + - requirements-file: dj60_cms50.txt + python-version: "3.11" - requirements-file: dj42_cms41.txt python-version: "3.14" + - requirements-file: dj50_cms41.txt + python-version: "3.14" + - requirements-file: dj51_cms41.txt + python-version: "3.14" - requirements-file: dj52_cms41.txt python-version: "3.14" - requirements-file: dj52_cms50.txt @@ -118,16 +132,30 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ "3.11", "3.12", "3.13", "3.14" ] + python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14" ] requirements-file: [ dj42_cms41.txt, + dj50_cms41.txt, + dj51_cms41.txt, dj52_cms41.txt, dj52_cms50.txt, dj60_cms50.txt, - ] + ] exclude: + - requirements-file: dj52_cms41.txt + python-version: "3.10" + - requirements-file: dj52_cms50.txt + python-version: "3.10" + - requirements-file: dj60_cms50.txt + python-version: "3.10" + - requirements-file: dj60_cms50.txt + python-version: "3.11" - requirements-file: dj42_cms41.txt python-version: "3.14" + - requirements-file: dj50_cms41.txt + python-version: "3.14" + - requirements-file: dj51_cms41.txt + python-version: "3.14" - requirements-file: dj52_cms41.txt python-version: "3.14" - requirements-file: dj52_cms50.txt From 5252aa08730ef808a6b6daa6d176575187020e8d Mon Sep 17 00:00:00 2001 From: Vinit kumar Date: Sat, 25 Oct 2025 18:45:01 +0530 Subject: [PATCH 13/14] fix: wrong Django version in pyproject.toml --- .github/workflows/test.yml | 6 ++++++ pyproject.toml | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0a9f3b23..2b5c238c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -51,6 +51,12 @@ jobs: uses: astral-sh/setup-uv@v5 - name: Install dependencies run: | + sudo apt install gettext gcc -y + sudo apt install gettext gcc -y + sudo apt install gettext gcc -y + python -m pip install --upgrade pip uv + python -m pip install --upgrade pip uv + python -m pip install --upgrade pip uv uv pip install --system -r tests/requirements/${{ matrix.requirements-file }} uv pip install --system -e . diff --git a/pyproject.toml b/pyproject.toml index bb5352d3..d05dbec0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,6 @@ classifiers = [ "Topic :: Software Development", ] dependencies = [ - "Django>=6.0a1,<6.1", "django-cms>=4.1.1", "django-fsm<3", "packaging", From a095f332b1825367592a9e329d011f2af4fab6f7 Mon Sep 17 00:00:00 2001 From: Vinit kumar Date: Sat, 25 Oct 2025 18:56:40 +0530 Subject: [PATCH 14/14] fix: mysqlclient errors with python 3.14 --- .github/workflows/test.yml | 4 ++++ tests/requirements/requirements_base.txt | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c1cb23ca..553b315f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -99,6 +99,8 @@ jobs: python-version: "3.14" - requirements-file: dj52_cms50.txt python-version: "3.14" + - requirements-file: dj60_cms50.txt + python-version: "3.14" services: postgres: @@ -166,6 +168,8 @@ jobs: python-version: "3.14" - requirements-file: dj52_cms50.txt python-version: "3.14" + - requirements-file: dj60_cms50.txt + python-version: "3.14" services: mysql: diff --git a/tests/requirements/requirements_base.txt b/tests/requirements/requirements_base.txt index 1d3793f0..fbd0213d 100644 --- a/tests/requirements/requirements_base.txt +++ b/tests/requirements/requirements_base.txt @@ -10,7 +10,7 @@ mock pillow pyflakes>=2.1.1 python-dateutil -mysqlclient==2.0.3 +mysqlclient>=2.2.1 psycopg2 setuptools