Skip to content

Fix cron-descriptor >= 2.0 compatibility#1009

Merged
auvipy merged 8 commits into
celery:mainfrom
EliShteinman:fix/cron-descriptor-v2-compat
Mar 26, 2026
Merged

Fix cron-descriptor >= 2.0 compatibility#1009
auvipy merged 8 commits into
celery:mainfrom
EliShteinman:fix/cron-descriptor-v2-compat

Conversation

@EliShteinman

Copy link
Copy Markdown
Contributor

Summary

cron-descriptor 2.0 renamed exception classes (FormatExceptionFormatError, etc.). PR #935 temporarily capped the dependency to <2.0.0 to avoid ImportError on deploy (#937). This PR applies the real fix:

  • Try/except import for the renamed exceptions — supports both cron-descriptor v1.x (old *Exception names) and v2.x (new *Error names)
  • Removes the <2.0.0 version cap from requirements/default.txt

The except ImportError: fallback is already excluded from coverage reporting in pyproject.toml (line 136), so patch coverage should not be affected.

Related

Compatibility matrix

cron-descriptor *Exception (old) *Error (new)
1.x YES NO
2.0.3–2.0.5 NO YES
2.0.6+ YES (alias) YES

Test plan

  • All unit tests pass locally (125/125, excluding test_run_task which requires RabbitMQ)
  • Tested with cron-descriptor 1.4.5 and 2.0.6
  • ruff linter passes
  • CI passes with full matrix (Python 3.9–3.14, Django 3.2–6.0)

cron-descriptor 2.0 renamed FormatException, MissingFieldException,
and WrongArgumentException to FormatError, MissingFieldError, and
WrongArgumentError. Versions 2.0.3-2.0.5 only export the new names,
while 2.0.6+ provides both as aliases.

Use try/except ImportError to import new names first, falling back
to old names for cron-descriptor <2.0. Remove the <2.0.0 version cap.

Fixes celery#934
Add clarifying comment, extract get_description from try/except since
it was not renamed in cron-descriptor v2.
Copilot AI review requested due to automatic review settings March 6, 2026 11:17

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates django-celery-beat to be compatible with cron-descriptor v2.x (which renamed exception classes) while retaining compatibility with v1.x, and removes the temporary dependency cap that blocked v2 upgrades.

Changes:

  • Add dual-import shim in django_celery_beat/models.py to support both *Exception (v1) and *Error (v2) cron-descriptor exception names.
  • Update the human_readable exception handling to catch the unified *Error names.
  • Remove the <2.0.0 upper cap for cron-descriptor in requirements/default.txt.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
requirements/default.txt Removes the temporary <2.0.0 cap on cron-descriptor to allow v2.x installs.
django_celery_beat/models.py Adds backward/forward-compatible exception imports and updates exception handling in human_readable.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread requirements/default.txt Outdated
tzdata
python-crontab>=2.3.4
cron-descriptor>=1.2.32,<2.0.0
cron-descriptor>=1.2.32

Copilot AI Mar 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cron-descriptor is now unbounded above. Given the project already caps other major dependencies (e.g., celery<6.0, Django<6.1) and cron-descriptor has already introduced a breaking change at v2, consider adding an upper bound like <3.0.0 to avoid an untested future major release breaking installs again while still allowing v2.x.

Suggested change
cron-descriptor>=1.2.32
cron-descriptor>=1.2.32,<3.0.0

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point — added <3.0.0 upper bound in 088355d to stay consistent with the other capped dependencies.

Cap at <3.0.0 to stay consistent with other dependencies in the
project and guard against future breaking changes.
@auvipy auvipy self-requested a review March 9, 2026 15:17

@auvipy auvipy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please check the build failures

@codecov

codecov Bot commented Mar 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.64%. Comparing base (aff5b71) to head (0301888).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1009      +/-   ##
==========================================
+ Coverage   87.58%   87.64%   +0.06%     
==========================================
  Files          32       32              
  Lines        1007     1012       +5     
  Branches       81       81              
==========================================
+ Hits          882      887       +5     
  Misses        107      107              
  Partials       18       18              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

cron-descriptor v2 changed the default to 24h format, causing
test_long_name to fail. Explicitly set use_24hour_time_format=False
via a module-level Options constant.
- Verify human_readable uses 12-hour format (PM for hour 14)
- Cover FormatError, MissingFieldError, WrongArgumentError fallback paths
@EliShteinman

Copy link
Copy Markdown
Contributor Author

Fixed the CI failure — cron-descriptor v2 changed the default time format from 12h to 24h (At 02:00 instead of At 02:00 AM), which broke test_long_name.

Changes in this push:

  • Explicitly set use_24hour_time_format=False via a module-level Options constant to preserve the existing 12h output
  • Added tests for the 12h format and for each exception fallback path (FormatError, MissingFieldError, WrongArgumentError)

pre-commit-ci Bot and others added 2 commits March 9, 2026 17:44
Replace @patch decorator with `with patch(...)` context manager
to avoid unused fixture parameter lint error.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

from cron_descriptor import (FormatException as FormatError,
MissingFieldException as MissingFieldError,
WrongArgumentException as WrongArgumentError)

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change stops exporting the old *Exception symbols from django_celery_beat.models (they were previously imported directly). If any downstream code imports FormatException/MissingFieldException/WrongArgumentException from this module, it will now break. Consider keeping backward-compatible aliases (e.g., bind the old names to the new *Error symbols) so both naming schemes continue to work.

Suggested change
# Backwards compatibility: re-export legacy *Exception names
FormatException = FormatError
MissingFieldException = MissingFieldError
WrongArgumentException = WrongArgumentError

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These exceptions are internal to cron-descriptor and are only used privately inside human_readable. They were never part of django-celery-beat's public API — no downstream code should be importing them from django_celery_beat.models.

If anyone needs these exceptions, the correct import is directly from cron_descriptor, not from this module. Adding re-export aliases would actually make things worse by encouraging an incorrect import path.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@EliShteinman

Copy link
Copy Markdown
Contributor Author

@auvipy Build failures are fixed — all 18 CI jobs pass (Python 3.9–3.14, Django 3.2–6.0).

The last push adds pragma: no cover on the except ImportError fallback for the cron-descriptor v1 import path. This block can't be exercised in CI since each run installs a single version of cron-descriptor. The project already excludes except ImportError: lines in pyproject.toml, but that only skips the line itself, not the block body — pragma: no cover covers the entire block.

Ready for re-review when you get a chance.

@auvipy auvipy merged commit 6b6a026 into celery:main Mar 26, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Issue with the cron-descriptor cron_descriptor needs to be locked down as the new release changes the signature.

3 participants