Reworked graceful shutdown (#1124)

* Reworked graceful shutdown

* Remove special errors from polling process

* Update dependencies

* Coverage

* Added changelog
This commit is contained in:
Alex Root Junior 2023-02-18 15:46:28 +02:00 committed by GitHub
parent a332e88bc3
commit d0b7135ca6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 197 additions and 50 deletions

View file

@ -1,7 +1,9 @@
import asyncio
import datetime
import signal
import time
import warnings
from asyncio import Event
from collections import Counter
from typing import Any
from unittest.mock import AsyncMock, patch
@ -667,6 +669,17 @@ class TestDispatcher:
async def test_start_polling(self, bot: MockedBot):
dispatcher = Dispatcher()
with pytest.raises(
ValueError, match="At least one bot instance is required to start polling"
):
await dispatcher.start_polling()
with pytest.raises(
ValueError,
match="Keyword argument 'bot' is not acceptable, "
"the bot instance should be passed as positional argument",
):
await dispatcher.start_polling(bot, bot=bot)
bot.add_result_for(
GetMe, ok=True, result=User(id=42, is_bot=True, first_name="The bot", username="tbot")
)
@ -690,6 +703,65 @@ class TestDispatcher:
mocked_process_update.assert_awaited()
mocked_emit_shutdown.assert_awaited()
async def test_stop_polling(self):
dispatcher = Dispatcher()
with pytest.raises(RuntimeError):
await dispatcher.stop_polling()
assert not dispatcher._stop_signal.is_set()
assert not dispatcher._stopped_signal.is_set()
with patch("asyncio.locks.Event.wait", new_callable=AsyncMock) as mocked_wait:
async with dispatcher._running_lock:
await dispatcher.stop_polling()
assert dispatcher._stop_signal.is_set()
mocked_wait.assert_awaited()
async def test_signal_stop_polling(self):
dispatcher = Dispatcher()
with patch("asyncio.locks.Event.set") as mocked_set:
dispatcher._signal_stop_polling(signal.SIGINT)
mocked_set.assert_not_called()
async with dispatcher._running_lock:
dispatcher._signal_stop_polling(signal.SIGINT)
mocked_set.assert_called()
async def test_stop_polling_by_method(self, bot: MockedBot):
dispatcher = Dispatcher()
bot.add_result_for(
GetMe, ok=True, result=User(id=42, is_bot=True, first_name="The bot", username="tbot")
)
running = Event()
async def _mock_updates(*_):
running.set()
while True:
yield Update(update_id=42)
await asyncio.sleep(1)
with patch(
"aiogram.dispatcher.dispatcher.Dispatcher._process_update", new_callable=AsyncMock
) as mocked_process_update, patch(
"aiogram.dispatcher.dispatcher.Dispatcher._listen_updates",
return_value=_mock_updates(),
):
task = asyncio.ensure_future(dispatcher.start_polling(bot))
await running.wait()
assert not dispatcher._stop_signal.is_set()
assert not dispatcher._stopped_signal.is_set()
await dispatcher.stop_polling()
assert dispatcher._stop_signal.is_set()
assert dispatcher._stopped_signal.is_set()
assert not task.exception()
mocked_process_update.assert_awaited()
@pytest.mark.skip("Stopping by signal should also be tested as the same as stopping by method")
async def test_stop_polling_by_signal(self, bot: MockedBot):
pass
def test_run_polling(self, bot: MockedBot):
dispatcher = Dispatcher()
with patch(