Skip to content

Commit

Permalink
Fixed AndTrigger skipping run times (#914)
Browse files Browse the repository at this point in the history
Removed redundant `self._next_triggers` construction and added `AndTrigger` test.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Alex Grönholm <[email protected]>
  • Loading branch information
3 people authored May 15, 2024
1 parent 05ab4ba commit 4c3effd
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 2 deletions.
2 changes: 2 additions & 0 deletions docs/versionhistory.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ APScheduler, see the :doc:`migration section <migration>`.
(PR by MohammadAmin Vahedinia)
- Fixed the shutdown procedure of the Redis event broker
- Fixed ``SQLAlchemyDataStore`` not respecting custom schema name when creating enums
- Fixed skipped intervals with overlapping schedules in ``AndTrigger``
(#911 <https://github.com/agronholm/apscheduler/issues/911>_; PR by Bennett Meares)

**4.0.0a4**

Expand Down
1 change: 0 additions & 1 deletion src/apscheduler/triggers/combining.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ def next(self) -> datetime | None:

# If all the fire times were within the threshold, return the earliest one
if latest_fire_time - earliest_fire_time <= self.threshold:
self._next_fire_times = [t.next() for t in self.triggers]
return earliest_fire_time
else:
raise MaxIterationsReached
Expand Down
101 changes: 100 additions & 1 deletion tests/triggers/test_combining.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from __future__ import annotations

from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone

import pytest

from apscheduler import MaxIterationsReached
from apscheduler.triggers.calendarinterval import CalendarIntervalTrigger
from apscheduler.triggers.combining import AndTrigger, OrTrigger
from apscheduler.triggers.cron import CronTrigger
from apscheduler.triggers.date import DateTrigger
from apscheduler.triggers.interval import IntervalTrigger

Expand Down Expand Up @@ -62,6 +64,103 @@ def test_repr(self, timezone, serializer):
"threshold=1.0, max_iterations=10000)"
)

@pytest.mark.parametrize(
"left_trigger,right_trigger,expected_datetimes",
[
(
IntervalTrigger(
hours=6, start_time=datetime(2024, 5, 1, tzinfo=timezone.utc)
),
IntervalTrigger(
hours=12, start_time=datetime(2024, 5, 1, tzinfo=timezone.utc)
),
[
datetime(2024, 5, 1, 0, tzinfo=timezone.utc),
datetime(2024, 5, 1, 12, tzinfo=timezone.utc),
datetime(2024, 5, 2, 0, tzinfo=timezone.utc),
],
),
(
IntervalTrigger(
days=1, start_time=datetime(2024, 5, 1, tzinfo=timezone.utc)
),
IntervalTrigger(
weeks=1, start_time=datetime(2024, 5, 1, tzinfo=timezone.utc)
),
[
datetime(2024, 5, 1, tzinfo=timezone.utc),
datetime(2024, 5, 8, tzinfo=timezone.utc),
datetime(2024, 5, 15, tzinfo=timezone.utc),
],
),
(
CronTrigger(
day_of_week="mon-fri",
hour="*",
timezone=timezone.utc,
start_time=datetime(2024, 5, 3, tzinfo=timezone.utc),
),
IntervalTrigger(
hours=12, start_time=datetime(2024, 5, 3, tzinfo=timezone.utc)
),
[
datetime(2024, 5, 3, 0, tzinfo=timezone.utc),
datetime(2024, 5, 3, 12, tzinfo=timezone.utc),
datetime(2024, 5, 6, 0, tzinfo=timezone.utc),
],
),
(
CronTrigger(
day_of_week="mon-fri",
timezone=timezone.utc,
start_time=datetime(2024, 5, 13, tzinfo=timezone.utc),
),
IntervalTrigger(
days=4, start_time=datetime(2024, 5, 13, tzinfo=timezone.utc)
),
[
datetime(2024, 5, 13, tzinfo=timezone.utc),
datetime(2024, 5, 17, tzinfo=timezone.utc),
datetime(2024, 5, 21, tzinfo=timezone.utc),
datetime(2024, 5, 29, tzinfo=timezone.utc),
],
),
(
CalendarIntervalTrigger(
months=1,
timezone=timezone.utc,
start_date=datetime(2024, 1, 1, tzinfo=timezone.utc),
),
CronTrigger(
day_of_week="mon-fri",
timezone=timezone.utc,
start_time=datetime(2024, 1, 1, tzinfo=timezone.utc),
),
[
datetime(2024, 1, 1, tzinfo=timezone.utc),
datetime(2024, 2, 1, tzinfo=timezone.utc),
datetime(2024, 3, 1, tzinfo=timezone.utc),
datetime(2024, 4, 1, tzinfo=timezone.utc),
datetime(2024, 5, 1, tzinfo=timezone.utc),
datetime(2024, 7, 1, tzinfo=timezone.utc),
datetime(2024, 8, 1, tzinfo=timezone.utc),
datetime(2024, 10, 1, tzinfo=timezone.utc),
datetime(2024, 11, 1, tzinfo=timezone.utc),
],
),
],
)
def test_overlapping_triggers(
self, left_trigger, right_trigger, expected_datetimes
):
"""
Verify that the `AndTrigger` fires at the intersection of two triggers.
"""
and_trigger = AndTrigger([left_trigger, right_trigger])
for expected_datetime in expected_datetimes:
next_datetime = and_trigger.next()
assert next_datetime == expected_datetime


class TestOrTrigger:
def test_two_datetriggers(self, timezone, serializer):
Expand Down

0 comments on commit 4c3effd

Please sign in to comment.