1818# along with this program. If not, see [http://www.gnu.org/licenses/].
1919"""This module contains methods to make POST and GET requests using the aiohttp library."""
2020import asyncio
21+ import logging
2122from typing import Any , Optional , Union
2223
2324import aiohttp
2425import yarl
25- from telegram ._utils .logging import get_logger
26+ from telegram ._utils .defaultvalue import DefaultValue
2627from telegram ._utils .types import ODVInput
2728from telegram .error import NetworkError , TimedOut
2829from telegram .request import BaseRequest , RequestData
2930
30- _LOGGER = get_logger ( __name__ , "AiohttpRequest" )
31+ _LOGGER = logging . getLogger ( "AiohttpRequest" )
3132
3233
3334class AiohttpRequest (BaseRequest ):
@@ -43,13 +44,13 @@ class AiohttpRequest(BaseRequest):
4344 Note:
4445 :paramref:`media_total_timeout` will still be applied if a file is send, so be sure
4546 to also set it to an appropriate value.
46- PylintDoesntAllowMeToWriteTODO Should I warn about this?
4747 media_total_timeout (:obj:`float` | :obj:`None`, optional): This overrides the total
4848 timeout with requests that upload media/files. Defaults to ``20`` seconds.
4949 proxy (:obj:`str` | `yarl.URL``, optional): The URL to a proxy server, aiohttp supports
5050 plain HTTP proxies and HTTP proxies that can be upgraded to HTTPS via the HTTP
5151 CONNECT method. See the docs of aiohttp: https://docs.aiohttp.org/en/stable/
5252 client_advanced.html#aiohttp-client-proxy-support.
53+ proxy_auth (``aiohttp.BasicAuth``, optional): Proxy authorization, see :paramref:`proxy`.
5354 trust_env (:obj:`bool`, optional): In order to read proxy environmental variables, see the
5455 docs of aiohttp: https://docs.aiohttp.org/en/stable/client_advanced.html
5556 #aiohttp-client-proxy-support.
@@ -61,25 +62,28 @@ class AiohttpRequest(BaseRequest):
6162 This parameter is intended for advanced users that want to fine-tune the behavior
6263 of the underlying ``aiohttp`` clientSession. The values passed here will override
6364 all the defaults set by ``python-telegram-bot`` and all other parameters passed to
64- :class:`ClientSession`. The only exception is the :paramref:`media_write_timeout `
65+ :class:`ClientSession`. The only exception is the :paramref:`media_total_timeout `
6566 parameter, which is not passed to the client constructor.
6667 No runtime warnings will be issued about parameters that are overridden in this
6768 way.
6869
6970 """
7071
71- __slots__ = ("_session" , "_session_kwargs" , "_media_total_timeout" )
72+ __slots__ = ("_session" , "_session_kwargs" , "_media_total_timeout" , "_connection_pool_size" )
7273
7374 def __init__ ( # pylint: disable=too-many-arguments,too-many-positional-arguments
7475 self ,
7576 connection_pool_size : int = 1 ,
7677 client_timeout : Optional [aiohttp .ClientTimeout ] = None ,
77- media_total_timeout : Optional [float ] = 20 .0 ,
78+ media_total_timeout : Optional [float ] = 30 .0 ,
7879 proxy : Optional [Union [str , yarl .URL ]] = None ,
80+ proxy_auth : Optional [aiohttp .BasicAuth ] = None ,
7981 trust_env : Optional [bool ] = None ,
8082 aiohttp_kwargs : Optional [dict [str , Any ]] = None ,
8183 ):
8284 self ._media_total_timeout = media_total_timeout
85+ # this needs to be saved in case of initialize gets a closed session
86+ self ._connection_pool_size = connection_pool_size
8387 timeout = (
8488 client_timeout
8589 if client_timeout
@@ -103,6 +107,7 @@ def __init__( # pylint: disable=too-many-arguments,too-many-positional-argument
103107 "timeout" : timeout ,
104108 "connector" : conn ,
105109 "proxy" : proxy ,
110+ "proxy_auth" : proxy_auth ,
106111 "trust_env" : trust_env ,
107112 ** (aiohttp_kwargs or {}),
108113 }
@@ -113,8 +118,8 @@ def __init__( # pylint: disable=too-many-arguments,too-many-positional-argument
113118 def read_timeout (self ) -> Optional [float ]:
114119 """See :attr:`BaseRequest.read_timeout`.
115120
116- This makes not a lot of sense to implement since there is no actual read_timeout.
117- But what can I do .
121+ aiohttp does not have a read timeout. Instead the total timeout for a request (including
122+ connection establishment, request sending and response reading) is returned .
118123 """
119124 return self ._session .timeout .total
120125
@@ -124,6 +129,14 @@ def _build_client(self) -> aiohttp.ClientSession:
124129 async def initialize (self ) -> None :
125130 """See :meth:`BaseRequest.initialize`."""
126131 if self ._session .closed :
132+ # this means the TCPConnector has been closed, so we need to recreate it
133+ try :
134+ loop = asyncio .get_running_loop ()
135+ except RuntimeError :
136+ loop = asyncio .get_event_loop ()
137+
138+ conn = aiohttp .TCPConnector (limit = self ._connection_pool_size , loop = loop )
139+ self ._session_kwargs ["connector" ] = conn
127140 self ._session = self ._build_client ()
128141
129142 async def shutdown (self ) -> None :
@@ -134,7 +147,6 @@ async def shutdown(self) -> None:
134147
135148 await self ._session .close ()
136149
137- # pylint: disable=unused-argument
138150 async def do_request ( # pylint: disable=too-many-arguments,too-many-positional-arguments
139151 self ,
140152 url : str ,
@@ -145,7 +157,18 @@ async def do_request( # pylint: disable=too-many-arguments,too-many-positional-
145157 connect_timeout : ODVInput [float ] = BaseRequest .DEFAULT_NONE ,
146158 pool_timeout : ODVInput [float ] = BaseRequest .DEFAULT_NONE ,
147159 ) -> tuple [int , bytes ]:
148- """See :meth:`BaseRequest.do_request`."""
160+ """See :meth:`BaseRequest.do_request`.
161+
162+ Since aiohttp has differen't timeouts, the params were mapped.
163+
164+ * :paramref:`pool_timeout` is mapped to :attr`~aiohttp.ClientTimeout.connect`
165+ * :paramref:`connect_timeout` is mapped to :attr`~aiohttp.ClientTimeout.sock_connect`
166+ * :paramref:`read_timeout` is mapped to :attr`~aiohttp.ClientTimeout.sock_read`
167+ * :paramref:`write_timeout` is mapped to :attr`~aiohttp.ClientTimeout.ceil_threshold`
168+
169+ The :attr`~aiohttp.ClientTimeout.total` timeout is not changed since it also includes
170+ response reading. You can only change them when initializing the class.
171+ """
149172 if self ._session .closed :
150173 raise RuntimeError ("This AiohttpRequest is not initialized!" )
151174
@@ -161,20 +184,27 @@ async def do_request( # pylint: disable=too-many-arguments,too-many-positional-
161184 filename = request_data .multipart_data [field_name ][0 ],
162185 )
163186
164- # I dont think it makes sense to support the timeout params.
165- # PylintDoesntAllowMeToWriteTOLiDO if no one complains in initial PR
166- # raise warnings if they are passed
167-
168- timeout = (
169- aiohttp .ClientTimeout (
170- total = self ._media_total_timeout ,
171- connect = self ._session_kwargs ["timeout" ].connect ,
172- sock_read = self ._session_kwargs ["timeout" ].sock_read ,
173- sock_connect = self ._session_kwargs ["timeout" ].sock_connect ,
174- ceil_threshold = self ._session_kwargs ["timeout" ].ceil_threshold ,
175- )
176- if request_data and request_data .contains_files
177- else self ._session_kwargs ["timeout" ]
187+ # If user did not specify timeouts (for e.g. in a bot method), use the default ones when we
188+ # created this instance.
189+ if isinstance (read_timeout , DefaultValue ):
190+ read_timeout = self ._session_kwargs ["timeout" ].sock_read
191+ if isinstance (connect_timeout , DefaultValue ):
192+ connect_timeout = self ._session_kwargs ["timeout" ].sock_connect
193+ if isinstance (pool_timeout , DefaultValue ):
194+ pool_timeout = self ._session_kwargs ["timeout" ].connect
195+ if isinstance (write_timeout , DefaultValue ):
196+ write_timeout = self ._session_kwargs ["timeout" ].ceil_threshold
197+
198+ timeout = aiohttp .ClientTimeout (
199+ total = (
200+ self ._media_total_timeout
201+ if (request_data and request_data .contains_files )
202+ else self ._session_kwargs ["timeout" ].total
203+ ),
204+ connect = pool_timeout ,
205+ sock_read = read_timeout ,
206+ sock_connect = connect_timeout ,
207+ ceil_threshold = write_timeout ,
178208 )
179209
180210 try :
0 commit comments