Fix closing client session#3733
Fix closing client session#3733asvetlov merged 21 commits intoaio-libs:masterfrom atemate:ay/fix-close-client-session
Conversation
Codecov Report
@@ Coverage Diff @@
## master #3733 +/- ##
==========================================
- Coverage 97.96% 97.86% -0.11%
==========================================
Files 43 43
Lines 8590 8602 +12
Branches 1373 1375 +2
==========================================
+ Hits 8415 8418 +3
- Misses 71 79 +8
- Partials 104 105 +1
Continue to review full report at Codecov.
|
|
Please don't forget to update docs. |
|
Sorry for this long-pending PR, I will return back to it in the end of June 2019 when I have more time, thank you |
aiohttp/connector.py
Outdated
| if waiters: | ||
| await asyncio.gather(*waiters, | ||
| loop=self._loop, | ||
| return_exceptions=True) |
There was a problem hiding this comment.
possibly re-raise ? I'm unsure
There was a problem hiding this comment.
Ineed, we lost exceptions here. But maybe we should just log (as warnings) them instead?
There was a problem hiding this comment.
Heh, very interesting question.
Python and asyncio itself have no MultipleException yet.
Maybe we add it in Python 3.9.
I'm not sure if warnings.warn() is a good choice here: it is not a programming error but network issue.
Please use logging.error() instead.
| proto.close() | ||
| waiters.append(proto.closed) | ||
|
|
||
| # TODO (A.Yushovskiy, 24-May-2019) collect transp. closing futures |
There was a problem hiding this comment.
work with transports here, especially self._cleanup_closed_transports, seem to be a workaround of the non-closed transports for SSL only:
Lines 345 to 348 in dcce5ea
Also, transports in _cleanup_closed_transports are of type asyncio.Transport so we can not modify them to save closing future same way as we did in this PR with ResponseHandler, thus I don't see a way to get the awaitable result of transport.abort().
Good news is that most likely these calls are redundant since we close all protocols (i.e., save them to the list of futures: waiters.append(proto.closed))
asvetlov
left a comment
There was a problem hiding this comment.
Looks good but please fix my comments.
aiohttp/connector.py
Outdated
| from .client_reqrep import ConnectionKey # noqa | ||
| from .tracing import Trace # noqa | ||
|
|
||
| _ListOfFutures = List['asyncio.Future[Optional[Exception]]'] |
There was a problem hiding this comment.
Is it a list of futures that contains None or Exception as a value or list of futures with fut.set_result(None) or fut.set_exception(exc)?
If later -- please use Future[None].
I believe we never return an exception but set it explicitly.
aiohttp/connector.py
Outdated
| if waiters: | ||
| await asyncio.gather(*waiters, | ||
| loop=self._loop, | ||
| return_exceptions=True) |
There was a problem hiding this comment.
Heh, very interesting question.
Python and asyncio itself have no MultipleException yet.
Maybe we add it in Python 3.9.
I'm not sure if warnings.warn() is a good choice here: it is not a programming error but network issue.
Please use logging.error() instead.
| CeilTimeout, | ||
| get_running_loop, | ||
| is_ip_address, | ||
| noop2, |
There was a problem hiding this comment.
Please delete helpers.noop2 function as well.
This is required since aio-libs/aiohttp@6b0bc4e. Fixes: tests/test_aioresponses.py:266: in test_address_as_instance_of_url_combined_with_pass_through api, ext = yield from doit() tests/test_aioresponses.py:258: in doit api_resp = yield from self.session.get(self.url) aioresponses/core.py:328: in _request_mock response = await self.match(method, url, **kwargs) aioresponses/core.py:304: in match response = await matcher.build_response(url, **kwargs) aioresponses/core.py:184: in build_response reason=result.reason) aioresponses/core.py:157: in _build_response resp.content = stream_reader_factory() aioresponses/compat.py:26: in stream_reader_factory protocol = ResponseHandler(loop=loop) ../aiohttp/aiohttp/client_proto.py:41: in __init__ self.closed = self._loop.create_future() # type: asyncio.Future[None] E AttributeError: 'NoneType' object has no attribute 'create_future' Ref: aio-libs/aiohttp#3733
This is required since aio-libs/aiohttp@6b0bc4e. Fixes: tests/test_aioresponses.py:266: in test_address_as_instance_of_url_combined_with_pass_through api, ext = yield from doit() tests/test_aioresponses.py:258: in doit api_resp = yield from self.session.get(self.url) aioresponses/core.py:328: in _request_mock response = await self.match(method, url, **kwargs) aioresponses/core.py:304: in match response = await matcher.build_response(url, **kwargs) aioresponses/core.py:184: in build_response reason=result.reason) aioresponses/core.py:157: in _build_response resp.content = stream_reader_factory() aioresponses/compat.py:26: in stream_reader_factory protocol = ResponseHandler(loop=loop) ../aiohttp/aiohttp/client_proto.py:41: in __init__ self.closed = self._loop.create_future() # type: asyncio.Future[None] E AttributeError: 'NoneType' object has no attribute 'create_future' Ref: aio-libs/aiohttp#3733
This is required since aio-libs/aiohttp@6b0bc4e. Fixes: tests/test_aioresponses.py:266: in test_address_as_instance_of_url_combined_with_pass_through api, ext = yield from doit() tests/test_aioresponses.py:258: in doit api_resp = yield from self.session.get(self.url) aioresponses/core.py:328: in _request_mock response = await self.match(method, url, **kwargs) aioresponses/core.py:304: in match response = await matcher.build_response(url, **kwargs) aioresponses/core.py:184: in build_response reason=result.reason) aioresponses/core.py:157: in _build_response resp.content = stream_reader_factory() aioresponses/compat.py:26: in stream_reader_factory protocol = ResponseHandler(loop=loop) ../aiohttp/aiohttp/client_proto.py:41: in __init__ self.closed = self._loop.create_future() # type: asyncio.Future[None] E AttributeError: 'NoneType' object has no attribute 'create_future' Ref: aio-libs/aiohttp#3733
This is required since aio-libs/aiohttp@6b0bc4e. Fixes: tests/test_aioresponses.py:266: in test_address_as_instance_of_url_combined_with_pass_through api, ext = yield from doit() tests/test_aioresponses.py:258: in doit api_resp = yield from self.session.get(self.url) aioresponses/core.py:328: in _request_mock response = await self.match(method, url, **kwargs) aioresponses/core.py:304: in match response = await matcher.build_response(url, **kwargs) aioresponses/core.py:184: in build_response reason=result.reason) aioresponses/core.py:157: in _build_response resp.content = stream_reader_factory() aioresponses/compat.py:26: in stream_reader_factory protocol = ResponseHandler(loop=loop) ../aiohttp/aiohttp/client_proto.py:41: in __init__ self.closed = self._loop.create_future() # type: asyncio.Future[None] E AttributeError: 'NoneType' object has no attribute 'create_future' Ref: aio-libs/aiohttp#3733
What do these changes do?
Make
BaseConnector.close()an asynchronous function (stillBaseConnector._close()is synchronous and preserves the old behaviour).Now,
await connector.close()waits until all the connections are closed instead of just sending the closing signal to all the connections. The "Unclosed connection" warnings appeared because some connections required more time to be closed because some protocols (TLS) perform additional steps on connection-close and thus require longer time.Are there changes in behavior for the user?
Yes:
BaseConnector.close()->await BaseConnector.close()with Connector():syntax is droppedRelated issue number
#3736 #1925.
This PR removes functionality deprecated in PR #3417.
Checklist
CONTRIBUTORS.txtCHANGESfolder<issue_id>.<type>for example (588.bugfix)issue_idchange it to the pr id after creating the pr.feature: Signifying a new feature..bugfix: Signifying a bug fix..doc: Signifying a documentation improvement..removal: Signifying a deprecation or removal of public API..misc: A ticket has been closed, but it is not of interest to users.