Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"unclosed transport" warning with long running client under Python 3.10 #1211

Closed
6 tasks done
larashores opened this issue Oct 3, 2024 · 2 comments
Closed
6 tasks done

Comments

@larashores
Copy link

larashores commented Oct 3, 2024

Describe the bug
Running Python 3.10, making any call to S3 without immediately closing the S3 client will cause the following warning to be emitted.

/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/sslproto.py:320: ResourceWarning: unclosed transport <asyncio.sslproto._SSLProtocolTransport object at 0x10814dcc0>
  _warn(f"unclosed transport {self!r}", ResourceWarning, source=self)

Here is a minimally reproducible example:

import asyncio
import aiobotocore
import warnings

import aiobotocore.session

warnings.simplefilter("default")  # Warning only goes to console if this call is made

async def main():
    session = aiobotocore.session.get_session(
        # Replace with real tokens
        {
            "AWS_ACCESS_KEY_ID": "test",
            "AWS_SECRET_ACCESS_KEY": "test",
            "AWS_SESSION_TOKEN": "test",
        }
    )
    async with session.create_client('s3') as s3:
        print("Entered client")
        try:
            await s3.get_object(Bucket='bucket', Key="key")
        except Exception as exc:
            print("Execption", exc)
        # On Python 3.10, a ResourceWarning will appear during the sleep. If instead the client is
        # closed immediately after the get_object() call is made, the warning will not appear.
        # However, this is not desirable, as we want to share the client across multiple coroutines.
        await asyncio.sleep(10)
    
    print("Exited client")
    await asyncio.sleep(10)

if __name__ == "__main__":
    asyncio.run(main())

Running this outputs the following:

Entered client
Execption An error occurred (AccessDenied) when calling the GetObject operation: Access Denied
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/sslproto.py:320: ResourceWarning: unclosed transport <asyncio.sslproto._SSLProtocolTransport object at 0x104d2dcc0>
  _warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
ResourceWarning: Enable tracemalloc to get the object allocation traceback
Exited client

Commenting out the first sleep, the warning disappears:

Entered client
Execption An error occurred (AccessDenied) when calling the GetObject operation: Access Denied
Exited client

Checklist

  • I have reproduced in environment where pip check passes without errors
  • I have provided pip freeze results
  • I have provided sample code or detailed way to reproduce
  • I have tried the same code in botocore to ensure this is an aiobotocore specific issue
  • I have tried similar code in aiohttp to ensure this is is an aiobotocore specific issue
  • I have checked the latest and older versions of aiobotocore/aiohttp/python to see if this is a regression / injection

pip freeze results

aiobotocore==2.15.1
aiohappyeyeballs==2.4.3
aiohttp==3.10.8
aioitertools==0.12.0
aiosignal==1.3.1
async-timeout==4.0.3
attrs==24.2.0
botocore==1.35.23
frozenlist==1.4.1
idna==3.10
jmespath==1.0.1
multidict==6.1.0
python-dateutil==2.9.0.post0
six==1.16.0
typing_extensions==4.12.2
urllib3==2.2.3
wrapt==1.16.0
yarl==1.13.1

Environment:

  • Python Version: 3.10.11
  • OS name and version: MacOS Sonoma 14.6.1

Additional context

  • Running the code on Python 3.11 with the exact same dependencies will fix the issue.
  • I've tried similar code in botocore and the issue doesn't occur. Though the code isn't exactly equivalent since clients are not context managers in botocore
  • I've tried leaving long running sessions in aiohttp and the issue doesn't occur either.
  • I see the same behavior going all the way back into aiobotocore==1.4.0 and aiohttp==3.7.0. Any earlier versions and I start to see other errors that prevents the code from running.
@larashores larashores changed the title Unclosed transport error with long running client under Python 3.10 "unclosed transport" warning with long running client under Python 3.10 Oct 3, 2024
@thehesiod
Copy link
Collaborator

interesting, why only python3.10, here's the trace with -X tracemalloc=25

 python3 -X tracemalloc=25 /tmp/test.py 
Entered client
Execption An error occurred (AccessDenied) when calling the GetObject operation: Access Denied
/opt/homebrew/Cellar/[email protected]/3.10.14_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/sslproto.py:320: ResourceWarning: unclosed transport <asyncio.sslproto._SSLProtocolTransport object at 0x104470280>
  _warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
