FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Forward pool timeouts to polling error callbacks by Mmx233 · Pull Request #5337 · python-telegram-bot/python-telegram-bot · GitHub

Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .py  (7) All 1 file type selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
19 changes: 19 additions & 0 deletions src/telegram/error.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
Expand Up @@ -37,6 +37,7 @@
"InvalidToken",
"NetworkError",
"PassportDecryptionError",
"PoolTimeout",
"RetryAfter",
"TelegramError",
"TimedOut",
Expand Down Expand Up @@ -179,6 +180,24 @@ def __init__(self, message: str | None = None) -> None:
super().__init__(message or "Timed out")


class PoolTimeout(TimedOut):
"""Raised when a request could not acquire a connection from the connection pool in time.

This is a subclass of :class:`TimedOut` to preserve compatibility with code handling all
request timeouts.

.. versionadded:: NEXT.VERSION

Args:
message (:obj:`str`, optional): Any additional information about the exception.
"""

__slots__ = ()

def __init__(self, message: str | None = None) -> None:
super().__init__(message or "Pool timed out")


class ChatMigrated(TelegramError):
"""
Raised when the requested group chat migrated to supergroup and has a new chat id.
Expand Down
13 changes: 12 additions & 1 deletion src/telegram/ext/_utils/networkloop.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
Expand Up @@ -36,7 +36,7 @@
from collections.abc import Callable, Coroutine

from telegram._utils.logging import get_logger
from telegram.error import InvalidToken, RetryAfter, TelegramError, TimedOut
from telegram.error import InvalidToken, PoolTimeout, RetryAfter, TelegramError, TimedOut

_LOGGER = get_logger(__name__)

Expand Down Expand Up @@ -169,6 +169,17 @@ async def do_action() -> None:
exception_info = f"{exc}. Adding {slack_time} seconds to the specified time."

# Check max_retries for RetryAfter as well
if check_max_retries_and_log(retries, exception_info):
raise
except PoolTimeout as pool_timeout:
# Unlike regular request timeouts, pool exhaustion should be observable by callers.
if on_err_cb:
on_err_cb(pool_timeout)

# If failure is due to timeout, we should retry asap.
cur_interval = 0
exception_info = f"Pool timed out: {pool_timeout}."

if check_max_retries_and_log(retries, exception_info):
raise
except TimedOut as toe:
Expand Down
10 changes: 7 additions & 3 deletions src/telegram/request/_httpxrequest.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
Expand Up @@ -26,7 +26,7 @@
from telegram._utils.defaultvalue import DefaultValue
from telegram._utils.logging import get_logger
from telegram._utils.types import HTTPVersion, ODVInput, SocketOpt
from telegram.error import NetworkError, TimedOut
from telegram.error import NetworkError, PoolTimeout, TimedOut
from telegram.request._baserequest import BaseRequest
from telegram.request._requestdata import RequestData

Expand Down Expand Up @@ -79,9 +79,13 @@ class HTTPXRequest(BaseRequest):
Defaults to ``1``.

Warning:
With a finite pool timeout, you must expect :exc:`telegram.error.TimedOut`
With a finite pool timeout, you must expect :exc:`telegram.error.PoolTimeout`
exceptions to be thrown when more requests are made simultaneously than there are
connections in the connection pool!

.. versionchanged:: NEXT.VERSION
Raises :exc:`telegram.error.PoolTimeout`, a subclass of
:exc:`telegram.error.TimedOut`, instead of :exc:`telegram.error.TimedOut`.
http_version (:obj:`str`, optional): If ``"2"`` or ``"2.0"``, HTTP/2 will be used instead
of HTTP/1.1. Defaults to ``"1.1"``.

Expand Down Expand Up @@ -286,7 +290,7 @@ async def do_request(
)
except httpx.TimeoutException as err:
if isinstance(err, httpx.PoolTimeout):
raise TimedOut(
raise PoolTimeout(
message=(
"Pool timeout: All connections in the connection pool are occupied. "
"Request was *not* sent to Telegram. Consider adjusting the connection "
Expand Down
26 changes: 25 additions & 1 deletion tests/ext/_utils/test_networkloop.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
Expand Up @@ -26,7 +26,7 @@

import pytest

from telegram.error import InvalidToken, RetryAfter, TelegramError, TimedOut
from telegram.error import InvalidToken, PoolTimeout, RetryAfter, TelegramError, TimedOut
from telegram.ext._utils.networkloop import network_retry_loop


Expand Down Expand Up @@ -194,6 +194,30 @@ async def action_with_telegram_error():
assert error_callback_count == 3
assert isinstance(caught_exception, TelegramError)

async def test_error_callback_called_for_pool_timeout(self):
"""Test that pool timeouts are observable while regular timeouts remain silent."""
pool_timeout = PoolTimeout("Test pool timeout")
error_callback_count = 0

def error_callback(exc):
nonlocal error_callback_count
error_callback_count += 1
assert exc is pool_timeout

async def action_with_pool_timeout():
raise pool_timeout

with pytest.raises(PoolTimeout):
await network_retry_loop(
action_cb=action_with_pool_timeout,
on_err_cb=error_callback,
description="Test PoolTimeout callback",
interval=0,
max_retries=2,
)

assert error_callback_count == 3

async def test_success_after_retries(self):
"""Test that action succeeds after some retries."""
call_count = 0
Expand Down
5 changes: 3 additions & 2 deletions tests/ext/test_updater.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
Expand Up @@ -28,7 +28,7 @@
import pytest

from telegram import Bot, InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.error import InvalidToken, RetryAfter, TelegramError, TimedOut
from telegram.error import InvalidToken, PoolTimeout, RetryAfter, TelegramError, TimedOut
from telegram.ext import ExtBot, InvalidCallbackData, Updater
from tests.auxil.build_messages import make_message, make_message_update
from tests.auxil.envvars import TEST_WITH_OPT_DEPS
Expand Down Expand Up @@ -492,10 +492,11 @@ async def delete_webhook(*args, **kwargs):
("error", "callback_should_be_called"),
argvalues=[
(TelegramError("TestMessage"), True),
(PoolTimeout("TestMessage"), True),
(RetryAfter(1), False),
(TimedOut("TestMessage"), False),
],
ids=("TelegramError", "RetryAfter", "TimedOut"),
ids=("TelegramError", "PoolTimeout", "RetryAfter", "TimedOut"),
)
@pytest.mark.parametrize("custom_error_callback", [True, False])
async def test_start_polling_exceptions_and_error_callback(
Expand Down
4 changes: 3 additions & 1 deletion tests/request/test_request.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
Expand Up @@ -43,6 +43,7 @@
Forbidden,
InvalidToken,
NetworkError,
PoolTimeout,
RetryAfter,
TelegramError,
TimedOut,
Expand Down Expand Up @@ -658,13 +659,14 @@ async def request(_, **kwargs):
monkeypatch.setattr(httpx.AsyncClient, "request", request)

async with HTTPXRequest(pool_timeout=0.02) as httpx_request:
with pytest.raises(TimedOut, match="Pool timeout") as exc_info:
with pytest.raises(PoolTimeout, match="Pool timeout") as exc_info:
await asyncio.gather(
httpx_request.do_request(method="GET", url="URL"),
httpx_request.do_request(method="GET", url="URL"),
)

assert exc_info.value.__cause__ is pool_timeout
assert isinstance(exc_info.value, TimedOut)

@pytest.mark.parametrize("media", [True, False])
async def test_do_request_write_timeout(
Expand Down
8 changes: 8 additions & 0 deletions tests/test_error.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
Expand Up @@ -31,6 +31,7 @@
InvalidToken,
NetworkError,
PassportDecryptionError,
PoolTimeout,
RetryAfter,
TelegramError,
TimedOut,
Expand Down Expand Up @@ -89,6 +90,10 @@ def test_timed_out(self):
with pytest.raises(TimedOut, match=r"^Timed out$"):
raise TimedOut

def test_pool_timeout(self):
with pytest.raises(PoolTimeout, match=r"^Pool timed out$"):
raise PoolTimeout

def test_chat_migrated(self):
with pytest.raises(ChatMigrated, match="New chat id: 1234") as e:
raise ChatMigrated(1234)
Expand Down Expand Up @@ -130,6 +135,7 @@ def test_conflict(self):
(NetworkError("test message"), ["message"]),
(BadRequest("test message"), ["message"]),
(TimedOut(), ["message"]),
(PoolTimeout(), ["message"]),
(ChatMigrated(1234), ["message", "new_chat_id"]),
(RetryAfter(12), ["message", "retry_after"]),
(RetryAfter(dtm.timedelta(seconds=12)), ["message", "retry_after"]),
Expand Down Expand Up @@ -157,6 +163,7 @@ def test_errors_pickling(self, exception, attributes):
(NetworkError("test message")),
(BadRequest("test message")),
(TimedOut()),
(PoolTimeout()),
(ChatMigrated(1234)),
(RetryAfter(dtm.timedelta(seconds=12))),
(Conflict("test message")),
Expand Down Expand Up @@ -198,6 +205,7 @@ def make_assertion(cls):
EndPointNotFound,
},
NetworkError: {BadRequest, TimedOut},
TimedOut: {PoolTimeout},
}
)

Expand Down

Back | FazBrowse Home | New Git URL