mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-11-21 22:56:38 +01:00
Make dispatcher use one context per update (#1283)
* Make dispatcher use one context per update It gives user the option to `overload` context with their own properties in a lower group handler if they like * Improve callbackcontext & run_async docstring - Add note about how you can add custom attributes to context. - Add warnings about how run_async and custom attributes should not be used together. * Small documentation improvements. [ci skip]
This commit is contained in:
parent
2c5eade4f0
commit
b02b68880f
5 changed files with 52 additions and 10 deletions
|
@ -28,6 +28,20 @@ class CallbackContext(object):
|
|||
:attr:`telegram.ext.Dispatcher.add_error_handler` or to the callback of a
|
||||
:class:`telegram.ext.Job`.
|
||||
|
||||
Note:
|
||||
:class:`telegram.ext.Dispatcher` will create a single context for an entire update. This
|
||||
means that if you got 2 handlers in different groups and they both get called, they will
|
||||
get passed the same `CallbackContext` object (of course with proper attributes like
|
||||
`.matches` differing). This allows you to add custom attributes in a lower handler group
|
||||
callback, and then subsequently access those attributes in a higher handler group callback.
|
||||
Note that the attributes on `CallbackContext` might change in the future, so make sure to
|
||||
use a fairly unique name for the attributes.
|
||||
|
||||
Warning:
|
||||
Do not combine custom attributes and @run_async. Due to how @run_async works, it will
|
||||
almost certainly execute the callbacks for an update out of order, and the attributes
|
||||
that you think you added will not be present.
|
||||
|
||||
Attributes:
|
||||
chat_data (:obj:`dict`, optional): A dict that can be used to keep any data in. For each
|
||||
update from the same chat it will be the same ``dict``.
|
||||
|
|
|
@ -317,7 +317,7 @@ class ConversationHandler(Handler):
|
|||
|
||||
return key, handler, check
|
||||
|
||||
def handle_update(self, update, dispatcher, check_result):
|
||||
def handle_update(self, update, dispatcher, check_result, context=None):
|
||||
"""Send the update to the callback for the current state and Handler
|
||||
|
||||
Args:
|
||||
|
@ -328,7 +328,7 @@ class ConversationHandler(Handler):
|
|||
|
||||
"""
|
||||
conversation_key, handler, check_result = check_result
|
||||
new_state = handler.handle_update(update, dispatcher, check_result)
|
||||
new_state = handler.handle_update(update, dispatcher, check_result, context)
|
||||
timeout_job = self.timeout_jobs.pop(conversation_key, None)
|
||||
|
||||
if timeout_job is not None:
|
||||
|
|
|
@ -43,14 +43,16 @@ DEFAULT_GROUP = 0
|
|||
|
||||
|
||||
def run_async(func):
|
||||
"""Function decorator that will run the function in a new thread.
|
||||
"""
|
||||
Function decorator that will run the function in a new thread.
|
||||
|
||||
Will run :attr:`telegram.ext.Dispatcher.run_async`.
|
||||
|
||||
Using this decorator is only possible when only a single Dispatcher exist in the system.
|
||||
|
||||
Note: Use this decorator to run handlers asynchronously.
|
||||
|
||||
Warning:
|
||||
If you're using @run_async you cannot rely on adding custom attributes to
|
||||
:class:`telegram.ext.CallbackContext`s. See its docs for more info.
|
||||
"""
|
||||
|
||||
@wraps(func)
|
||||
|
@ -210,6 +212,10 @@ class Dispatcher(object):
|
|||
def run_async(self, func, *args, **kwargs):
|
||||
"""Queue a function (with given args/kwargs) to be run asynchronously.
|
||||
|
||||
Warning:
|
||||
If you're using @run_async you cannot rely on adding custom attributes to
|
||||
:class:`telegram.ext.CallbackContext`s. See its docs for more info.
|
||||
|
||||
Args:
|
||||
func (:obj:`callable`): The function to run in the thread.
|
||||
*args (:obj:`tuple`, optional): Arguments to `func`.
|
||||
|
@ -315,12 +321,16 @@ class Dispatcher(object):
|
|||
self.logger.exception('An uncaught error was raised while handling the error')
|
||||
return
|
||||
|
||||
context = None
|
||||
|
||||
for group in self.groups:
|
||||
try:
|
||||
for handler in self.handlers[group]:
|
||||
check = handler.check_update(update)
|
||||
if check is not None and check is not False:
|
||||
handler.handle_update(update, self, check)
|
||||
if not context and self.use_context:
|
||||
context = CallbackContext.from_update(update, self)
|
||||
handler.handle_update(update, self, check, context)
|
||||
if self.persistence and isinstance(update, Update):
|
||||
if self.persistence.store_chat_data and update.effective_chat:
|
||||
chat_id = update.effective_chat.id
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains the base class for handlers as used by the Dispatcher."""
|
||||
from telegram.ext.callbackcontext import CallbackContext
|
||||
|
||||
|
||||
class Handler(object):
|
||||
|
@ -99,7 +98,7 @@ class Handler(object):
|
|||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def handle_update(self, update, dispatcher, check_result):
|
||||
def handle_update(self, update, dispatcher, check_result, context=None):
|
||||
"""
|
||||
This method is called if it was determined that an update should indeed
|
||||
be handled by this instance. Calls :attr:`self.callback` along with its respectful
|
||||
|
@ -113,8 +112,7 @@ class Handler(object):
|
|||
check_result: The result from :attr:`check_update`.
|
||||
|
||||
"""
|
||||
if dispatcher.use_context:
|
||||
context = CallbackContext.from_update(update, dispatcher)
|
||||
if context:
|
||||
self.collect_additional_context(context, update, dispatcher, check_result)
|
||||
return self.callback(update, context)
|
||||
else:
|
||||
|
|
|
@ -80,6 +80,26 @@ class TestDispatcher(object):
|
|||
and isinstance(context.error, TelegramError)):
|
||||
self.received = context.error.message
|
||||
|
||||
def test_one_context_per_update(self, cdp):
|
||||
def one(update, context):
|
||||
if update.message.text == 'test':
|
||||
context.my_flag = True
|
||||
|
||||
def two(update, context):
|
||||
if update.message.text == 'test':
|
||||
if not hasattr(context, 'my_flag'):
|
||||
pytest.fail()
|
||||
else:
|
||||
if hasattr(context, 'my_flag'):
|
||||
pytest.fail()
|
||||
|
||||
cdp.add_handler(MessageHandler(Filters.regex('test'), one), group=1)
|
||||
cdp.add_handler(MessageHandler(None, two), group=2)
|
||||
u = Update(1, Message(1, None, None, None, text='test'))
|
||||
cdp.process_update(u)
|
||||
u.message.text = 'something'
|
||||
cdp.process_update(u)
|
||||
|
||||
def test_error_handler(self, dp):
|
||||
dp.add_error_handler(self.error_handler)
|
||||
error = TelegramError('Unauthorized.')
|
||||
|
|
Loading…
Reference in a new issue