Job queue time units (#452)

* Adding timeunit and day support to the jobqueue

* Adding tests

* Changed the file permission back to 644.

* Changed AssertEqual argument order to (actual, expectd).

* Removed the TimeUnit enum and unit param, instead use datetime.time for interval.

* Removing the TimeUnits enum and unit param in favour of optionally using a datetime.time as the interval.

* Removing the TimeUnits enumeration, forgot the remove it in the last one.

* Removed some old docstrings refering to the TimeUnits enum.

* Removed the old TimeUnits import.

* Adding some error handling for the 'days' argument (only a 'tuple' with 'Days')

* Writing the error message directly in the exception.

* Moving a debug statement wrongfully saying a job would be running on days it wouldn't.

* Writing error messages directly in the exceptions instead of making an extra variable.

* Replacing datetime.time in favour of datetime.timedelta because of the get_seconds() method.

* Adding error handling for the method .

* Splitting the tests up in multiple ones, no float test because I haven't found a reliable way to test it.

* Excluding .exrc file.

* Removing \ at EOF of ValueError.

* Replacing Enums with plain new-style classes.

* Using numbers.number to check for ints/floats instead of seperate int/float checks.

* Fixing typo, number -> Number.

* Changed lower_case Days attributes to UPPER_CASE.

* Different formatting for Days class, removed the get_days function in favour of a tuple.

* Removed redundant function get_days.

* Edited the docstring for next_t to also take datetime.timedelta.

* Removed for-loop in favour of any().

* Changed docstring for interval.

* Removed debug print.

* Changing some docstrings.

* Changing some docstrings (again).
This commit is contained in:
Wesley Gahr 2016-11-08 23:39:25 +01:00 committed by Jannes Höke
parent a7bfb0c3a1
commit 68e87db909
4 changed files with 74 additions and 10 deletions

3
.gitignore vendored
View file

@ -71,3 +71,6 @@ telegram.webp
# original files from merges
*.orig
# Exclude .exrc file for Vim
.exrc

View file

@ -32,6 +32,7 @@ The following wonderful people contributed directly or indirectly to this projec
- `Shelomentsev D <https://github.com/shelomentsevd>`_
- `sooyhwang <https://github.com/sooyhwang>`_
- `Valentijn <https://github.com/Faalentijn>`_
- `voider1 <https://github.com/voider1>`_
- `wjt <https://github.com/wjt>`_
Please add yourself here alphabetically when you submit your first pull request.

View file

@ -21,10 +21,17 @@
import logging
import time
import warnings
import datetime
from numbers import Number
from threading import Thread, Lock, Event
from queue import PriorityQueue, Empty
class Days(object):
MON, TUE, WED, THU, FRI, SAT, SUN = range(7)
EVERY_DAY = tuple(range(7))
class JobQueue(object):
"""This class allows you to periodically perform tasks with the bot.
@ -61,14 +68,25 @@ class JobQueue(object):
Args:
job (telegram.ext.Job): The ``Job`` instance representing the new job
next_t (Optional[float]): Time in seconds in which the job should be executed first.
Defaults to ``job.interval``
next_t (Optional[int, float, datetime.timedelta]): Time in which the job
should be executed first. Defaults to ``job.interval``. ``int`` and ``float``
will be interpreted as seconds.
"""
job.job_queue = self
if next_t is None:
next_t = job.interval
interval = job.interval
if isinstance(interval, Number):
next_t = interval
elif isinstance(interval, datetime.timedelta):
next_t = interval.total_seconds()
else:
raise ValueError("The interval argument should be of type datetime.timedelta,"
" int or float")
elif isinstance(next_t, datetime.timedelta):
next_t = next_t.total_second()
now = time.time()
next_t += now
@ -123,11 +141,11 @@ class JobQueue(object):
continue
if job.enabled:
self.logger.debug('Running job %s', job.name)
try:
current_week_day = datetime.datetime.now().weekday()
if any(day == current_week_day for day in job.days):
self.logger.debug('Running job %s', job.name)
job.run(self.bot)
except:
self.logger.exception('An uncaught error was raised while executing job %s',
job.name)
@ -200,6 +218,7 @@ class Job(object):
Attributes:
callback (function):
interval (float):
days: (tuple)
repeat (bool):
name (str):
enabled (bool): Boolean property that decides if this job is currently active
@ -208,22 +227,34 @@ class Job(object):
callback (function): The callback function that should be executed by the Job. It should
take two parameters ``bot`` and ``job``, where ``job`` is the ``Job`` instance. It
can be used to terminate the job or modify its interval.
interval (float): The interval in which this job should execute its callback function in
seconds.
interval ([int, float, datetime.timedelta]): The interval in which the job will execute its
callback function. ``int`` and ``float`` will be interpreted as seconds.
repeat (Optional[bool]): If this job should be periodically execute its callback function
(``True``) or only once (``False``). Defaults to ``True``
context (Optional[object]): Additional data needed for the callback function. Can be
accessed through ``job.context`` in the callback. Defaults to ``None``
days (Tuple): Defines on which days the job should be ran.
"""
job_queue = None
def __init__(self, callback, interval, repeat=True, context=None):
def __init__(self, callback, interval, repeat=True, context=None, days=Days.EVERY_DAY):
self.callback = callback
self.interval = interval
self.repeat = repeat
self.context = context
if not isinstance(days, tuple):
raise ValueError("The 'days argument should be of type 'tuple'")
if not all(isinstance(day, int) for day in days):
raise ValueError("The elements of the 'days' argument should be of type 'int'")
if not all(day >= 0 and day <= 6 for day in days):
raise ValueError("The elements of the 'days' argument should be from 0 up to and "
"including 6")
self.days = days
self.name = callback.__name__
self._remove = Event()
self._enabled = Event()

View file

@ -23,6 +23,9 @@ This module contains an object that represents Tests for JobQueue
import logging
import sys
import unittest
import datetime
import time
from math import ceil
from time import sleep
from tests.test_updater import MockBot
@ -53,11 +56,15 @@ class JobQueueTest(BaseTest, unittest.TestCase):
self.jq = JobQueue(MockBot('jobqueue_test'))
self.jq.start()
self.result = 0
self.job_time = 0
def tearDown(self):
if self.jq is not None:
self.jq.stop()
def getSeconds(self):
return int(ceil(time.time()))
def job1(self, bot, job):
self.result += 1
@ -71,6 +78,9 @@ class JobQueueTest(BaseTest, unittest.TestCase):
def job4(self, bot, job):
self.result += job.context
def job5(self, bot, job):
self.job_time = self.getSeconds()
def test_basic(self):
self.jq.put(Job(self.job1, 0.1))
sleep(1.5)
@ -169,6 +179,25 @@ class JobQueueTest(BaseTest, unittest.TestCase):
finally:
u.stop()
def test_time_unit_int(self):
# Testing seconds in int
seconds_interval = 5
expected_time = self.getSeconds() + seconds_interval
self.jq.put(Job(self.job5, seconds_interval, repeat=False))
sleep(6)
self.assertEqual(self.job_time, expected_time)
def test_time_unit_dt_time(self):
# Testing seconds, minutes and hours as datetime.timedelta object
# This is sufficient to test that it actually works.
interval = datetime.timedelta(seconds=5)
expected_time = self.getSeconds() + interval.total_seconds()
self.jq.put(Job(self.job5, interval, repeat=False))
sleep(6)
self.assertEqual(self.job_time, expected_time)
if __name__ == '__main__':
unittest.main()