-
Notifications
You must be signed in to change notification settings - Fork 2
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
Use of nest_asyncio? #3
Comments
This comment has been minimized.
This comment has been minimized.
I understand that pluggy, the library we use to define/collect/run plugin logic on how to handle custom git puller logic, isn't capable of accepting an async function. This can be traced back to: pytest-dev/pluggy#320 Due to this, the hook specification is an non-async function. The implementation of a hook now looks like this. nbgitpuller-downloader-plugins/src/nbgitpuller_downloader_generic_web/generic_web_downloader.py Lines 12 to 28 in d99c343
The code above schedules an locally defined async function (handle_files_helper) to run in an event loop. nbgitpuller-downloader-plugins/src/nbgitpuller_downloader_plugins_util/plugin_helper.py Lines 271 to 289 in d99c343
Current strategy to avoid blocking issues
Do we need async pluggy?I don't think so. We can block the dedicated thread and can write to a message queue. Plugin implementation now and suggestionFirst let's clarify responsibilities:
It seems like the current approach has two separate strategies to avoid blocking.
My conclusion is that the problem arise from not using the same strategy, and that we should:
Practically I think |
I think I follow you here @consideRatio. I am can take a look at what you suggest. I am about to commit to a solution that I came up with late yesterday that uses aiopluggy instead of pluggy. In this scenario, we are able to have an async hookimpl. I had to write my own code to do plugin discovery because this functionality does not exist in aiopluggy as it did in pluggy. I realized as I was responding to this issue yesterday, that I could solve this problem with aiopluggy. I had tried this solution early on and abandoned it for a bunch of reasons that I now realize the solution to. Anyway. I will commit this, take a look and I will work on what you suggest here. |
@sean-morris while https://github.com/Amsterdam/aiopluggy could have been relevant, I think we should steer clear from using it because the last commit was 4 years ago. An example of an issue of relying on such project is that it may not support modern versions of python or similar which we should in this actively maintained project. I think if you expand the following... def pull():
try:
for line in gp.pull():
q.put_nowait(line)
# Sentinel when we're done
q.put_nowait(None)
except Exception as e:
q.put_nowait(e)
raise e
self.gp_thread = threading.Thread(target=pull)
self.gp_thread.start() To be like... def pull():
try:
+ # TODO: do content provider plugin work here
+ # TODO: let the hook communicate back to this async function via
+ # a progress message queue which is repeatedly polled in this
+ # thread to report back to the UI
for line in gp.pull():
q.put_nowait(line)
# Sentinel when we're done
q.put_nowait(None)
except Exception as e:
q.put_nowait(e)
raise e
self.gp_thread = threading.Thread(target=pull)
self.gp_thread.start() |
@consideRatio @manics Got it. I committed the changes. It is much cleaner. The only issue I see is how to get the returned JSON structure from the hookimpl, handle_files, back to the nbgitpuller call. I hacked it a bit just so we can see it working by setting helper_args["handle_files_output"] as the last line.
Then in nbgitpuller:
A better/cleaner method for getting the results from handle_files may be to set up the downloader plugins as Classes with an instance variable, results, that is set as the handle_file function completes and then accessed via the object reference on the nbgitpuller side. The only issue with this is that we have to write our own plugin discovery method because pluggy's function can't handle automated discovery via entry points if the hookimpl function is in a Class. I wrote a mock-up of what the discovery might take; it is doable but not sure if that is where we want/need to go. |
Refactoring plugin logic executionCurrently we have logic in handlers.py to execute the plugin logic, but which represents when a user visits a /git-pull url. But, in practice we should of course behave the same if you run the handlers.py + pull.py both have logic to use the GitPuller class and its method By doing that, you can also make use of the GitPuller class as an object to store state as well, and you may not need to pass anything back and forth etc. I think it is absolutely critical that we avoid duplicating the implementation logic in two locations, and my suggestion above didn't reflect that. def pull():
try:
- # TODO: do content provider plugin work here
- # TODO: let the hook communicate back to this async function via
- # a progress message queue which is repeatedly polled in this
- # thread to report back to the UI
+ # FIXME: Let the GitPuller object doing work, also do the plugin work
+ # practically leaving this code as it was.
for line in gp.pull():
q.put_nowait(line)
# Sentinel when we're done
q.put_nowait(None)
except Exception as e:
q.put_nowait(e)
raise e
self.gp_thread = threading.Thread(target=pull)
self.gp_thread.start() |
ok. I am following. We are stepping into the middle of it! I will throw this up and we can check it out. |
@consideRatio Ok, I committed the changes to pull everything into pull.py. I have not tried the CLI yet but will. We still end up needing to get the results from the downloader plugin. My suggestion is to either pass a reference to GitPuller class(which I would say is weird) or implement the plugins as classes and write our own discovery. |
Hmmm i dont get this yet.
Can you clarify what information needs to be passed from where to where? |
In the normal git puller where the source is git the name of the repo is in
the link; with an archive you don’t know where you expanded until you
decompress the file. I pass back the name of the folder and the path to the
local git repo which is then handed off to the git pulling functionality as
repo_dir and git_url.
…On Tue, 18 Jan 2022 at 23:57 Erik Sundell ***@***.***> wrote:
Hmmm i dont get this yet.
We still end up needing to get the results from the downloader plugin.
Can you clarify what information needs to be passed from where to where?
—
Reply to this email directly, view it on GitHub
<#3 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AALR344HBPPKC7UTB6ZBIVDUWZVHDANCNFSM5LXHS3ZA>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
This is a dedicated issue for the discussion in jupyterhub/nbgitpuller#194 (comment) surrounding the following code snippet.
Goal
Clarify what made us need a workaround like nest_asyncio in the first place.
I currently don't have a good grasp of where in the codebase we end up erroring without nest_asyncio.apply() called ahead of time.
@sean-morris can you provide some more details on this?
Get a recommendation and go with it
I figure we should ask Min RK on the use of this workaround.
It is my understanding that by using it, we modify asyncio core functionality and my main concern would be if just installing
nbgitpuller
automatically would lead to making a change that breaks other code. Before doing this, I figure we just try to clarify and pinpoint what code, and where, makes us need to need the workaround. I'd like to have it more explicit wherever we declare to use it.If we go with the workaround: decide on location in codebase
We decide on the location. It just need to run once, and it can run from anywhere. So, perhaps a init.py file not related to a specific plugin makes the most sense. I think it can be in ngitpuller also if the most primitive structure that all plugins will need requires it also, and, a plugin has been detected.
Mainly I'd like to avoid having it in nbgitpuller in situations when users don't even use plugins.
The text was updated successfully, but these errors were encountered: