X Tutup
Skip to content

Commit 8ead72e

Browse files
committed
jobqueue: add support for specifying next_t in datetime.datetime or datetime.time
1 parent a37add3 commit 8ead72e

File tree

2 files changed

+73
-22
lines changed

2 files changed

+73
-22
lines changed

telegram/ext/jobqueue.py

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,12 @@ def put(self, job, next_t=None):
6868
6969
Args:
7070
job (telegram.ext.Job): The ``Job`` instance representing the new job
71-
next_t (Optional[int, float, datetime.timedelta]): Time in which the job
72-
should be executed first. Defaults to ``job.interval``. ``int`` and ``float``
73-
will be interpreted as seconds.
71+
next_t (Optional[int, float, datetime.timedelta, datetime.datetime, datetime.time]):
72+
Time in or at which the job should be executed first. Defaults to ``job.interval``.
73+
If it is an ``int`` or a ``float``, it will be interpreted as seconds. If it is
74+
a ``datetime.datetime``, it will be executed at the specified date and time. If it
75+
is a ``datetime.time``, it will execute at the specified time today or, if the time
76+
has already passed, tomorrow.
7477
7578
"""
7679
job.job_queue = self
@@ -85,8 +88,20 @@ def put(self, job, next_t=None):
8588
else:
8689
raise ValueError("The interval argument should be of type datetime.timedelta,"
8790
" int or float")
88-
elif isinstance(next_t, datetime.timedelta):
89-
next_t = next_t.total_second()
91+
92+
elif isinstance(next_t, datetime.datetime):
93+
next_t = next_t - datetime.datetime.now()
94+
95+
elif isinstance(next_t, datetime.time):
96+
next_datetime = datetime.datetime.combine(datetime.date.today(), next_t)
97+
98+
if datetime.datetime.now().time() > next_t:
99+
next_datetime += datetime.timedelta(days=1)
100+
101+
next_t = next_datetime - datetime.datetime.now()
102+
103+
if isinstance(next_t, datetime.timedelta):
104+
next_t = next_t.total_seconds()
90105

91106
now = time.time()
92107
next_t += now
@@ -227,8 +242,10 @@ class Job(object):
227242
callback (function): The callback function that should be executed by the Job. It should
228243
take two parameters ``bot`` and ``job``, where ``job`` is the ``Job`` instance. It
229244
can be used to terminate the job or modify its interval.
230-
interval ([int, float, datetime.timedelta]): The interval in which the job will execute its
231-
callback function. ``int`` and ``float`` will be interpreted as seconds.
245+
interval (Optional[int, float, datetime.timedelta]): The interval in which the job will
246+
execute its callback function. ``int`` and ``float`` will be interpreted as seconds.
247+
If you don't set this value, you must set ``repeat=False`` and specify ``next_t`` when
248+
you put the job into the job queue.
232249
repeat (Optional[bool]): If this job should be periodically execute its callback function
233250
(``True``) or only once (``False``). Defaults to ``True``
234251
context (Optional[object]): Additional data needed for the callback function. Can be
@@ -238,7 +255,7 @@ class Job(object):
238255
"""
239256
job_queue = None
240257