Object allocated at (most recent call last):
  File "/tmp/test.py", lineno 33
    asyncio.run(main())
  File "/opt/homebrew/Cellar/[email protected]/3.10.14_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/runners.py", lineno 44
    return loop.run_until_complete(main)
  File "/opt/homebrew/Cellar/[email protected]/3.10.14_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/base_events.py", lineno 636
    self.run_forever()
  File "/opt/homebrew/Cellar/[email protected]/3.10.14_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/base_events.py", lineno 603
    self._run_once()
  File "/opt/homebrew/Cellar/[email protected]/3.10.14_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/base_events.py", lineno 1909
    handle._run()
  File "/opt/homebrew/Cellar/[email protected]/3.10.14_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/events.py", lineno 80
    self._context.run(self._callback, *self._args)
  File "/tmp/test.py", lineno 21
    await s3.get_object(Bucket='bucket', Key="key")
  File "/Users/alexmohr/dev/third_party/aiobotocore/aiobotocore/client.py", lineno 394
    http, parsed_response = await self._make_request(
  File "/Users/alexmohr/dev/third_party/aiobotocore/aiobotocore/client.py", lineno 420
    return await self._endpoint.make_request(
  File "/Users/alexmohr/dev/third_party/aiobotocore/aiobotocore/endpoint.py", lineno 97
    success_response, exception = await self._get_response(
  File "/Users/alexmohr/dev/third_party/aiobotocore/aiobotocore/endpoint.py", lineno 139
    success_response, exception = await self._do_get_response(
  File "/Users/alexmohr/dev/third_party/aiobotocore/aiobotocore/endpoint.py", lineno 181
    http_response = await self._send(request)
  File "/Users/alexmohr/dev/third_party/aiobotocore/aiobotocore/endpoint.py", lineno 294
    return await self.http_session.send(request)
  File "/Users/alexmohr/dev/third_party/aiobotocore/aiobotocore/httpsession.py", lineno 222
    response = await session.request(
  File "/Users/alexmohr/dev/third_party/aiobotocore/venv-3.10/lib/python3.10/site-packages/aiohttp/client.py", lineno 657
    conn = await self._connector.connect(
  File "/Users/alexmohr/dev/third_party/aiobotocore/venv-3.10/lib/python3.10/site-packages/aiohttp/connector.py", lineno 564
    proto = await self._create_connection(req, traces, timeout)
  File "/Users/alexmohr/dev/third_party/aiobotocore/venv-3.10/lib/python3.10/site-packages/aiohttp/connector.py", lineno 975
    _, proto = await self._create_direct_connection(req, traces, timeout)
  File "/Users/alexmohr/dev/third_party/aiobotocore/venv-3.10/lib/python3.10/site-packages/aiohttp/connector.py", lineno 1319
    transp, proto = await self._wrap_create_connection(
  File "/Users/alexmohr/dev/third_party/aiobotocore/venv-3.10/lib/python3.10/site-packages/aiohttp/connector.py", lineno 1080
    return await self._loop.create_connection(*args, **kwargs, sock=sock)
  File "/opt/homebrew/Cellar/[email protected]/3.10.14_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/base_events.py", lineno 1103
    transport, protocol = await self._create_connection_transport(
  File "/opt/homebrew/Cellar/[email protected]/3.10.14_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/base_events.py", lineno 1125
    transport = self._make_ssl_transport(
  File "/opt/homebrew/Cellar/[email protected]/3.10.14_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/selector_events.py", lineno 69
    ssl_protocol = sslproto.SSLProtocol(
  File "/opt/homebrew/Cellar/[email protected]/3.10.14_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/sslproto.py", lineno 451
    self._app_transport = _SSLProtocolTransport(self._loop, self)
/opt/homebrew/Cellar/[email protected]/3.10.14_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/sslproto.py:320: ResourceWarning: unclosed transport <asyncio.sslproto._SSLProtocolTransport object at 0x1044715a0>
  _warn(f"unclosed transport {self!r}", ResourceWarning, source=self)

@thehesiod
Copy link
Collaborator

this seems to be a bug with python3.10 and/or aiohttp. Even if I explicitly release the connections python3.10 still complains. Note that aiohttp will release the connection back to the pool at the end of the stream (As in this case) so aiobotocore does not need to explicitly release it. In this case I don't think there's a "leak" because the connection is still re-usable as the body was fully read. I suggest upgrading to 3.11 or asking aiohttp why this is the case in 3.10

@thehesiod thehesiod mentioned this issue Dec 28, 2024
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants