- Notifications
You must be signed in to change notification settings - Fork5.7k
Ensure Safe Handler Looping inApplication.process_update/error
#4802
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Open
Bibo-Joshi wants to merge10 commits intomasterChoose a base branch fromsafe-handler-loop
base:master
Could not load branches
Branch not found:{{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline, and old review comments may become outdated.
Uh oh!
There was an error while loading.Please reload this page.
Open
Changes fromall commits
Commits
Show all changes
10 commits Select commitHold shift + click to select a range
4a3d51a
Ensure Safe Handler Looping in `Application.process_update`
Bibo-Joshibbeb077
Add chango fragment for PR #4802
Bibo-Joshi4b577d2
chango fix
Bibo-Joshi8f349df
Link closed issue in chango fragment
Bibo-Joshi9161e9a
Extend scope of PR
Bibo-Joshi5b819ac
extend a test
Bibo-Joshi2373596
fix deepcopy issue
Bibo-Joshi8a86d18
Merge branch 'master' into safe-handler-loop
Bibo-Joshi19903e0
update comment
Bibo-Joshi1dffd82
Elaborate chango fragment and fix docstring rendering
Bibo-JoshiFile filter
Filter by extension
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
14 changes: 14 additions & 0 deletionschanges/unreleased/4802.RMLufX4UazobYg5aZojyoD.toml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
bugfixes = """ | ||
Fixed a bug where calling ``Application.remove/add_handler`` during update handling can cause a ``RuntimeError`` in ``Application.process_update``. | ||
.. hint:: | ||
Calling ``Application.add/remove_handler`` now has no influence on calls to :meth:`process_update` that are | ||
already in progress. The same holds for ``Application.add/remove_error_handler`` and ``Application.process_error``, respectively. | ||
.. warning:: | ||
This behavior should currently be considered an implementation detail and not as guaranteed behavior. | ||
""" | ||
[[pull_requests]] | ||
uid = "4802" | ||
author_uid = "Bibo-Joshi" | ||
closes_threads = ["4803"] |
50 changes: 45 additions & 5 deletionssrc/telegram/ext/_application.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
89 changes: 89 additions & 0 deletionstests/ext/test_application.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -2583,6 +2583,70 @@ async def callback(update, context): | ||
assert received_updates == {2} | ||
assert len(caplog.records) == 0 | ||
@pytest.mark.parametrize("change_type", ["remove", "add"]) | ||
async def test_process_update_handler_change_groups_during_iteration(self, app, change_type): | ||
run_groups = set() | ||
async def dummy_callback(_, __, g: int): | ||
run_groups.add(g) | ||
for group in range(10, 20): | ||
handler = TypeHandler(int, functools.partial(dummy_callback, g=group)) | ||
app.add_handler(handler, group=group) | ||
async def wait_callback(_, context): | ||
# Trigger a change of the app.handlers dict during the iteration | ||
if change_type == "remove": | ||
context.application.remove_handler(handler, group) | ||
else: | ||
context.application.add_handler( | ||
TypeHandler(int, functools.partial(dummy_callback, g=42)), group=42 | ||
) | ||
app.add_handler(TypeHandler(int, wait_callback)) | ||
async with app: | ||
await app.process_update(1) | ||
# check that exactly those handlers were called that were configured when | ||
# process_update was called | ||
assert run_groups == set(range(10, 20)) | ||
async def test_process_update_handler_change_group_during_iteration(self, app): | ||
async def dummy_callback(_, __): | ||
pass | ||
checked_handlers = set() | ||
class TrackHandler(TypeHandler): | ||
def __init__(self, name: str, *args, **kwargs): | ||
self.name = name | ||
super().__init__(*args, **kwargs) | ||
def check_update(self, update: object) -> bool: | ||
checked_handlers.add(self.name) | ||
return super().check_update(update) | ||
remove_handler = TrackHandler("remove", int, dummy_callback) | ||
add_handler = TrackHandler("add", int, dummy_callback) | ||
class TriggerHandler(TypeHandler): | ||
def check_update(self, update: object) -> bool: | ||
# Trigger a change of the app.handlers *in the same group* during the iteration | ||
app.remove_handler(remove_handler) | ||
app.add_handler(add_handler) | ||
# return False to ensure that additional handlers in the same group are checked | ||
return False | ||
app.add_handler(TriggerHandler(str, dummy_callback)) | ||
app.add_handler(remove_handler) | ||
async with app: | ||
await app.process_update("string update") | ||
Bibo-Joshi marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
# check that exactly those handlers were checked that were configured when | ||
# process_update was called | ||
assert checked_handlers == {"remove"} | ||
async def test_process_error_exception_in_building_context(self, monkeypatch, caplog, app): | ||
# Makes sure that exceptions in building the context don't stop the application | ||
exception = ValueError("TestException") | ||
@@ -2622,3 +2686,28 @@ async def callback(update, context): | ||
assert received_errors == {2} | ||
assert len(caplog.records) == 0 | ||
@pytest.mark.parametrize("change_type", ["remove", "add"]) | ||
async def test_process_error_change_during_iteration(self, app, change_type): | ||
called_handlers = set() | ||
async def dummy_process_error(name: str, *_, **__): | ||
called_handlers.add(name) | ||
add_error_handler = functools.partial(dummy_process_error, "add_handler") | ||
remove_error_handler = functools.partial(dummy_process_error, "remove_handler") | ||
async def trigger_change(*_, **__): | ||
if change_type == "remove": | ||
app.remove_error_handler(remove_error_handler) | ||
else: | ||
app.add_error_handler(add_error_handler) | ||
app.add_error_handler(trigger_change) | ||
app.add_error_handler(remove_error_handler) | ||
async with app: | ||
await app.process_error(update=None, error=None) | ||
# check that exactly those handlers were checked that were configured when | ||
# add_error_handler was called | ||
assert called_handlers == {"remove_handler"} |
Oops, something went wrong.
Uh oh!
There was an error while loading.Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.