From 349baa020292a5428213a701a3320c942cf0e9e4 Mon Sep 17 00:00:00 2001 From: James Carl Necio Date: Wed, 25 May 2022 16:02:00 +0800 Subject: [PATCH] Align Behavior Of `JobQueue.run_daily` With `cron` (#3046) --- telegram/ext/_jobqueue.py | 16 ++++++++++++++-- tests/test_jobqueue.py | 30 +++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/telegram/ext/_jobqueue.py b/telegram/ext/_jobqueue.py index bfba9ebb4..f34c79090 100644 --- a/telegram/ext/_jobqueue.py +++ b/telegram/ext/_jobqueue.py @@ -28,6 +28,7 @@ from apscheduler.job import Job as APSJob from apscheduler.schedulers.asyncio import AsyncIOScheduler from telegram._utils.types import JSONDict +from telegram._utils.warnings import warn from telegram.ext._extbot import ExtBot from telegram.ext._utils.types import JobCallback @@ -50,6 +51,7 @@ class JobQueue: """ __slots__ = ("_application", "scheduler", "_executor") + _CRON_MAPPING = ("sun", "mon", "tue", "wed", "thu", "fri", "sat") def __init__(self) -> None: self._application: "Optional[weakref.ReferenceType[Application]]" = None @@ -419,7 +421,11 @@ class JobQueue: (:obj:`datetime.time.tzinfo`) is :obj:`None`, the default timezone of the bot will be used. days (Tuple[:obj:`int`], optional): Defines on which days of the week the job should - run (where ``0-6`` correspond to monday - sunday). Defaults to ``EVERY_DAY`` + run (where ``0-6`` correspond to sunday - saturday). By default, the job will run + every day. + + .. versionchanged:: 20.0 + Changed day of the week mapping of 0-6 from monday-sunday to sunday-saturday. data (:obj:`object`, optional): Additional data needed for the callback function. Can be accessed through :attr:`Job.data` in the callback. Defaults to :obj:`None`. @@ -447,6 +453,12 @@ class JobQueue: queue. """ + # TODO: After v20.0, we should remove the this warning. + warn( + "Prior to v20.0 the `days` parameter was not aligned to that of cron's weekday scheme." + "We recommend double checking if the passed value is correct.", + stacklevel=2, + ) if not job_kwargs: job_kwargs = {} @@ -458,7 +470,7 @@ class JobQueue: name=name, args=(self.application,), trigger="cron", - day_of_week=",".join([str(d) for d in days]), + day_of_week=",".join([self._CRON_MAPPING[d] for d in days]), hour=time.hour, minute=time.minute, second=time.second, diff --git a/tests/test_jobqueue.py b/tests/test_jobqueue.py index feac95135..39256a0cd 100644 --- a/tests/test_jobqueue.py +++ b/tests/test_jobqueue.py @@ -303,7 +303,11 @@ class TestJobQueue: scheduled_time = job_queue.jobs()[0].next_t.timestamp() assert scheduled_time == pytest.approx(expected_time) - async def test_run_daily(self, job_queue): + async def test_run_daily(self, job_queue, recwarn): + expected_warning = ( + "Prior to v20.0 the `days` parameter was not aligned to that of cron's weekday scheme." + "We recommend double checking if the passed value is correct." + ) delta, now = 1, dtm.datetime.now(pytz.utc) time_of_day = (now + dtm.timedelta(seconds=delta)).time() expected_reschedule_time = (now + dtm.timedelta(seconds=delta, days=1)).timestamp() @@ -313,6 +317,30 @@ class TestJobQueue: assert self.result == 1 scheduled_time = job_queue.jobs()[0].next_t.timestamp() assert scheduled_time == pytest.approx(expected_reschedule_time) + assert len(recwarn) == 1 + assert str(recwarn[0].message) == expected_warning + assert recwarn[0].filename == __file__, "wrong stacklevel" + + @pytest.mark.parametrize("weekday", (0, 1, 2, 3, 4, 5, 6)) + async def test_run_daily_days_of_week(self, job_queue, recwarn, weekday): + expected_warning = ( + "Prior to v20.0 the `days` parameter was not aligned to that of cron's weekday scheme." + "We recommend double checking if the passed value is correct." + ) + delta, now = 1, dtm.datetime.now(pytz.utc) + time_of_day = (now + dtm.timedelta(seconds=delta)).time() + # offset in days until next weekday + offset = (weekday + 6 - now.weekday()) % 7 + offset = offset if offset > 0 else 7 + expected_reschedule_time = (now + dtm.timedelta(seconds=delta, days=offset)).timestamp() + + job_queue.run_daily(self.job_run_once, time_of_day, days=[weekday]) + await asyncio.sleep(delta + 0.1) + scheduled_time = job_queue.jobs()[0].next_t.timestamp() + assert scheduled_time == pytest.approx(expected_reschedule_time) + assert len(recwarn) == 1 + assert str(recwarn[0].message) == expected_warning + assert recwarn[0].filename == __file__, "wrong stacklevel" async def test_run_monthly(self, job_queue, timezone): delta, now = 1, dtm.datetime.now(timezone)