Add Parameter media_write_timeout to HTTPXRequest and Method ApplicationBuilder.media_write_timeout (#4120)

This commit is contained in:
Bibo-Joshi 2024-02-26 17:47:04 +01:00 committed by GitHub
parent 277031cfb2
commit 9c263fbd1a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 97 additions and 8 deletions

View file

@ -78,6 +78,7 @@ _BOT_CHECKS = [
("connect_timeout", "connect_timeout"),
("read_timeout", "read_timeout"),
("write_timeout", "write_timeout"),
("media_write_timeout", "media_write_timeout"),
("http_version", "http_version"),
("get_updates_connection_pool_size", "get_updates_connection_pool_size"),
("get_updates_proxy", "get_updates_proxy"),
@ -152,6 +153,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
"_http_version",
"_job_queue",
"_local_mode",
"_media_write_timeout",
"_persistence",
"_pool_timeout",
"_post_init",
@ -181,6 +183,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
self._connect_timeout: ODVInput[float] = DEFAULT_NONE
self._read_timeout: ODVInput[float] = DEFAULT_NONE
self._write_timeout: ODVInput[float] = DEFAULT_NONE
self._media_write_timeout: ODVInput[float] = DEFAULT_NONE
self._pool_timeout: ODVInput[float] = DEFAULT_NONE
self._request: DVInput[BaseRequest] = DEFAULT_NONE
self._get_updates_connection_pool_size: DVInput[int] = DEFAULT_NONE
@ -243,6 +246,10 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
"write_timeout": getattr(self, f"{prefix}write_timeout"),
"pool_timeout": getattr(self, f"{prefix}pool_timeout"),
}
if not get_updates:
timeouts["media_write_timeout"] = self._media_write_timeout
# Get timeouts that were actually set-
effective_timeouts = {
key: value for key, value in timeouts.items() if not isinstance(value, DefaultValue)
@ -424,9 +431,13 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
prefix = "get_updates_" if get_updates else ""
name = prefix + "request"
timeouts = ["connect_timeout", "read_timeout", "write_timeout", "pool_timeout"]
if not get_updates:
timeouts.append("media_write_timeout")
# Code below tests if it's okay to set a Request object. Only okay if no other request args
# or instances containing a Request were set previously
for attr in ("connect_timeout", "read_timeout", "write_timeout", "pool_timeout"):
for attr in timeouts:
if not isinstance(getattr(self, f"_{prefix}{attr}"), DefaultValue):
raise RuntimeError(_TWO_ARGS_REQ.format(name, attr))
@ -617,6 +628,26 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
self._write_timeout = write_timeout
return self
def media_write_timeout(
self: BuilderType, media_write_timeout: Optional[float]
) -> BuilderType:
"""Sets the media write operation timeout for the
:paramref:`~telegram.request.HTTPXRequest.media_write_timeout` parameter of
:attr:`telegram.Bot.request`. Defaults to ``20``.
.. versionadded:: NEXT.VERSION
Args:
media_write_timeout (:obj:`float`): See
:paramref:`telegram.request.HTTPXRequest.media_write_timeout` for more information.
Returns:
:class:`ApplicationBuilder`: The same builder with the updated argument.
"""
self._request_param_check(name="media_write_timeout", get_updates=False)
self._media_write_timeout = media_write_timeout
return self
def pool_timeout(self: BuilderType, pool_timeout: Optional[float]) -> BuilderType:
"""Sets the connection pool's connection freeing timeout for the
:paramref:`~telegram.request.HTTPXRequest.pool_timeout` parameter of

View file

@ -64,6 +64,10 @@ class HTTPXRequest(BaseRequest):
a network socket; i.e. POSTing a request or uploading a file).
This value is used unless a different value is passed to :meth:`do_request`.
Defaults to ``5``.
Hint:
This timeout is used for all requests except for those that upload media/files.
For the latter, :paramref:`media_write_timeout` is used.
connect_timeout (:obj:`float` | :obj:`None`, optional): If passed, specifies the
maximum amount of time (in seconds) to wait for a connection attempt to a server
to succeed. This value is used unless a different value is passed to
@ -112,10 +116,16 @@ class HTTPXRequest(BaseRequest):
.. _the docs of httpx: https://www.python-httpx.org/environment_variables/#proxies
.. versionadded:: 20.7
media_write_timeout (:obj:`float` | :obj:`None`, optional): Like :paramref:`write_timeout`,
but used only for requests that upload media/files. This value is used unless a
different value is passed to :paramref:`do_request.write_timeout` of
:meth:`do_request`. Defaults to ``20`` seconds.
.. versionadded:: NEXT.VERSION
"""
__slots__ = ("_client", "_client_kwargs", "_http_version")
__slots__ = ("_client", "_client_kwargs", "_http_version", "_media_write_timeout")
def __init__(
self,
@ -128,6 +138,7 @@ class HTTPXRequest(BaseRequest):
http_version: HTTPVersion = "1.1",
socket_options: Optional[Collection[SocketOpt]] = None,
proxy: Optional[Union[str, httpx.Proxy, httpx.URL]] = None,
media_write_timeout: Optional[float] = 20.0,
):
if proxy_url is not None and proxy is not None:
raise ValueError("The parameters `proxy_url` and `proxy` are mutually exclusive.")
@ -142,6 +153,7 @@ class HTTPXRequest(BaseRequest):
)
self._http_version = http_version
self._media_write_timeout = media_write_timeout
timeout = httpx.Timeout(
connect=connect_timeout,
read=read_timeout,
@ -251,11 +263,7 @@ class HTTPXRequest(BaseRequest):
pool_timeout = self._client.timeout.pool
if isinstance(write_timeout, DefaultValue):
# Making the networking backend decide on the proper timeout values instead of doing
# it via the default values of the Bot methods was introduced in version 20.7.
# We hard-code the value here for now until we add additional parameters to this
# class to control the media_write_timeout separately.
write_timeout = self._client.timeout.write if not files else 20
write_timeout = self._client.timeout.write if not files else self._media_write_timeout
timeout = httpx.Timeout(
connect=connect_timeout,

View file

@ -76,6 +76,9 @@ class TestApplicationBuilder:
for argument in arguments:
if argument == "self":
continue
if argument == "media_write_timeout" and get_updates:
# get_updates never makes media requests
continue
assert hasattr(builder, prefix + argument), f"missing method {prefix}{argument}"
@pytest.mark.parametrize("bot_class", [Bot, ExtBot])
@ -202,6 +205,7 @@ class TestApplicationBuilder:
"pool_timeout",
"read_timeout",
"write_timeout",
"media_write_timeout",
"proxy",
"proxy_url",
"socket_options",
@ -272,6 +276,7 @@ class TestApplicationBuilder:
"pool_timeout",
"read_timeout",
"write_timeout",
"media_write_timeout",
"proxy",
"proxy_url",
"socket_options",
@ -316,6 +321,7 @@ class TestApplicationBuilder:
"pool_timeout",
"read_timeout",
"write_timeout",
"media_write_timeout",
"proxy",
"proxy_url",
"socket_options",
@ -384,12 +390,20 @@ class TestApplicationBuilder:
http2: object
transport: object = None
original_init = HTTPXRequest.__init__
media_write_timeout = []
def init_httpx_request(self_, *args, **kwargs):
media_write_timeout.append(kwargs.get("media_write_timeout"))
original_init(self_, *args, **kwargs)
monkeypatch.setattr(httpx, "AsyncClient", Client)
monkeypatch.setattr(HTTPXRequest, "__init__", init_httpx_request)
builder = ApplicationBuilder().token(bot.token)
builder.connection_pool_size(1).connect_timeout(2).pool_timeout(3).read_timeout(
4
).write_timeout(5).http_version("1.1")
).write_timeout(5).media_write_timeout(6).http_version("1.1")
getattr(builder, proxy_method)("proxy")
app = builder.build()
client = app.bot.request._client
@ -399,7 +413,9 @@ class TestApplicationBuilder:
assert client.proxy == "proxy"
assert client.http1 is True
assert client.http2 is False
assert media_write_timeout == [6, None]
media_write_timeout.clear()
builder = ApplicationBuilder().token(bot.token)
builder.get_updates_connection_pool_size(1).get_updates_connect_timeout(
2
@ -417,6 +433,7 @@ class TestApplicationBuilder:
assert client.proxy == "get_updates_proxy"
assert client.http1 is True
assert client.http2 is False
assert media_write_timeout == [None, None]
def test_custom_socket_options(self, builder, monkeypatch, bot):
httpx_request_kwargs = []

View file

@ -738,6 +738,39 @@ class TestHTTPXRequestWithoutRequest:
# other than HTTPXRequest
assert len(recwarn) == 0
@pytest.mark.parametrize("init", [True, False])
async def test_setting_media_write_timeout(
self, monkeypatch, init, input_media_photo, recwarn # noqa: F811
):
httpx_request = HTTPXRequest(media_write_timeout=42) if init else HTTPXRequest()
async def request(_, **kwargs):
self.test_flag = kwargs["timeout"].write
return httpx.Response(HTTPStatus.OK, content=b'{"ok": "True", "result": {}}')
monkeypatch.setattr(httpx.AsyncClient, "request", request)
data = {"string": "string", "int": 1, "float": 1.0, "media": input_media_photo}
request_data = RequestData(
parameters=[RequestParameter.from_input(key, value) for key, value in data.items()],
)
# First make sure that custom timeouts are always respected
await httpx_request.post(
"url",
request_data,
write_timeout=43,
)
assert self.test_flag == 43
# Now also ensure that the init value is respected
await httpx_request.post("url", request_data)
assert self.test_flag == 42 if init else 20
# Just for double-checking, since warnings are issued for implementations of BaseRequest
# other than HTTPXRequest
assert len(recwarn) == 0
async def test_socket_opts(self, monkeypatch):
transport_kwargs = {}
transport_init = AsyncHTTPTransport.__init__