You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ResourceWarning: unclosed transport <_SelectorSocketTransport fd=6> (asyncio only) #825
$ ipython
Python 3.8.0 (default, Dec 12 2019, 09:56:28)
Type 'copyright', 'credits' or 'license'for more information
IPython 7.12.0 -- An enhanced Interactive Python. Type '?'for help.
In [1]: import httpx
In [2]: async with httpx.AsyncClient() as client:
...: r = await client.get('https://www.example.org/')
In [3]: r
Out[3]: <Response [200 OK]>
In [4]:
Do you really want to exit ([y]/n)?
An open stream object is being garbage collected; call "stream.close()" explicitly.
source_traceback: Object created at (most recent call last):
[...]
File "/home/mdk/.local/lib/python3.8/asyncio/base_events.py", line 595, in run_until_complete
self.run_forever()
File "/home/mdk/.local/lib/python3.8/asyncio/base_events.py", line 563, in run_forever
self._run_once()
File "/home/mdk/.local/lib/python3.8/asyncio/base_events.py", line 1836, in _run_once
handle._run()
File "/home/mdk/.local/lib/python3.8/asyncio/events.py", line 81, in _run
self._context.run(self._callback, *self._args)
File "/home/mdk/.local/lib/python3.8/asyncio/streams.py", line 50, in open_connection
reader = StreamReader(limit=limit, loop=loop)
$
So this is related to #634, _backends/asyncio.py, SocketStream's close method:
async def close(self) -> None:
# NOTE: StreamWriter instances expose a '.wait_closed()' coroutine function,
# but using it has caused compatibility issues with certain sites in
# the past (see https://github.com/encode/httpx/issues/634), which is
# why we don't call it here.
# This is fine, though, because '.close()' schedules the actual closing of the
# stream, meaning that at best it will happen during the next event loop
# iteration, and at worst asyncio will take care of it on program exit.
async with self.write_lock:
with as_network_error(OSError):
self.stream_writer.close()
Obviously adding a await self.stream_writer.wait_closed() fixes #825 probably while reopening #634
@JulienPalard I'm curious — what happens if you await asyncio.sleep(0) at the end of main()? Does this give asyncio the chance to execute the callback that closes the stream?
Checks done using httpx on current HEAD (50d337e), ipython==7.12.0, on Debian bullseye, trio==0.13.0:
Sorry I don't know how to design 3d tables in markdown. So, trust_env has no effect, (I tried True and False for each tests) cpython 3.8.1 fixed an issue that did not reappeared in 3.8.2.
Python 3.8.2
ipython, trust_env=*: No issue
script asyncio, trust_env=*: ResourceWarning
script trio, trust_env=*: No issue
Python 3.8.1
ipython, trust_env=*: No issue
script asyncio, trust_env=*: ResourceWarning
script trio, trust_env=*: No issue
Python 3.8.0
ipython, trust_env=*: An open stream object is being garbage collected
script asyncio, trust_env=*: ResourceWarning
script trio, trust_env=*: No issue
[1]:
importasyncio# or import trioimporthttpxasyncdefmdk():
asyncwithhttpx.AsyncClient(trust_env=False) asclient:
r=awaitclient.get('https://www.example.org/')
print(r.text)
asyncio.run(mdk()) # or trio.run(mdk)
changed the title [-]An open stream object is being garbage collected; call "stream.close()" explicitly.[/-][+]ResourceWarning: unclosed transport <_SelectorSocketTransport fd=6>[/+]on Feb 26, 2020
changed the title [-]ResourceWarning: unclosed transport <_SelectorSocketTransport fd=6>[/-][+]ResourceWarning: unclosed transport <_SelectorSocketTransport fd=6> (asyncio only)[/+]on Feb 26, 2020
Haven't looked into how awkward it is to do, but really I think we'd want to monkeypatch asyncio's socket unwrapping behaviour, so that we can reliably call .wait_closed(...) without it potentially hanging.
# NOTE: StreamWriter instances expose a '.wait_closed()' coroutine function,# but using it has caused compatibility issues with certain sites in# the past (see https://github.com/encode/httpx/issues/634), which is# why we don't call it here.# This is fine, though, because '.aclose()' schedules the actual closing of the# stream, meaning that at best it will happen during the next event loop# iteration, and at worst asyncio will take care of it on program exit.asyncwithself.write_lock:
withmap_exceptions({OSError: CloseError}):
self.stream_writer.close()
Activity
JulienPalard commentedon Feb 26, 2020
Tried with
HTTPX_LOG_LEVE=tracebecause tracemalloc don't tells me much, and Python 3.8.2:JulienPalard commentedon Feb 26, 2020
So this is related to #634, _backends/asyncio.py, SocketStream's close method:
Obviously adding a
await self.stream_writer.wait_closed()fixes #825 probably while reopening #634So I'm going to read #634 now.
florimondmanca commentedon Feb 26, 2020
Hi @JulienPalard,
This is very strange to me. It's a straight-on request/response flow, no redirects, no special cases that surface in the TRACE logs…
Also, I am not able to reproduce on Python 3.8.1/HTTPX 0.11.1.
florimondmanca commentedon Feb 26, 2020
Huhh… I'm sick with issues we've been having with asyncio's
.close()vs.wait_closed()…You can also look at the end of #714. Our conclusion back then was to close off as "this is a Python bug". @tomchristie also noted:
which matches my experience on 3.8.1. The bug may have come back on 3.8.2…? 🤷♂
florimondmanca commentedon Feb 26, 2020
@JulienPalard I'm curious — what happens if you
await asyncio.sleep(0)at the end ofmain()? Does this give asyncio the chance to execute the callback that closes the stream?lovelydinosaur commentedon Feb 26, 2020
Feasible that it could be a duplicate of #824 if there happens to be proxies setup in the environment.
Few questions here @JulienPalard:
async with httpx.AsyncClient(trust_env=False)?example.pyscript, and run withasyncio.run(main()), rather than using a REPL?example.pyscript, and run withtrio.run(main), rather than using a REPL?JulienPalard commentedon Feb 26, 2020
@florimondmanca No, adding
asyncio.sleep(0)don't help butasyncio.sleep(.1)fixes it (probably randomly).JulienPalard commentedon Feb 26, 2020
Checks done using httpx on current HEAD (50d337e), ipython==7.12.0, on Debian bullseye, trio==0.13.0:
Sorry I don't know how to design 3d tables in markdown. So,
trust_envhas no effect, (I tried True and False for each tests) cpython 3.8.1 fixed an issue that did not reappeared in 3.8.2.Python 3.8.2
Python 3.8.1
Python 3.8.0
[1]:
[-]An open stream object is being garbage collected; call "stream.close()" explicitly.[/-][+]ResourceWarning: unclosed transport <_SelectorSocketTransport fd=6>[/+]JulienPalard commentedon Feb 26, 2020
I'm changing the title appropriately, as the "An open stream ..." has been fixed in Python 3.8.1, this discussion is about the ResourceWarning.
lovelydinosaur commentedon Feb 26, 2020
Gotcha, thanks @JulienPalard.
We'd be able to resolve that if we were doing what we're supposed to and calling
.wait_closed(), but we can't because it appears to have an issue where it can hang #634 (comment) which trio is particularly careful to work around https://github.com/python-trio/trio/blob/master/trio/_ssl.py#L782-L785I guess things we ought to figure out are:
asyncio.wait_closed()issue to a simple enough reproducible case that we can constructively raise it as a Python bug?[-]ResourceWarning: unclosed transport <_SelectorSocketTransport fd=6>[/-][+]ResourceWarning: unclosed transport <_SelectorSocketTransport fd=6> (asyncio only)[/+]lovelydinosaur commentedon Feb 26, 2020
Also worth investigating - how are the
aiohttpteam dealing with this, and is there an equivelent issue on their tracker?lovelydinosaur commentedon Feb 26, 2020
Here's a minimal reproducing case...
lovelydinosaur commentedon Feb 26, 2020
Raised https://bugs.python.org/issue39758
lovelydinosaur commentedon May 6, 2020
Haven't looked into how awkward it is to do, but really I think we'd want to monkeypatch asyncio's socket unwrapping behaviour, so that we can reliably call
.wait_closed(...)without it potentially hanging.lovelydinosaur commentedon Aug 20, 2020
Okay, so reviewed this one today, and looks like we can close it off.
Our asyncio backend has this comment...
Also, this does not reproduce any warnings...
lovelydinosaur commentedon Aug 20, 2020
Oh right,
ResourceWarningis ignored by default, but this replicates when using eg.python -WallI think we'll need to look into a fix that allows us to call
.wait_closedthat doesn't ever hang if it can't unwrap the SSL connection cleanly.