From dea08122d30840ee5fd879b6fd078107ae891191 Mon Sep 17 00:00:00 2001 From: thodnev Date: Thu, 2 Mar 2017 06:19:24 +0200 Subject: [PATCH 1/5] Introduce MessageQueue --- AUTHORS.rst | 1 + docs/source/telegram.ext.messagequeue.rst | 7 + docs/source/telegram.ext.rst | 1 + telegram/ext/messagequeue.py | 309 ++++++++++++++++++++++ tests/test_messagequeue.py | 91 +++++++ 5 files changed, 409 insertions(+) create mode 100644 docs/source/telegram.ext.messagequeue.rst create mode 100644 telegram/ext/messagequeue.py create mode 100644 tests/test_messagequeue.py diff --git a/AUTHORS.rst b/AUTHORS.rst index 970ea5fcecd..f273884120c 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -41,6 +41,7 @@ The following wonderful people contributed directly or indirectly to this projec - `Joscha Götzer `_ - `Shelomentsev D `_ - `sooyhwang `_ +- `thodnev `_ - `Valentijn `_ - `voider1 `_ - `wjt `_ diff --git a/docs/source/telegram.ext.messagequeue.rst b/docs/source/telegram.ext.messagequeue.rst new file mode 100644 index 00000000000..80cce040c5c --- /dev/null +++ b/docs/source/telegram.ext.messagequeue.rst @@ -0,0 +1,7 @@ +telegram.ext.messagequeue module +================================ + +.. automodule:: telegram.ext.messagequeue + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/telegram.ext.rst b/docs/source/telegram.ext.rst index 4da91810cdc..9af7aa4c44e 100644 --- a/docs/source/telegram.ext.rst +++ b/docs/source/telegram.ext.rst @@ -16,6 +16,7 @@ Submodules telegram.ext.commandhandler telegram.ext.inlinequeryhandler telegram.ext.messagehandler + telegram.ext.messagequeue telegram.ext.filters telegram.ext.regexhandler telegram.ext.stringcommandhandler diff --git a/telegram/ext/messagequeue.py b/telegram/ext/messagequeue.py new file mode 100644 index 00000000000..b71dfbec80c --- /dev/null +++ b/telegram/ext/messagequeue.py @@ -0,0 +1,309 @@ +'''A throughput-limiting message dispatcher for Telegram bots''' +from telegram.utils import promise + +import functools +import sys +import time +import threading +if sys.version_info.major > 2: + import queue as q +else: + import Queue as q + +# We need to count < 1s intervals, so the most accurate timer is needed +# Starting from Python 3.3 we have time.perf_counter which is the clock +# with the highest resolution available to the system, so let's use it there. +# In Python 2.7, there's no perf_counter yet, so fallback on what we have: +# on Windows, the best available is time.clock while time.time is on +# another platforms (M. Lutz, "Learning Python," 4ed, p.630-634) +if sys.version_info.major == 3 and sys.version_info.minor >= 3: + curtime = time.perf_counter +else: + curtime = time.clock if sys.platform[:3] == 'win' else time.time + + +class DelayQueueError(RuntimeError): + '''Indicates dispatching errors''' + pass + + +class DelayQueue(threading.Thread): + '''Dispatches callbacks from queue with specified throughput limits. + Creates a separate thread to dispatch callbacks with delays. + + Args: + queue (:obj:`queue.Queue`, optional): used to pass callbacks to + thread. + Creates `queue.Queue` implicitly if not provided. + burst_limit (:obj:`int`, optional): numer of maximum callbacks to + dispatch per time-window defined by `time_limit_ms`. + Defaults to 30. + time_limit_ms (:obj:`int`, optional): defines width of time-window + used when each dispatching limit is calculated. + Defaults to 1000. + exc_route (:obj:`callable`, optional): a callable, accepting 1 + positional argument; used to route exceptions from dispatcher + thread to main thread; is called on `Exception` subclass + exceptions. + If not provided, exceptions are routed through dummy handler, + which re-raises them. + autostart (:obj:`bool`, optional): if True, dispatcher is started + immediately after object's creation; if False, should be + started manually by `start` method. + Defaults to True. + name (:obj:`str`, optional): thread's name. + Defaults to ``'DelayQueue-N'``, where N is sequential + number of object created. + ''' + _instcnt = 0 # instance counter + + def __init__(self, + queue=None, + burst_limit=30, + time_limit_ms=1000, + exc_route=None, + autostart=True, + name=None): + self._queue = queue if queue is not None else q.Queue() + self.burst_limit = burst_limit + self.time_limit = time_limit_ms / 1000 + self.exc_route = (exc_route if exc_route is not None else self._default_exception_handler) + self.__exit_req = False # flag to gently exit thread + self.__class__._instcnt += 1 + if name is None: + name = '%s-%s' % (self.__class__.__name__, self.__class__._instcnt) + super(DelayQueue, self).__init__(name=name) + self.daemon = False + if autostart: # immediately start dispatching + super(DelayQueue, self).start() + + def run(self): + '''Do not use the method except for unthreaded testing purposes, + the method normally is automatically called by `start` method. + ''' + times = [] # used to store each callable dispatch time + while True: + item = self._queue.get() + if self.__exit_req: + return # shutdown thread + # delay routine + now = curtime() + t_delta = now - self.time_limit # calculate early to improve perf. + if times and t_delta > times[-1]: + # if last call was before the limit time-window + # used to impr. perf. in long-interval calls case + times = [now] + else: + # collect last in current limit time-window + times = [t for t in times if t >= t_delta] + times.append(now) + if len(times) >= self.burst_limit: # if throughput limit was hit + time.sleep(times[1] - t_delta) + # finally dispatch one + try: + func, args, kwargs = item + func(*args, **kwargs) + except Exception as exc: # re-route any exceptions + self.exc_route(exc) # to prevent thread exit + + def stop(self, timeout=None): + '''Used to gently stop dispatching process and shutdown its thread. + + Args: + timeout (:obj:`float`): indicates maximum time to wait for + dispatcher to stop and its thread to exit. + If timeout exceeds and dispatcher has not stopped, method + silently returns. `is_alive` could be used afterwards + to check the actual status. If `timeout` set to None, blocks + until dispatcher is shut down. + Defaults to None. + Returns: + None + ''' + self.__exit_req = True # gently request + self._queue.put(None) # put something to unfreeze if frozen + super(DelayQueue, self).join(timeout=timeout) + + @staticmethod + def _default_exception_handler(exc): + '''Dummy exception handler which re-raises exception in thread. + Could be possibly overwritten by subclasses. + ''' + raise exc + + def __call__(self, func, *args, **kwargs): + '''Used to dispatch callbacks in throughput-limiting thread + through queue. + Args: + func (:obj:`callable`): the actual function (or any callable) that + is dispatched through queue. + *args: variable-length `func` arguments. + **kwargs: arbitrary keyword-arguments to `func`. + Returns: + None + ''' + if not self.is_alive() or self.__exit_req: + raise DelayQueueError('Could not dispatch into stopped thread') + self._queue.put((func, args, kwargs)) + + +# The most straightforward way to implement this is to use 2 sequenital delay +# queues, like on classic delay chain schematics in electronics. +# So, message path is: +# msg --> group delay if group msg, else no delay --> normal msg delay --> out +# This way OS threading scheduler cares of timings accuracy. +# (see time.time, time.clock, time.perf_counter, time.sleep @ docs.python.org) +class MessageQueue(object): + '''Implements callback dispatching with proper delays to avoid hitting + Telegram's message limits. + Contains two `DelayQueue`s, for group and for all messages, interconnected + in delay chain. Callables are dispatched through *group* `DelayQueue`, then + through *all* `DelayQueue` for group-type messages. For non-group messages, + only the *all* `DelayQueue` is used. + + Args: + all_burst_limit (:obj:`int`, optional): numer of maximum *all-type* + callbacks to dispatch per time-window defined by + `all_time_limit_ms`. + Defaults to 30. + all_time_limit_ms (:obj:`int`, optional): defines width of *all-type* + time-window used when each dispatching limit is calculated. + Defaults to 1000 ms. + group_burst_limit (:obj:`int`, optional): numer of maximum *group-type* + callbacks to dispatch per time-window defined by + `group_time_limit_ms`. + Defaults to 20. + group_time_limit_ms (:obj:`int`, optional): defines width of + *group-type* time-window used when each dispatching limit is + calculated. + Defaults to 60000 ms. + exc_route (:obj:`callable`, optional): a callable, accepting one + positional argument; used to route exceptions from dispatcher + threads to main thread; is called on `Exception` subclass + exceptions. + If not provided, exceptions are routed through dummy handler, + which re-raises them. + autostart (:obj:`bool`, optional): if True, dispatchers are started + immediately after object's creation; if False, should be + started manually by `start` method. + Defaults to True. + + Attributes: + _all_delayq (:obj:`telegram.ext.messagequeue.DelayQueue`): actual + `DelayQueue` used for *all-type* callback dispatching + _group_delayq (:obj:`telegram.ext.messagequeue.DelayQueue`): actual + `DelayQueue` used for *group-type* callback dispatching + ''' + + def __init__(self, + all_burst_limit=30, + all_time_limit_ms=1000, + group_burst_limit=20, + group_time_limit_ms=60000, + exc_route=None, + autostart=True): + # create accoring delay queues, use composition + self._all_delayq = DelayQueue( + burst_limit=all_burst_limit, + time_limit_ms=all_time_limit_ms, + exc_route=exc_route, + autostart=autostart) + self._group_delayq = DelayQueue( + burst_limit=group_burst_limit, + time_limit_ms=group_time_limit_ms, + exc_route=exc_route, + autostart=autostart) + + def start(self): + '''Method is used to manually start the `MessageQueue` dispatching + + Returns: + None + ''' + self._all_delayq.start() + self._group_delayq.start() + + def stop(self, timeout=None): + self._group_delayq.stop(timeout=timeout) + self._all_delayq.stop(timeout=timeout) + + stop.__doc__ = DelayQueue.stop.__doc__ or '' # reuse docsting if any + + def __call__(self, promise, is_group_msg=False): + '''Dispatches callables through troughput-limiting queues to avoid + hitting limits (specified with \*_burst_limit and *\_time_limit_ms). + Args: + promise (:obj:`callable`): mainly the + :obj:`telegram.utils.promise.Promise` (see Notes for other + callables), that is dispatched through delay queues + is_group_msg (:obj:`bool`, optional): defines whether `promise` + would be dispatched through *group*+*all* `DelayQueue`s + (if set to ``True``), or only through *all* `DelayQueue` + (if set to ``False``), resulting in needed delays to avoid + hitting specified limits. + Defaults to ``True``. + + Notes: + Method is designed to accept :obj:`telegram.utils.promise.Promise` + as `promise` argument, but other callables could be used too. + For example, lambdas or simple functions could be used to wrap + original func to be called with needed args. + In that case, be sure that either wrapper func does not raise + outside exceptions or the proper `exc_route` handler is provided. + + Returns: + Either :obj:`telegram.utils.promise.Promise` in case call is + queued, or original method's return value if it's not. + ''' + if not is_group_msg: # ignore middle group delay + self._all_delayq(promise) + else: # use middle group delay + self._group_delayq(self._all_delayq, promise) + return promise + + +def queuedmessage(method): + '''A decorator to be used with `telegram.bot.Bot` send* methods. + + Note: + As it probably wouldn't be a good idea to make this decorator a + property, it had been coded as decorator function, so it implies that + **first positional argument to wrapped MUST be self**\. + + The next object attributes are used by decorator: + + Attributes: + self._is_queued_out (:obj:`bool`): Value to provide class-defaults + to `queued` kwarg if not provided during wrapped method call. + self._msg_queue (:obj:`telegram.ext.messagequeue.MessageQueue`): + The actual `MessageQueue` used to delay outbond messages according + to specified time-limits. + + Wrapped method starts accepting the next kwargs: + + Args: + queued (:obj:`bool`, optional): if set to ``True``, the `MessageQueue` + is used to dispatch output messages. + Defaults to `self._is_queued_out`. + isgroup (:obj:`bool`, optional): if set to ``True``, the message is + meant to be group-type (as there's no obvious way to determine its + type in other way at the moment). Group-type messages could have + additional dispatch delay according to limits set in + `self._out_queue`. + Defaults to ``False``. + + Returns: + Either :obj:`telegram.utils.promise.Promise` in case call is queued, + or original method's return value if it's not. + ''' + + @functools.wraps(method) + def wrapped(self, *args, **kwargs): + queued = kwargs.pop('queued', self._is_queued_out) + isgroup = kwargs.pop('isgroup', False) + if queued: + prom = promise.Promise(method, args, kwargs) + return self._msg_queue(prom, isgroup) + return method(self, *args, **kwargs) + + return wrapped diff --git a/tests/test_messagequeue.py b/tests/test_messagequeue.py new file mode 100644 index 00000000000..3aa33c75a84 --- /dev/null +++ b/tests/test_messagequeue.py @@ -0,0 +1,91 @@ +'''This module contains telegram.ext.messagequeue test logic''' +from __future__ import print_function, division + +import sys +import os +import time +import unittest + +sys.path.insert(0, os.path.dirname(__file__) + os.sep + '..') +from tests.base import BaseTest + +from telegram.ext import messagequeue as mq + + +class DelayQueueTest(BaseTest, unittest.TestCase): + + def __init__(self, *args, **kwargs): + self._N = kwargs.pop('N', 128) + self._msglimit = kwargs.pop('burst_limit', 30) + self._timelimit = kwargs.pop('time_limit_ms', 1000) + self._margin = kwargs.pop('margin_ms', 0) + isprint = kwargs.pop('isprint', False) + + def noprint(*args, **kwargs): + pass + + self._print = print if isprint else noprint + super(DelayQueueTest, self).__init__(*args, **kwargs) + + def setUp(self): + print = self._print + print('Self-test with N = {} msgs, burst_limit = {} msgs, ' + 'time_limit = {:.2f} ms, margin = {:.2f} ms' + ''.format(self._N, self._msglimit, self._timelimit, self._margin)) + self.testtimes = [] + + def testcall(): + self.testtimes.append(mq.curtime()) + + self.testcall = testcall + + def test_delayqueue_limits(self): + '''Test that DelayQueue dispatched calls don't hit time-window limit''' + print = self._print + dsp = mq.DelayQueue( + burst_limit=self._msglimit, time_limit_ms=self._timelimit, autostart=True) + print('Started dispatcher {}\nStatus: {}' + ''.format(dsp, ['inactive', 'active'][dsp.is_alive()])) + self.assertTrue(dsp.is_alive()) + + print('Dispatching {} calls @ {}'.format(self._N, time.asctime())) + + for i in range(self._N): + dsp(self.testcall) + + print('Queue filled, waiting 4 dispatch finish @ ' + str(time.asctime())) + + starttime = mq.curtime() + app_endtime = ( + (self._N * self._msglimit / + (1000 * self._timelimit)) + starttime + 20) # wait up to 20 sec more than needed + while not dsp._queue.empty() and mq.curtime() < app_endtime: + time.sleep(1) + self.assertTrue(dsp._queue.empty()) # check loop exit condition + + dsp.stop() + print('Dispatcher ' + ['stopped', '!NOT STOPPED!'][dsp.is_alive()] + ' @ ' + str( + time.asctime())) + self.assertFalse(dsp.is_alive()) + + self.assertTrue(self.testtimes or self._N == 0) + print('Calculating call time windows') + passes, fails = [], [] + delta = (self._timelimit - self._margin) / 1000 + it = enumerate(range(self._msglimit + 1, len(self.testtimes))) + for start, stop in it: + part = self.testtimes[start:stop] + if (part[-1] - part[0]) >= delta: + passes.append(part) + else: + fails.append(part) + + print('Tested: {}, Passed: {}, Failed: {}' + ''.format(len(passes + fails), len(passes), len(fails))) + if fails: + print('(!) Got mismatches: ' + ';\n'.join(map(str, fails))) + self.assertFalse(fails) + + +if __name__ == '__main__': + unittest.main() From dfb22dce651e7cb384ea9fc3fe6d10b129ebcc09 Mon Sep 17 00:00:00 2001 From: thodnev Date: Sun, 12 Mar 2017 03:39:42 +0200 Subject: [PATCH 2/5] minor documentation and terminology fixes according to the review --- telegram/ext/messagequeue.py | 93 ++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 36 deletions(-) diff --git a/telegram/ext/messagequeue.py b/telegram/ext/messagequeue.py index b71dfbec80c..a7fc0a01141 100644 --- a/telegram/ext/messagequeue.py +++ b/telegram/ext/messagequeue.py @@ -1,4 +1,25 @@ -'''A throughput-limiting message dispatcher for Telegram bots''' +#!/usr/bin/env python +# +# Module author: +# Tymofii A. Khodniev (thodnev) +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2017 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/] +'''A throughput-limiting message processor for Telegram bots''' from telegram.utils import promise import functools @@ -23,31 +44,31 @@ class DelayQueueError(RuntimeError): - '''Indicates dispatching errors''' + '''Indicates processing errors''' pass class DelayQueue(threading.Thread): - '''Dispatches callbacks from queue with specified throughput limits. - Creates a separate thread to dispatch callbacks with delays. + '''Processes callbacks from queue with specified throughput limits. + Creates a separate thread to process callbacks with delays. Args: queue (:obj:`queue.Queue`, optional): used to pass callbacks to thread. Creates `queue.Queue` implicitly if not provided. burst_limit (:obj:`int`, optional): numer of maximum callbacks to - dispatch per time-window defined by `time_limit_ms`. + process per time-window defined by `time_limit_ms`. Defaults to 30. time_limit_ms (:obj:`int`, optional): defines width of time-window - used when each dispatching limit is calculated. + used when each processing limit is calculated. Defaults to 1000. exc_route (:obj:`callable`, optional): a callable, accepting 1 - positional argument; used to route exceptions from dispatcher + positional argument; used to route exceptions from processor thread to main thread; is called on `Exception` subclass exceptions. If not provided, exceptions are routed through dummy handler, which re-raises them. - autostart (:obj:`bool`, optional): if True, dispatcher is started + autostart (:obj:`bool`, optional): if True, processor is started immediately after object's creation; if False, should be started manually by `start` method. Defaults to True. @@ -74,14 +95,14 @@ def __init__(self, name = '%s-%s' % (self.__class__.__name__, self.__class__._instcnt) super(DelayQueue, self).__init__(name=name) self.daemon = False - if autostart: # immediately start dispatching + if autostart: # immediately start processing super(DelayQueue, self).start() def run(self): '''Do not use the method except for unthreaded testing purposes, the method normally is automatically called by `start` method. ''' - times = [] # used to store each callable dispatch time + times = [] # used to store each callable processing time while True: item = self._queue.get() if self.__exit_req: @@ -99,7 +120,7 @@ def run(self): times.append(now) if len(times) >= self.burst_limit: # if throughput limit was hit time.sleep(times[1] - t_delta) - # finally dispatch one + # finally process one try: func, args, kwargs = item func(*args, **kwargs) @@ -107,15 +128,15 @@ def run(self): self.exc_route(exc) # to prevent thread exit def stop(self, timeout=None): - '''Used to gently stop dispatching process and shutdown its thread. + '''Used to gently stop processor and shutdown its thread. Args: timeout (:obj:`float`): indicates maximum time to wait for - dispatcher to stop and its thread to exit. - If timeout exceeds and dispatcher has not stopped, method + processor to stop and its thread to exit. + If timeout exceeds and processor has not stopped, method silently returns. `is_alive` could be used afterwards to check the actual status. If `timeout` set to None, blocks - until dispatcher is shut down. + until processor is shut down. Defaults to None. Returns: None @@ -132,18 +153,19 @@ def _default_exception_handler(exc): raise exc def __call__(self, func, *args, **kwargs): - '''Used to dispatch callbacks in throughput-limiting thread + '''Used to process callbacks in throughput-limiting thread through queue. Args: func (:obj:`callable`): the actual function (or any callable) that - is dispatched through queue. + is processed through queue. *args: variable-length `func` arguments. **kwargs: arbitrary keyword-arguments to `func`. Returns: None ''' if not self.is_alive() or self.__exit_req: - raise DelayQueueError('Could not dispatch into stopped thread') + raise DelayQueueError('Could not process callback ' + 'in stopped thread') self._queue.put((func, args, kwargs)) @@ -154,45 +176,45 @@ def __call__(self, func, *args, **kwargs): # This way OS threading scheduler cares of timings accuracy. # (see time.time, time.clock, time.perf_counter, time.sleep @ docs.python.org) class MessageQueue(object): - '''Implements callback dispatching with proper delays to avoid hitting + '''Implements callback processing with proper delays to avoid hitting Telegram's message limits. Contains two `DelayQueue`s, for group and for all messages, interconnected - in delay chain. Callables are dispatched through *group* `DelayQueue`, then + in delay chain. Callables are processed through *group* `DelayQueue`, then through *all* `DelayQueue` for group-type messages. For non-group messages, only the *all* `DelayQueue` is used. Args: all_burst_limit (:obj:`int`, optional): numer of maximum *all-type* - callbacks to dispatch per time-window defined by + callbacks to process per time-window defined by `all_time_limit_ms`. Defaults to 30. all_time_limit_ms (:obj:`int`, optional): defines width of *all-type* - time-window used when each dispatching limit is calculated. + time-window used when each processing limit is calculated. Defaults to 1000 ms. group_burst_limit (:obj:`int`, optional): numer of maximum *group-type* - callbacks to dispatch per time-window defined by + callbacks to process per time-window defined by `group_time_limit_ms`. Defaults to 20. group_time_limit_ms (:obj:`int`, optional): defines width of - *group-type* time-window used when each dispatching limit is + *group-type* time-window used when each processing limit is calculated. Defaults to 60000 ms. exc_route (:obj:`callable`, optional): a callable, accepting one - positional argument; used to route exceptions from dispatcher + positional argument; used to route exceptions from processor threads to main thread; is called on `Exception` subclass exceptions. If not provided, exceptions are routed through dummy handler, which re-raises them. - autostart (:obj:`bool`, optional): if True, dispatchers are started + autostart (:obj:`bool`, optional): if True, processors are started immediately after object's creation; if False, should be started manually by `start` method. Defaults to True. Attributes: _all_delayq (:obj:`telegram.ext.messagequeue.DelayQueue`): actual - `DelayQueue` used for *all-type* callback dispatching + `DelayQueue` used for *all-type* callback processing _group_delayq (:obj:`telegram.ext.messagequeue.DelayQueue`): actual - `DelayQueue` used for *group-type* callback dispatching + `DelayQueue` used for *group-type* callback processing ''' def __init__(self, @@ -215,7 +237,7 @@ def __init__(self, autostart=autostart) def start(self): - '''Method is used to manually start the `MessageQueue` dispatching + '''Method is used to manually start the `MessageQueue` processing Returns: None @@ -230,14 +252,14 @@ def stop(self, timeout=None): stop.__doc__ = DelayQueue.stop.__doc__ or '' # reuse docsting if any def __call__(self, promise, is_group_msg=False): - '''Dispatches callables through troughput-limiting queues to avoid + '''Processes callables in troughput-limiting queues to avoid hitting limits (specified with \*_burst_limit and *\_time_limit_ms). Args: promise (:obj:`callable`): mainly the :obj:`telegram.utils.promise.Promise` (see Notes for other - callables), that is dispatched through delay queues + callables), that is processed in delay queues is_group_msg (:obj:`bool`, optional): defines whether `promise` - would be dispatched through *group*+*all* `DelayQueue`s + would be processed in *group*+*all* `DelayQueue`s (if set to ``True``), or only through *all* `DelayQueue` (if set to ``False``), resulting in needed delays to avoid hitting specified limits. @@ -252,8 +274,7 @@ def __call__(self, promise, is_group_msg=False): outside exceptions or the proper `exc_route` handler is provided. Returns: - Either :obj:`telegram.utils.promise.Promise` in case call is - queued, or original method's return value if it's not. + :obj:`callable` used as `promise` argument. ''' if not is_group_msg: # ignore middle group delay self._all_delayq(promise) @@ -283,12 +304,12 @@ def queuedmessage(method): Args: queued (:obj:`bool`, optional): if set to ``True``, the `MessageQueue` - is used to dispatch output messages. + is used to process output messages. Defaults to `self._is_queued_out`. isgroup (:obj:`bool`, optional): if set to ``True``, the message is meant to be group-type (as there's no obvious way to determine its type in other way at the moment). Group-type messages could have - additional dispatch delay according to limits set in + additional processing delay according to limits set in `self._out_queue`. Defaults to ``False``. From 62bc1cf5f7f9366727ef14cde73b9668c253a5df Mon Sep 17 00:00:00 2001 From: thodnev Date: Sun, 12 Mar 2017 03:42:43 +0200 Subject: [PATCH 3/5] minor documentation and terminology fixes according to the review --- telegram/ext/messagequeue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telegram/ext/messagequeue.py b/telegram/ext/messagequeue.py index a7fc0a01141..7da7154900f 100644 --- a/telegram/ext/messagequeue.py +++ b/telegram/ext/messagequeue.py @@ -56,7 +56,7 @@ class DelayQueue(threading.Thread): queue (:obj:`queue.Queue`, optional): used to pass callbacks to thread. Creates `queue.Queue` implicitly if not provided. - burst_limit (:obj:`int`, optional): numer of maximum callbacks to + burst_limit (:obj:`int`, optional): number of maximum callbacks to process per time-window defined by `time_limit_ms`. Defaults to 30. time_limit_ms (:obj:`int`, optional): defines width of time-window From 31f2d5a626c5dec1b3475ee13c4b00e2e8945543 Mon Sep 17 00:00:00 2001 From: thodnev Date: Sun, 12 Mar 2017 03:51:13 +0200 Subject: [PATCH 4/5] minor documentation and terminology fixes according to the review --- telegram/ext/messagequeue.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/telegram/ext/messagequeue.py b/telegram/ext/messagequeue.py index 7da7154900f..3d73c2c5e0a 100644 --- a/telegram/ext/messagequeue.py +++ b/telegram/ext/messagequeue.py @@ -294,8 +294,9 @@ def queuedmessage(method): The next object attributes are used by decorator: Attributes: - self._is_queued_out (:obj:`bool`): Value to provide class-defaults - to `queued` kwarg if not provided during wrapped method call. + self._is_messages_queued_default (:obj:`bool`): Value to provide + class-defaults to `queued` kwarg if not provided during wrapped + method call. self._msg_queue (:obj:`telegram.ext.messagequeue.MessageQueue`): The actual `MessageQueue` used to delay outbond messages according to specified time-limits. @@ -320,7 +321,7 @@ def queuedmessage(method): @functools.wraps(method) def wrapped(self, *args, **kwargs): - queued = kwargs.pop('queued', self._is_queued_out) + queued = kwargs.pop('queued', self._is_messages_queued_default) isgroup = kwargs.pop('isgroup', False) if queued: prom = promise.Promise(method, args, kwargs) From 7adb54146694c8ba4feec8af113cf38ea6487598 Mon Sep 17 00:00:00 2001 From: thodnev Date: Wed, 15 Mar 2017 16:41:03 +0200 Subject: [PATCH 5/5] pep8 fix --- telegram/ext/messagequeue.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/telegram/ext/messagequeue.py b/telegram/ext/messagequeue.py index 3d73c2c5e0a..6d512a27fb0 100644 --- a/telegram/ext/messagequeue.py +++ b/telegram/ext/messagequeue.py @@ -164,8 +164,7 @@ def __call__(self, func, *args, **kwargs): None ''' if not self.is_alive() or self.__exit_req: - raise DelayQueueError('Could not process callback ' - 'in stopped thread') + raise DelayQueueError('Could not process callback in stopped thread') self._queue.put((func, args, kwargs)) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy