Open rmawatson opened 4 years ago
I assume this is yet another strange issue with asyncio’s stream.close() - it schedules the closing of the socket but it’s not guaranteed to be done once client.aclose finishes, which means the socket is actually deleted upon garbage collection, and by then I suppose the event loop is already closed... The sleep() call fixed this because it allows asyncio to execute the close callback before proceeding to shutdown the async environment.
See also #825 for more discussion on a possibly related issue.
Have you tried running this on Python 3.8.2?
Also: May well be specific to Window's Proactor event loop.
Yes, definitely looks like a Windows specific issue to me - the linked issues are for Windows too.
@florimondmanca , yes upgraded to 3.8.2 to check, the result is the same.
Can confirm this is an issue with the latest PyPi version of aiohttp and Python 3.8.2 on Windows.
Okay, thank you.
I personally don’t have a Windows machine so help with investigation and possible fixes would help.
Would we need to consider black magic such as https://github.com/aio-libs/aiohttp/issues/1925#issuecomment-592596034? How about https://github.com/aio-libs/aiohttp/pull/3733?
FYI this is likely to be something to address on the HTTPCore side, as connection management and networking is done there. Might then be helpful to first come up with a minimal repro example using HTTPCore only?
It'd also be helpful to try switching to using SelectorEventLoop
and let us know what the behaviour is like then... https://docs.python.org/3/library/asyncio-eventloop.html#event-loop-implementations (Or to try with Python 3.7 or earlier)
Python only switched the default event loop to proactor from 3.8 onwards. https://docs.python.org/3/library/asyncio-policy.html#asyncio.DefaultEventLoopPolicy
not sure there's a link and can't remember why now but in uvicorn we force the SelectorEventLoop to be the default for windows
Le ven. 1 mai 2020 à 10:23 AM, Tom Christie notifications@github.com a écrit :
It'd also be helpful to try switching to using SelectorEventLoop and let us know what the behaviour is like then... https://docs.python.org/3/library/asyncio-eventloop.html#event-loop-implementations (Or to try with Python 3.7 or earlier)
Python only switched the default event loop to proactor from 3.8 onwards. https://docs.python.org/3/library/asyncio-policy.html#asyncio.DefaultEventLoopPolicy
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/encode/httpx/issues/914#issuecomment-622297443, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAINSPTJI3OZEDU6VKCF2KLRPKBHTANCNFSM4MVRF2GQ .
The workaround is to use the selector event loop like this:
import sys
import asyncio
if sys.version_info[0] == 3 and sys.version_info[1] >= 8 and sys.platform.startswith('win'):
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
The following from the documentation will not resolve this issue (for some reason):
import asyncio
import selectors
selector = selectors.SelectSelector()
loop = asyncio.SelectorEventLoop(selector)
asyncio.set_event_loop(loop)
Thanks @Baysul.
This bug report looks like it is tracking the same issue.
Same as with #825 - 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.
Can anyone on Windows, running Python 3.8+ confirm what currently happens when running this?...
import httpx
import asyncio
async def main():
async with httpx.AsyncClient() as client:
r = await client.get("https://login.microsoftonline.com/")
print(r)
asyncio.run(main(), debug=True)
Trying to verify if we're still exposed to this issue. (I guess we are but 🤷♂️)
Using:
I have a httpx._exceptions.ConnectTimeout
even with timeout=60
or http2=True
.
@dalf - Thanks!
Also uh... that's not at all what I was expecting / looking for. 🙃🙃🙃
Does that replicate on other URLs?...
"https://www.google.com/"
"https://www.example.org/"
Does removing the debug=True
change anything?
Does it work okay in the sync version?...
import httpx
def main():
with httpx.Client() as client:
r = client.get("https://login.microsoftonline.com/")
print(r)
main()
Or with trio?... (Use pip install trio
)
import httpx
import trio
async def main():
async with httpx.AsyncClient() as client:
r = await client.get("https://login.microsoftonline.com/")
print(r)
trio.run(main)
Maybe my report is irrelevant, I'm not familiar with the Python Windows environment:
import httpx
import requests
r_requests = requests.get('https://www.google.com')
r_httpx = httpx.get('https://www.google.com')
raises ConnectTimeout: _ssl.c:1106: The handshake operation timed out
🤨
That's pretty relevant. But just super surprising.
Are you able to double double check you've got fully up to date httpx and httpcore versions installed? Is anyone else able to confirm?
To do the test, I've installed Python from https://www.python.org/ftp/python/3.8.5/python-3.8.5.exe (it wasn't installed before), and just pip install httpx[http2]
pip freeze shows
httpcore==0.10.2
httpx==0.14.1
I think a test from someone else would be helpful.
I ran some tests:
https://github.com/
import httpx
import requests
resp = httpx.get('https://github.com/')
print('httpx', resp)
resp = requests.get('https://github.com/')
print('requests', resp)
results:
httpx <Response [200 OK]>
requests <Response [200]>
asyncio
,import httpx
import asyncio
async def main():
async with httpx.AsyncClient() as client:
r = await client.get("https://github.com/")
print(r)
asyncio.run(main(), debug=True)
results:
<Response [200 OK]>
Exception ignored in: <function _ProactorBasePipeTransport.__del__ at 0x0000024774DEA0D0>
Traceback (most recent call last):
File "D:\Programs\Python38\lib\asyncio\proactor_events.py", line 116, in __del__
self.close()
File "D:\Programs\Python38\lib\asyncio\proactor_events.py", line 108, in close
self._loop.call_soon(self._call_connection_lost, None)
File "D:\Programs\Python38\lib\asyncio\base_events.py", line 719, in call_soon
self._check_closed()
File "D:\Programs\Python38\lib\asyncio\base_events.py", line 508, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
trio
, works fine:import httpx
import trio
async def main():
async with httpx.AsyncClient() as client:
r = await client.get("https://github.com/")
print(r)
trio.run(main)
results:
<Response [200 OK]>
https://login.microsoftonline.com/
import httpx
import requests
resp = httpx.get('https://login.microsoftonline.com/')
print('httpx', resp)
resp = requests.get('https://login.microsoftonline.com/')
print('requests', resp)
results:
asyncio
import httpx
import asyncio
async def main():
async with httpx.AsyncClient() as client:
r = await client.get("https://login.microsoftonline.com/")
print(r)
asyncio.run(main(), debug=True)
results in three different types error(in an abritary way):
Using:
I used to have this issue too. But surprisingly, this issue no longer occurs in any of my projects. I have tested it few times on Python 3.8.6
and Python 3.9.1
. I think this issue can be closed for now.
Versions:
httpx==0.16.1
- certifi [required: Any, installed: 2020.12.5]
- httpcore [required: ==0.12.*, installed: 0.12.3]
- h11 [required: ==0.*, installed: 0.12.0]
- sniffio [required: ==1.*, installed: 1.2.0]
- rfc3986 [required: >=1.3,<2, installed: 1.4.0]
- sniffio [required: Any, installed: 1.2.0]
Edit: Os version
Version Windows 10 Pro
Version 20H2
Compilation 19042.746
Features Windows Feature Experience Pack 120.2212.551.0
It might be related to some recent improvements in HTTPCore (especially some related to socket management).
Is anyone else able to confirm whether this seems resolved using the versions above?
It might be related to some recent improvements in HTTPCore
I have ran some tests.
Error raises on these versions of HTTPCore: 0.12.0
, 0.12.1
and 0.12.2
I can do a pull request with fixed requirements.
Coolio. The likely series of events is that prior to 0.12.2, we were scheduling the closing of sockets on the event loop but not awaiting, which might have caused closing to happen after event loop closure on Windows for some reason. On 0.12.3 we are now properly waiting for sockets to close whenever possible.
Thanks for the PR proposition but I guess we can just have existing users upgrade rather than update our pin and issue a new release of HTTPX.
While certainly not a fix, this has worked for me in many cases:
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.run_until_complete(asyncio.sleep(1))
loop.close()
@WoLpH 👋 Are you able to confirm if HTTPCore 0.12.3 makes the issue go away if you remove that sleep(1) workaround?
Yes, it solves the issue :)
I still think the workaround can be useful for people though. There are many scenarios where this is an issue from my experience
Nice. I'll close this off now then. Thanks all!
Also: May well be specific to Window's Proactor event loop.
I am experiencing this as well for anyone reading this in posterity, Windows 10 getting the event loop already closed error. The code terminates normally on Linux.
All -
I am seeing the same issue on Windows. The time.sleep(1)
has fixed the problem thank you @rmawatson - this has been driving me crazy.
When I tried workarounds such as the one here, what happened was that at the end of the loop, Python exited. And that was it. Even when I have code after the loop, it didn't care about it and just terminated everything. Unfortunately, the time.sleep(10) trick didn't work, and neither did this solution.
Are there any other workarounds? Thank you.
@Mennaruuk (or anyone else seeing this) - Let's take this from the start.
httpx
and httpcore
do you have installed?Dear Tom,
Here's the simplest script that I could reduce the original script to while still allowing for the error to be reproduced.
Sample command to run:
script.py -u jack -from 20170101 -to 20170102
From trial and error, I learned the following:
The problem seems to involve argparse. In the script above, there are three arguments (-u
, -from
, and -to
). When I remove -from
and -to
and keep just -u
, the script works. In short, multiple arguments in argparse lead to the script failing. Example script.
Sample command to run:
script.py -u jack -from 20170101 -to 20170102
There is one exception to the prior sentence. You see, I have at the end of the original script some if/elif/else
statements. If I keep the elif
, the script fails. But if I remove the elif
part, the script works. Example script.
Sample command to run:
script.py -u jack
Script suddenly terminates after the async process is done. Despite the presence of input functions right after the async function, Python terminates before the input functions are given a chance to run. See screenshot below for the input functions code following the async code.
Python: 3.10.0 httpx: 0.22.0 httpcore: 0.14.7
If you need any more information, please let me know. I'm happy to provide you with any information to the best of my ability. Here's the not-so-simple script that I initially worked from if it's of help.
Windows 10 Python 3.10.2 NOTE: Problem occured before and after i installed httpx and httpcore httpcore 0.14.7 httpx 0.22.0
Code gets the required data from BSCSCAN and then i get the Runtime Error. The time.sleep(1) command didnt work.
PYTHON import asyncio from bscscan import BscScan
YOUR_API_KEY = "REDACTED" CON_ADDR = "0xa38898a4ae982cb0131104a6746f77fa0da57aaa" WAL_ADDR = "0x411709fd0cdd5f06ff6ef45b48aa0cb9842f01fc"
async def main(): async with BscScan(YOUR_API_KEY) as bsc: print(await bsc.get_bnb_balance(address="0xa38898a4ae982cb0131104a6746f77fa0da57aaa")
if name == "main": asyncio.run(main())
OUTPUT
I'm having the same issue on Windows 10. It does not matter what library I'm using. If the loop is an ProactorEventLoop, then it is guaranteed that it will cause an exception if there have been any network requests made which use SSL or sessions.
Okay - could you show me that absolute simplest possible example that reproduces this.
@pprovart's example here is pretty good - does it still replicate if you simplify it even further?...
import asyncio
import httpx
async def main():
async with httpx.Client() as client:
r = await client.get("https://www.example.com")
print(r)
if name == "main":
asyncio.run(main())
Also, does it replicate with both http
and https
URLs?
Nope, it works pretty well for me. I get <Response [301 Moved Permanently]>
with and without HTTPS. I looked at this proposed solution, and I modified it as the following. It worked for me, not sure if it works for others. but I stopped getting the RuntimeError:
import platform
if platform.system() == 'Windows':
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
@Mennaruuk - Ah yup. Just looked though this thread and reminded myself of the comment here.
This is a bug in Python's stdlib with Windows. See: https://bugs.python.org/issue39232
One option here might be to check if the ProactorEventLoop is the default, and warn if it is.
when using async with httpx.AsyncClient() as client
the transport should be closed correctly by anyio and not trigger the __del__
so this looks like an anyio bug
@pprovart is using aiohttp and not httpx https://github.com/pcko1/bscscan-python/blob/fcfc30cb7d1a4ff2a4f2de30606f35698675cbc7/bscscan/core/async_client.py#L32
This bug in the Python stdlib
appears to have been fixed now in 3.11 (and possibly backported to 3.10?)
https://github.com/python/cpython/issues/83413
Are any windows users here able to attempt replicating the original issue on the latest patch versions of Python 3.9, 3.10 and 3.12?
This bug in the Python
stdlib
appears to have been fixed now in 3.11 (and possibly backported to 3.10?)Are any windows users here able to attempt replicating the original issue on the latest patch versions of Python 3.9, 3.10 and 3.12?
I can test later today.
This problem also occurs on linux.
Exception ignored in: <function BaseSubprocessTransport.__del__ at 0x7f85c758ce50>
Traceback (most recent call last):
File "/usr/lib/python3.8/asyncio/base_subprocess.py", line 126, in __del__
self.close()
File "/usr/lib/python3.8/asyncio/base_subprocess.py", line 104, in close
proto.pipe.close()
File "/usr/lib/python3.8/asyncio/unix_events.py", line 536, in close
self._close(None)
File "/usr/lib/python3.8/asyncio/unix_events.py", line 560, in _close
self._loop.call_soon(self._call_connection_lost, exc)
File "/usr/lib/python3.8/asyncio/base_events.py", line 711, in call_soon
self._check_closed()
File "/usr/lib/python3.8/asyncio/base_events.py", line 504, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
This problem also occurs on linux.
Have you considered switching to recent version of python?
Checklist
master
.Describe the bug
I get the following traceback when running the attached code
To reproduce
Inserting
time.sleep(0.1)
at the end ofmain()
fixes it. Possibly due to the same reasons in the issues linked below.Environment
Possibly the same as this and this