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
Gevent + fork + google.cloud.storage + urllib3/requests gives "'module' object has no attribute 'epoll'" #1268
Comments
@maingoh, can you test with an application design that imports and calls the gevent monkey patch method before importing anything else? It looks like we're enumerating the selector options in |
If I do : gipc.start_process(start_process)
cloud_storage = CloudStorage("bucket-test")
cloud_storage.upload("test") => It works But then If I fork again after, then it fails. I just figured out that since my process is forked, then all modules are also imported in the child .. Also If I monkey patch directly in the parent, at the beginning it works, but I don't want the parent to be monkey patched. The problem is that I am making tests and need to fork between each tests. Would deleting all sys.modules in |
It seem to fail only with https (replace the url by |
I managed to make it working by creating my child process using It would be better If the first method was working though. Not sure if you can do something about it. |
Yeah, this is tricky. We've run into this before with gevent patching; essentially the selector environment we end up working with can be different than the selector environment we initialize in. Generally, we've recommended ensuring that importing urllib3 (including through a module that uses us) happens after any monkeypatching. Pinging @SethMichaelLarson for his thoughts, since he's worked a lot in that area of the project. Is this something we can or should change? I'm wondering if we can guard this line with a try/except looking for an AttributeError, and then re-run the logic to find a default selector. Might look like this? def DefaultSelector():
""" This function serves as a first call for DefaultSelector to
detect if the select module is being monkey-patched incorrectly
by eventlet, greenlet, and preserve proper behavior. """
global _DEFAULT_SELECTOR
if _DEFAULT_SELECTOR is not None:
try:
return _DEFAULT_SELECTOR()
except AttributeError:
_DEFAULT_SELECTOR = None
if _can_allocate('kqueue'):
_DEFAULT_SELECTOR = KqueueSelector
elif _can_allocate('epoll'):
_DEFAULT_SELECTOR = EpollSelector
elif _can_allocate('poll'):
_DEFAULT_SELECTOR = PollSelector
elif hasattr(select, 'select'):
_DEFAULT_SELECTOR = SelectSelector
else: # Platform-specific: AppEngine
raise ValueError('Platform does not have a selector')
return _DEFAULT_SELECTOR() |
I personally don't have an issue with that change. If I'm understanding correctly (haven't had time to actually process everything in this thread) the part that's tripping up our current logic is the fact that it's being forked? Because the original reason we moved to this logic was so users could run: import urllib3
from gevent import monkey
monkey.patch_all()
# Use urllib3 here... and the world wouldn't end. Is it the forking what is causing this issue or something else? |
@SethMichaelLarson, I'm not sure if the issue is related to the fork or not - I think that's a red herring. It looks like it's that we're making a call (in |
Hrmm, I suppose that since we're declaring our wrapper classes conditionally, the above could run into issues if the monkey-patched |
I think that we're trying to do too much if we try to solve that issue as well. Either monkey patch or don't. Monkey-patching away features of a module in the middle of a script is bound to break more things than urllib3 eventually. I would recommend moving the monkey-patch to the beginning of the script. ie: I don't agree that urllib3 should account for what amounts to this: import select
import urllib3
# Use urllib3
delattr(select, 'epoll')
# Use urllib3 |
So just to explain a little more what I was trying to do. I am making a worker which I want to be monkey patched for network concurrency. But the main app (which can be anything) is not necessarily gevent friendly, so I don't want my worker to break the current app. |
It looks like it may be a gevent feature to add more than something to hack in urllib3. |
If you want to go down this route you can try doing this: # Stuff before monkey patching...
monkey.patch_all()
import urllib3.util.selectors
urllib3.util.selectors._DEFAULT_SELECTOR = None This should reset urllib3s memory of what our default selector is by the time we get there and cause a retrial of all selectors post-patch in the forked process. I haven't tested this code so use at your own risk. |
Just tried and it works ! I will stick with subprocess for now as it is safer ;) |
Okay; I'm going to close this out, as I think we can agree that no action ought to be taken as a result of this issue. |
Hello,
I am using the latest version of requests (and urllib3 1.22). I need to fork and monkey patch the child process (the parent has to stay unpatched). I also need to instantiate a google cloud storage client before forking and this gives me this traceback when downloading a page in the child:
The smallest code I can give you is :
I have tried :
patch_all(select=False)
it works but without concurrencyrequests.get("https://google.fr")
instead of creating a google_storage & uploading in the parent works. Note that if I only instantiate the google storage client, without uploading, it works.For now I will go with the version 1.19.1 but it seems that something has been broken since ? Or is it in the requests module ?
Thank you very much,
The text was updated successfully, but these errors were encountered: