From 0415a4ca18e15e734999925fd7f20607f05e2a4b Mon Sep 17 00:00:00 2001 From: Alexey Nikitin <30608416+NewGlad@users.noreply.github.com> Date: Tue, 16 Apr 2024 23:36:07 +0800 Subject: [PATCH] Patchback/backports/3.9/5fd29467fb63efdfae1ace280cec36b1f8139567/pr 8290 (#8311) --- CHANGES/8253.bugfix | 1 + CONTRIBUTORS.txt | 1 + aiohttp/client.py | 73 +++++++++++++++++++----------------- tests/test_client_session.py | 10 +++++ 4 files changed, 50 insertions(+), 35 deletions(-) create mode 100644 CHANGES/8253.bugfix diff --git a/CHANGES/8253.bugfix b/CHANGES/8253.bugfix new file mode 100644 index 00000000000..91b06d9b35d --- /dev/null +++ b/CHANGES/8253.bugfix @@ -0,0 +1 @@ +Fixed "Unclosed client session" when initialization of ClientSession fails -- by :user:`NewGlad`. diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index ab889685fc8..277171a239e 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -27,6 +27,7 @@ Alexander Shorin Alexander Travov Alexandru Mihai Alexey Firsov +Alexey Nikitin Alexey Popravka Alexey Stepanov Amin Etesamian diff --git a/aiohttp/client.py b/aiohttp/client.py index 7e9b32fad6f..32d2c3b7119 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -234,6 +234,44 @@ def __init__( max_field_size: int = 8190, fallback_charset_resolver: _CharsetResolver = lambda r, b: "utf-8", ) -> None: + # We initialise _connector to None immediately, as it's referenced in __del__() + # and could cause issues if an exception occurs during initialisation. + self._connector: Optional[BaseConnector] = None + if timeout is sentinel or timeout is None: + self._timeout = DEFAULT_TIMEOUT + if read_timeout is not sentinel: + warnings.warn( + "read_timeout is deprecated, " "use timeout argument instead", + DeprecationWarning, + stacklevel=2, + ) + self._timeout = attr.evolve(self._timeout, total=read_timeout) + if conn_timeout is not None: + self._timeout = attr.evolve(self._timeout, connect=conn_timeout) + warnings.warn( + "conn_timeout is deprecated, " "use timeout argument instead", + DeprecationWarning, + stacklevel=2, + ) + else: + if not isinstance(timeout, ClientTimeout): + raise ValueError( + f"timeout parameter cannot be of {type(timeout)} type, " + "please use 'timeout=ClientTimeout(...)'", + ) + self._timeout = timeout + if read_timeout is not sentinel: + raise ValueError( + "read_timeout and timeout parameters " + "conflict, please setup " + "timeout.read" + ) + if conn_timeout is not None: + raise ValueError( + "conn_timeout and timeout parameters " + "conflict, please setup " + "timeout.connect" + ) if loop is None: if connector is not None: loop = connector._loop @@ -271,41 +309,6 @@ def __init__( self._default_auth = auth self._version = version self._json_serialize = json_serialize - if timeout is sentinel or timeout is None: - self._timeout = DEFAULT_TIMEOUT - if read_timeout is not sentinel: - warnings.warn( - "read_timeout is deprecated, " "use timeout argument instead", - DeprecationWarning, - stacklevel=2, - ) - self._timeout = attr.evolve(self._timeout, total=read_timeout) - if conn_timeout is not None: - self._timeout = attr.evolve(self._timeout, connect=conn_timeout) - warnings.warn( - "conn_timeout is deprecated, " "use timeout argument instead", - DeprecationWarning, - stacklevel=2, - ) - else: - if not isinstance(timeout, ClientTimeout): - raise ValueError( - f"timeout parameter cannot be of {type(timeout)} type, " - "please use 'timeout=ClientTimeout(...)'", - ) - self._timeout = timeout - if read_timeout is not sentinel: - raise ValueError( - "read_timeout and timeout parameters " - "conflict, please setup " - "timeout.read" - ) - if conn_timeout is not None: - raise ValueError( - "conn_timeout and timeout parameters " - "conflict, please setup " - "timeout.connect" - ) self._raise_for_status = raise_for_status self._auto_decompress = auto_decompress self._trust_env = trust_env diff --git a/tests/test_client_session.py b/tests/test_client_session.py index a0654ed8ccd..416b6bbce5d 100644 --- a/tests/test_client_session.py +++ b/tests/test_client_session.py @@ -885,3 +885,13 @@ async def test_build_url_returns_expected_url( ) -> None: session = await create_session(base_url) assert session._build_url(url) == expected_url + + +async def test_instantiation_with_invalid_timeout_value(loop): + loop.set_debug(False) + logs = [] + loop.set_exception_handler(lambda loop, ctx: logs.append(ctx)) + with pytest.raises(ValueError, match="timeout parameter cannot be .*"): + ClientSession(timeout=1) + # should not have "Unclosed client session" warning + assert not logs