mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-10-24 01:46:22 +02:00
Add Parameter media_write_timeout
to HTTPXRequest
and Method ApplicationBuilder.media_write_timeout
(#4120)
This commit is contained in:
parent
277031cfb2
commit
9c263fbd1a
4 changed files with 97 additions and 8 deletions
|
@ -78,6 +78,7 @@ _BOT_CHECKS = [
|
||||||
("connect_timeout", "connect_timeout"),
|
("connect_timeout", "connect_timeout"),
|
||||||
("read_timeout", "read_timeout"),
|
("read_timeout", "read_timeout"),
|
||||||
("write_timeout", "write_timeout"),
|
("write_timeout", "write_timeout"),
|
||||||
|
("media_write_timeout", "media_write_timeout"),
|
||||||
("http_version", "http_version"),
|
("http_version", "http_version"),
|
||||||
("get_updates_connection_pool_size", "get_updates_connection_pool_size"),
|
("get_updates_connection_pool_size", "get_updates_connection_pool_size"),
|
||||||
("get_updates_proxy", "get_updates_proxy"),
|
("get_updates_proxy", "get_updates_proxy"),
|
||||||
|
@ -152,6 +153,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||||
"_http_version",
|
"_http_version",
|
||||||
"_job_queue",
|
"_job_queue",
|
||||||
"_local_mode",
|
"_local_mode",
|
||||||
|
"_media_write_timeout",
|
||||||
"_persistence",
|
"_persistence",
|
||||||
"_pool_timeout",
|
"_pool_timeout",
|
||||||
"_post_init",
|
"_post_init",
|
||||||
|
@ -181,6 +183,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]):
|
||||||
self._connect_timeout: ODVInput[float] = DEFAULT_NONE
|
self._connect_timeout: ODVInput[float] = DEFAULT_NONE
|
||||||
self._read_timeout: ODVInput[float] = DEFAULT_NONE
|
self._read_timeout: ODVInput[float] = DEFAULT_NONE
|
||||||
self._write_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._pool_timeout: ODVInput[float] = DEFAULT_NONE
|
||||||
self._request: DVInput[BaseRequest] = DEFAULT_NONE
|
self._request: DVInput[BaseRequest] = DEFAULT_NONE
|
||||||
self._get_updates_connection_pool_size: DVInput[int] = 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"),
|
"write_timeout": getattr(self, f"{prefix}write_timeout"),
|
||||||
"pool_timeout": getattr(self, f"{prefix}pool_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-
|
# Get timeouts that were actually set-
|
||||||
effective_timeouts = {
|
effective_timeouts = {
|
||||||
key: value for key, value in timeouts.items() if not isinstance(value, DefaultValue)
|
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 ""
|
prefix = "get_updates_" if get_updates else ""
|
||||||
name = prefix + "request"
|
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
|
# 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
|
# 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):
|
if not isinstance(getattr(self, f"_{prefix}{attr}"), DefaultValue):
|
||||||
raise RuntimeError(_TWO_ARGS_REQ.format(name, attr))
|
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
|
self._write_timeout = write_timeout
|
||||||
return self
|
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:
|
def pool_timeout(self: BuilderType, pool_timeout: Optional[float]) -> BuilderType:
|
||||||
"""Sets the connection pool's connection freeing timeout for the
|
"""Sets the connection pool's connection freeing timeout for the
|
||||||
:paramref:`~telegram.request.HTTPXRequest.pool_timeout` parameter of
|
:paramref:`~telegram.request.HTTPXRequest.pool_timeout` parameter of
|
||||||
|
|
|
@ -64,6 +64,10 @@ class HTTPXRequest(BaseRequest):
|
||||||
a network socket; i.e. POSTing a request or uploading a file).
|
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`.
|
This value is used unless a different value is passed to :meth:`do_request`.
|
||||||
Defaults to ``5``.
|
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
|
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
|
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
|
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
|
.. _the docs of httpx: https://www.python-httpx.org/environment_variables/#proxies
|
||||||
|
|
||||||
.. versionadded:: 20.7
|
.. 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__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
@ -128,6 +138,7 @@ class HTTPXRequest(BaseRequest):
|
||||||
http_version: HTTPVersion = "1.1",
|
http_version: HTTPVersion = "1.1",
|
||||||
socket_options: Optional[Collection[SocketOpt]] = None,
|
socket_options: Optional[Collection[SocketOpt]] = None,
|
||||||
proxy: Optional[Union[str, httpx.Proxy, httpx.URL]] = 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:
|
if proxy_url is not None and proxy is not None:
|
||||||
raise ValueError("The parameters `proxy_url` and `proxy` are mutually exclusive.")
|
raise ValueError("The parameters `proxy_url` and `proxy` are mutually exclusive.")
|
||||||
|
@ -142,6 +153,7 @@ class HTTPXRequest(BaseRequest):
|
||||||
)
|
)
|
||||||
|
|
||||||
self._http_version = http_version
|
self._http_version = http_version
|
||||||
|
self._media_write_timeout = media_write_timeout
|
||||||
timeout = httpx.Timeout(
|
timeout = httpx.Timeout(
|
||||||
connect=connect_timeout,
|
connect=connect_timeout,
|
||||||
read=read_timeout,
|
read=read_timeout,
|
||||||
|
@ -251,11 +263,7 @@ class HTTPXRequest(BaseRequest):
|
||||||
pool_timeout = self._client.timeout.pool
|
pool_timeout = self._client.timeout.pool
|
||||||
|
|
||||||
if isinstance(write_timeout, DefaultValue):
|
if isinstance(write_timeout, DefaultValue):
|
||||||
# Making the networking backend decide on the proper timeout values instead of doing
|
write_timeout = self._client.timeout.write if not files else self._media_write_timeout
|
||||||
# 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
|
|
||||||
|
|
||||||
timeout = httpx.Timeout(
|
timeout = httpx.Timeout(
|
||||||
connect=connect_timeout,
|
connect=connect_timeout,
|
||||||
|
|
|
@ -76,6 +76,9 @@ class TestApplicationBuilder:
|
||||||
for argument in arguments:
|
for argument in arguments:
|
||||||
if argument == "self":
|
if argument == "self":
|
||||||
continue
|
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}"
|
assert hasattr(builder, prefix + argument), f"missing method {prefix}{argument}"
|
||||||
|
|
||||||
@pytest.mark.parametrize("bot_class", [Bot, ExtBot])
|
@pytest.mark.parametrize("bot_class", [Bot, ExtBot])
|
||||||
|
@ -202,6 +205,7 @@ class TestApplicationBuilder:
|
||||||
"pool_timeout",
|
"pool_timeout",
|
||||||
"read_timeout",
|
"read_timeout",
|
||||||
"write_timeout",
|
"write_timeout",
|
||||||
|
"media_write_timeout",
|
||||||
"proxy",
|
"proxy",
|
||||||
"proxy_url",
|
"proxy_url",
|
||||||
"socket_options",
|
"socket_options",
|
||||||
|
@ -272,6 +276,7 @@ class TestApplicationBuilder:
|
||||||
"pool_timeout",
|
"pool_timeout",
|
||||||
"read_timeout",
|
"read_timeout",
|
||||||
"write_timeout",
|
"write_timeout",
|
||||||
|
"media_write_timeout",
|
||||||
"proxy",
|
"proxy",
|
||||||
"proxy_url",
|
"proxy_url",
|
||||||
"socket_options",
|
"socket_options",
|
||||||
|
@ -316,6 +321,7 @@ class TestApplicationBuilder:
|
||||||
"pool_timeout",
|
"pool_timeout",
|
||||||
"read_timeout",
|
"read_timeout",
|
||||||
"write_timeout",
|
"write_timeout",
|
||||||
|
"media_write_timeout",
|
||||||
"proxy",
|
"proxy",
|
||||||
"proxy_url",
|
"proxy_url",
|
||||||
"socket_options",
|
"socket_options",
|
||||||
|
@ -384,12 +390,20 @@ class TestApplicationBuilder:
|
||||||
http2: object
|
http2: object
|
||||||
transport: object = None
|
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(httpx, "AsyncClient", Client)
|
||||||
|
monkeypatch.setattr(HTTPXRequest, "__init__", init_httpx_request)
|
||||||
|
|
||||||
builder = ApplicationBuilder().token(bot.token)
|
builder = ApplicationBuilder().token(bot.token)
|
||||||
builder.connection_pool_size(1).connect_timeout(2).pool_timeout(3).read_timeout(
|
builder.connection_pool_size(1).connect_timeout(2).pool_timeout(3).read_timeout(
|
||||||
4
|
4
|
||||||
).write_timeout(5).http_version("1.1")
|
).write_timeout(5).media_write_timeout(6).http_version("1.1")
|
||||||
getattr(builder, proxy_method)("proxy")
|
getattr(builder, proxy_method)("proxy")
|
||||||
app = builder.build()
|
app = builder.build()
|
||||||
client = app.bot.request._client
|
client = app.bot.request._client
|
||||||
|
@ -399,7 +413,9 @@ class TestApplicationBuilder:
|
||||||
assert client.proxy == "proxy"
|
assert client.proxy == "proxy"
|
||||||
assert client.http1 is True
|
assert client.http1 is True
|
||||||
assert client.http2 is False
|
assert client.http2 is False
|
||||||
|
assert media_write_timeout == [6, None]
|
||||||
|
|
||||||
|
media_write_timeout.clear()
|
||||||
builder = ApplicationBuilder().token(bot.token)
|
builder = ApplicationBuilder().token(bot.token)
|
||||||
builder.get_updates_connection_pool_size(1).get_updates_connect_timeout(
|
builder.get_updates_connection_pool_size(1).get_updates_connect_timeout(
|
||||||
2
|
2
|
||||||
|
@ -417,6 +433,7 @@ class TestApplicationBuilder:
|
||||||
assert client.proxy == "get_updates_proxy"
|
assert client.proxy == "get_updates_proxy"
|
||||||
assert client.http1 is True
|
assert client.http1 is True
|
||||||
assert client.http2 is False
|
assert client.http2 is False
|
||||||
|
assert media_write_timeout == [None, None]
|
||||||
|
|
||||||
def test_custom_socket_options(self, builder, monkeypatch, bot):
|
def test_custom_socket_options(self, builder, monkeypatch, bot):
|
||||||
httpx_request_kwargs = []
|
httpx_request_kwargs = []
|
||||||
|
|
|
@ -738,6 +738,39 @@ class TestHTTPXRequestWithoutRequest:
|
||||||
# other than HTTPXRequest
|
# other than HTTPXRequest
|
||||||
assert len(recwarn) == 0
|
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):
|
async def test_socket_opts(self, monkeypatch):
|
||||||
transport_kwargs = {}
|
transport_kwargs = {}
|
||||||
transport_init = AsyncHTTPTransport.__init__
|
transport_init = AsyncHTTPTransport.__init__
|
||||||
|
|
Loading…
Reference in a new issue