241-
def __init__(self, callback, interval, repeat=True, context=None, days=Days.EVERY_DAY):
258+
def __init__(self, callback, interval=None, repeat=True, context=None, days=Days.EVERY_DAY):
242259
self.callback = callback
243260
self.interval = interval
244261
self.repeat = repeat
@@ -250,10 +267,13 @@ def __init__(self, callback, interval, repeat=True, context=None, days=Days.EVER
250267
if not all(isinstance(day, int) for day in days):
251268
raise ValueError("The elements of the 'days' argument should be of type 'int'")
252269

253-
if not all(day >= 0 and day <= 6 for day in days):
270+
if not all(0 <= day <= 6 for day in days):
254271
raise ValueError("The elements of the 'days' argument should be from 0 up to and "
255272
"including 6")
256273

274+
if interval is None and repeat:
275+
raise ValueError("You must either set an interval or set 'repeat' to 'False'")
276+
257277
self.days = days
258278
self.name = callback.__name__
259279
self._remove = Event()

tests/test_jobqueue.py

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,6 @@ def tearDown(self):
6262
if self.jq is not None:
6363
self.jq.stop()
6464

65-
def getSeconds(self):
66-
return int(ceil(time.time()))
67-
6865
def job1(self, bot, job):
6966
self.result += 1
7067

@@ -79,7 +76,7 @@ def job4(self, bot, job):
7976
self.result += job.context
8077

8178
def job5(self, bot, job):
82-
self.job_time = self.getSeconds()
79+
self.job_time = time.time()
8380

8481
def test_basic(self):
8582
self.jq.put(Job(self.job1, 0.1))
@@ -181,22 +178,56 @@ def test_inUpdater(self):
181178

182179
def test_time_unit_int(self):
183180
# Testing seconds in int
184-
seconds_interval = 5
185-
expected_time = self.getSeconds() + seconds_interval
181+
seconds_interval = 2
182+
expected_time = time.time() + seconds_interval
186183

187184
self.jq.put(Job(self.job5, seconds_interval, repeat=False))
188-
sleep(6)
189-
self.assertEqual(self.job_time, expected_time)
185+
sleep(2.5)
186+
self.assertAlmostEqual(self.job_time, expected_time, delta=0.1)
190187

191-
def test_time_unit_dt_time(self):
188+
def test_time_unit_dt_timedelta(self):
192189
# Testing seconds, minutes and hours as datetime.timedelta object
193190
# This is sufficient to test that it actually works.
194-
interval = datetime.timedelta(seconds=5)
195-
expected_time = self.getSeconds() + interval.total_seconds()
191+
interval = datetime.timedelta(seconds=2)
192+
expected_time = time.time() + interval.total_seconds()
196193

197194
self.jq.put(Job(self.job5, interval, repeat=False))
198-
sleep(6)
199-
self.assertEqual(self.job_time, expected_time)
195+
sleep(2.5)
196+
self.assertAlmostEqual(self.job_time, expected_time, delta=0.1)
197+
198+
def test_time_unit_dt_datetime(self):
199+
# Testing running at a specific datetime
200+
delta = datetime.timedelta(seconds=2)
201+
next_t = datetime.datetime.now() + delta
202+
expected_time = time.time() + delta.total_seconds()
203+
204+
self.jq.put(Job(self.job5, repeat=False), next_t=next_t)
205+
sleep(2.5)
206+
self.assertAlmostEqual(self.job_time, expected_time, delta=0.1)
207+
208+
def test_time_unit_dt_time_today(self):
209+
# Testing running at a specific time today
210+
delta = 2
211+
current_time = datetime.datetime.now().time()
212+
next_t = datetime.time(current_time.hour, current_time.minute, current_time.second + delta,
213+
current_time.microsecond)
214+
expected_time = time.time() + delta
215+
216+
self.jq.put(Job(self.job5, repeat=False), next_t=next_t)
217+
sleep(2.5)
218+
self.assertAlmostEqual(self.job_time, expected_time, delta=0.1)
219+
220+
def test_time_unit_dt_time_tomorrow(self):
221+
# Testing running at a specific time that has passed today. Since we can't wait a day, we
222+
# test if the jobs next_t has been calculated correctly
223+
delta = -2
224+
current_time = datetime.datetime.now().time()
225+
next_t = datetime.time(current_time.hour, current_time.minute, current_time.second + delta,
226+
current_time.microsecond)
227+
expected_time = time.time() + delta + 60 * 60 * 24
228+
229+
self.jq.put(Job(self.job5, repeat=False), next_t=next_t)
230+
self.assertAlmostEqual(self.jq.queue.get(False)[0], expected_time, delta=0.1)
200231

201232

202233
if __name__ == '__main__':

0 commit comments

Comments
 (0)
X Tutup