fix(cron): accept named months/weekdays in cron schedules
`parse_schedule()` detected cron expressions with a digit-only field pattern (`^[\d\*\-,/]+$`), so any field using named months or weekdays — `MON`, `JAN`, and common ranges/lists like `MON-FRI` or `MON,WED,FRI` — failed detection and fell through to a confusing "Invalid schedule" error, even though croniter supports them and they're standard cron. Allow letters in the field pattern so these route to croniter for validation. Truly-invalid expressions (`0 9 * * FUNDAY`, `99 9 * * MON`) are still rejected there with a clear "Invalid cron expression" message; duration/interval/ISO parsing is unchanged. Adds tests for named weekdays/months (incl. ranges and lists) and that an invalid named field is still rejected.
This commit is contained in:
+5
-1
@@ -960,9 +960,13 @@ def parse_schedule(schedule: str) -> Dict[str, Any]:
|
||||
|
||||
# Check for cron expression (5 or 6 space-separated fields)
|
||||
# Cron fields: minute hour day month weekday [year]
|
||||
# Allow letters so named months/weekdays (JAN-DEC, MON-SUN, incl. ranges
|
||||
# and lists like MON-FRI or MON,WED,FRI) are routed to croniter, which
|
||||
# supports them. The previous digit-only pattern silently rejected these
|
||||
# valid expressions as "Invalid schedule".
|
||||
parts = schedule.split()
|
||||
if len(parts) >= 5 and all(
|
||||
re.match(r'^[\d\*\-,/]+$', p) for p in parts[:5]
|
||||
re.match(r'^[A-Za-z\d\*\-,/]+$', p) for p in parts[:5]
|
||||
):
|
||||
if not _ensure_croniter():
|
||||
raise ValueError("Cron expressions require 'croniter' package. Install with: pip install croniter")
|
||||
|
||||
@@ -180,6 +180,25 @@ class TestParseSchedule:
|
||||
assert result["kind"] == "cron"
|
||||
assert result["expr"] == "0 9 * * *"
|
||||
|
||||
def test_cron_named_weekdays_and_months(self):
|
||||
# Named months/weekdays (and ranges/lists) are valid cron and must
|
||||
# route to croniter, not be rejected as "Invalid schedule".
|
||||
pytest.importorskip("croniter")
|
||||
for expr in (
|
||||
"0 9 * * MON",
|
||||
"*/15 9-17 * * MON-FRI",
|
||||
"0 9 1 JAN *",
|
||||
"0 9 * * MON,WED,FRI",
|
||||
):
|
||||
result = parse_schedule(expr)
|
||||
assert result["kind"] == "cron", expr
|
||||
assert result["expr"] == expr
|
||||
|
||||
def test_invalid_named_cron_still_rejected(self):
|
||||
pytest.importorskip("croniter")
|
||||
with pytest.raises(ValueError):
|
||||
parse_schedule("0 9 * * FUNDAY")
|
||||
|
||||
def test_iso_timestamp(self):
|
||||
result = parse_schedule("2030-01-15T14:00:00")
|
||||
assert result["kind"] == "once"
|
||||
|
||||
Reference in New Issue
Block a user