Skip to content
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 jupyter_client's AsyncKernelManager #191

Merged
merged 37 commits into from
Apr 4, 2020

Conversation

davidbrochart
Copy link
Contributor

No description provided.

Copy link
Member

@kevin-bates kevin-bates left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for adding this in @davidbrochart - this will be extremely useful to have prior to moving to kernel providers. This also appears to be a general gen.coroutine/yield -> async/await sweep - or at least a start - which is really nice!

I only had a few minor comments.

jupyter_server/services/kernels/kernelmanager.py Outdated Show resolved Hide resolved
)
# py2-compat
raise gen.Return(kernel_id)
kernel_id = await ensure_async(self.kernel_manager.start_kernel(path=kernel_path, kernel_name=kernel_name))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need the ensure_async() wrapper here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, and this involves some changes in the tests too.

setup.py Outdated Show resolved Hide resolved
@Zsailer
Copy link
Member

Zsailer commented Mar 16, 2020

👍 Thanks, @davidbrochart.

The failing AppVeyor test is due to a Github webhook that needed to be deleted. I just removed it, so it should drop from the test list.

@davidbrochart
Copy link
Contributor Author

Not sure about these errors on Windows for python3.5 and 3.6.

@davidbrochart
Copy link
Contributor Author

Trigger CI.

@davidbrochart davidbrochart reopened this Mar 16, 2020
@davidbrochart
Copy link
Contributor Author

Now the error is only on Windows/python3.5.

Copy link
Member

@kevin-bates kevin-bates left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks David!

I recall seeing the Windows issue when working with kernel providers. Unfortunately, I don't recall the solution. I was hoping this issue would provide some hints, but IIRC this was following my encounter with the invalid handle issue. I'll google around and see if that rings any bells.

@kevin-bates
Copy link
Member

Hmm - Windows 3.5 is sounding more familiar and I definitely recall this google hit: https://bugs.python.org/issue37380. Still trying to remove the cobwebs.

@echarles
Copy link
Member

Thx @davidbrochart

@kevin-bates When the kernel provider will be merged, my understanding is that this async kernel-client implementation will be completely replaced by the kernel-protocol code. Is this correct?

@kevin-bates
Copy link
Member

@echarles - yes, that's correct. We'll want to get that client implementation up on async. I'm fairly certain @davidbrochart is aware of that: jupyter/jupyter_client#428 (comment)

@davidbrochart
Copy link
Contributor Author

Hmm - Windows 3.5 is sounding more familiar and I definitely recall this google hit: https://bugs.python.org/issue37380. Still trying to remove the cobwebs.

This is not going to be fixed for python3.5 and 3.6 on Windows, and I don't have any workaround.

@davidbrochart
Copy link
Contributor Author

@echarles - yes, that's correct. We'll want to get that client implementation up on async. I'm fairly certain @davidbrochart is aware of that: jupyter/jupyter_client#428 (comment)

I don't think there is anything to do in jupyter_kernel_mgmt, it already has an async client (it's IOLoopKernelClient).

@kevin-bates
Copy link
Member

By golly - I didn't realize that. I knew it required changes to interact with the async kernel manager, but I never looked closely at the pure 'client' portion. If you say its good to go, then I'm good.

Looking just now, it doesn't appear to have a mixture of async/await and gen.coroutine/yield and it seems like we should convert the latter at some point.

@Zsailer
Copy link
Member

Zsailer commented Mar 17, 2020

This is not going to be fixed for python3.5 and 3.6 on Windows, and I don't have any workaround.

So what are our options here?

  1. Require Python 3.6>= on Windows and keep 3.5 support on other platforms.
  2. Or bump jupyter_server to only support Python 3.6 on all platforms.

Is that right?

And if we choose the former, we should probably add a table to the README with "supported" Python versions+platforms.

@davidbrochart
Copy link
Contributor Author

We even need python>=3.7 for Windows. I would go for option 1.

Comment on lines 386 to 397
def ensure_async(obj):
"""Convert a non-async object to a coroutine if needed.
"""
if inspect.isawaitable(obj):
return asyncio.ensure_future(obj)
elif isinstance(obj, concurrent.futures.Future):
return asyncio.wrap_future(obj)
else:
# not awaitable, wrap scalar in future
f = asyncio.Future()
f.set_result(obj)
return f
if asyncio.iscoroutine(obj):
return obj
if type(obj) is asyncio.Future:
return obj

async def _():
return obj

return _()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

It might be worth one more set of eyes to specifically approve this change (maybe @kevin-bates).

It LGTM, but I'm not super familiar with the async stuff. I want to make sure we're not missing any key edge cases that might lead to cryptic error messaging or bugs. Should we create a set of new tests specifically for this function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, async is not easy, so any help would be appreciated here, including ways to test this function.

@@ -1039,7 +1039,7 @@ def template_file_path(self):
)

kernel_manager_class = Type(
default_value=MappingKernelManager,
default_value=AsyncMappingKernelManager,
Copy link
Member

@Zsailer Zsailer Mar 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this changes the default manager class name, I think we need to add logic that addresses user config. If any traits of MappingKernelManager are configurable, some users may have config that will no longer be loaded by this new class, and thus, silently ignore their settings.

I see two ways to address this:

  1. If those traits are still valid for this new class, we can write an empty configurable MappingKernelManager class that AsyncMappingKernelManager inherits and thus loads the traits as a subclass.
  2. Throw warnings every time a MappingKernelManager trait pops up in the config.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing that out @Zsailer. I think option 1 is the best.

@Zsailer
Copy link
Member

Zsailer commented Mar 17, 2020

We even need python>=3.7 for Windows. I would go for option 1.

Why are the 3.6 Windows tests passing, but not the 3.5?

@kevin-bates
Copy link
Member

What I'm not understanding is why JKM is working on WIndows 3.5, 3.6? It's generally the same code since we can't use the async subprocess stuff on Windows there either.

@davidbrochart
Copy link
Contributor Author

The tests randomly pass on Windows/python3.6, and they seem to always fail with python3.5. This issue says that the fix has been back-ported to python 3.7 only, not beyond.
Since the behavior is random, it might explain why we don't see it in JKM, but I cannot be sure.

@kevin-bates
Copy link
Member

I believe this is ready to merge. I can't recall what option we should use. Deferring to @Zsailer.

@Zsailer
Copy link
Member

Zsailer commented Apr 1, 2020

I think the history of this PR is useful to keep around—so I'm going to merge directly (not squash-and-merge). This was a lot of work that's work tracking. Thanks @kevin-bates and @davidbrochart! Great work, here!

@kevin-bates
Copy link
Member

I hate to bring this up, but in working on the NB 4479 PR, I'm finding the checks for Python 3.5 should be sys.version_info < (3, 6) rather than sys.version_info <= (3, 5). I'm running on 3.5.5 and it's not detecting "3.5" correctly.

@davidbrochart
Copy link
Contributor Author

Good catch Kevin, thanks!
Zach, I think you should squash, we can always refer to this PR even though it's closed (and it has all the useful narrative too).

Copy link
Member

@kevin-bates kevin-bates left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks David. I think we're getting very close here.



class GatewayKernelManager(MappingKernelManager):
class GatewayKernelManager(AsyncMappingKernelManager):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be changed back to MappingKernelManager, otherwise we've disabled the use of a gateway on 3.5. As mentioned in the previous comment, the superclass is not critical since GatewayKernelManager serves as a proxy.

tests/test_gateway.py Outdated Show resolved Hide resolved
tests/services/kernels/test_api.py Outdated Show resolved Hide resolved
tests/services/kernels/test_api.py Show resolved Hide resolved
@davidbrochart
Copy link
Contributor Author

davidbrochart commented Apr 2, 2020

Thanks again Kevin!
I'm just adding back the removal of kernelmanager.AsyncMappingKernelManager in the test to check, I remember that it was needed but I don't know why.

@kevin-bates
Copy link
Member

Hmm. That condition is impossible, by definition, so how can it be considered valid for the test? I think it might be good to understand what's going on. The 3.5 no-async-kernel-manager test is failing because the trailet for the kernel_manager_class can't be resolved...

E               traitlets.traitlets.TraitError: The 'kernel_manager_class' trait of <jupyter_server.serverapp.ServerApp object at 0x7f997c5b7b70> instance must be a type, but 'jupyter_server.services.kernels.kernelmanager.AsyncMappingKernelManager' could not be imported

and this is impossible in the real world because AsyncMappingKernelManager will always exist.

@davidbrochart
Copy link
Contributor Author

I need to take a break and look more closely.

@@ -284,7 +285,7 @@ def no_async_mapping_kernel_manager(monkeypatch):

@pytest.mark.skipif(
sys.version_info < (3, 6),
reason="Kernel manager is AsyncMappingKernelManager, Python version < 3.6"
reason="Testing no AsyncMappingKernelManager on Python >=3.6
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need trailing quotes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Member

@kevin-bates kevin-bates left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great David - thank you!

@kevin-bates kevin-bates merged commit d1cb381 into jupyter-server:master Apr 4, 2020
@davidbrochart davidbrochart deleted the AsyncKernelManager branch April 4, 2020 06:27
@Zsailer
Copy link
Member

Zsailer commented Apr 4, 2020

Congrats, y'all! Great stuff!

Zsailer pushed a commit to Zsailer/jupyter_server that referenced this pull request Nov 18, 2022
hMED22 pushed a commit to hMED22/jupyter_server that referenced this pull request Jan 23, 2023
* Use jupyter_client's AsyncKernelManager

* Rename MappingKernelManage to AsyncMappingKernelManage, convert gen.coroutine/yield to async/await, remove run_blocking

* Fix Windows subprocess handle issue

* Restrict Windows to python>=3.7

* Fix GH actions matrix exclusion

* Make AsyncMappingKernelManager a subclass of MappingKernelManager for configuration back-compatibility

* Make AsyncKernelManager an opt-in

* Pin jupyter_core and jupyter_client a bit higher

* Remove async from MappingKernelManager.shutdown_kernel

* Hard-code super() in MappingKernelManager and AsyncMappingKernelManager

* Add argv fixture to enable MappingKernelManager and AsyncMappingKernelManager

* Rewrite ensure_async to not await already awaited coroutines

* Add async shutdown_kernel to AsyncMappingKernelManager, keep MappingKernelManager.shutdown_kernel blocking

* Add restart kwarg to shutdown_kernel

* Add log message when starting (async) kernel manager

* Bump jupyter_client 6.1.1

* Rename super attribute to pinned_superclass

* Prevent using AsyncMappingKernelManager on python<=3.5 (at run-time and in tests)

* Ignore last_activity and execution_state when comparing sessions

* Replace newsession with new_session

* Fix Python version check

* Fix skipping of tests

* GatewayKernelManager inherits from MappingKernelManager to keep python3.5 compatibility

* Added back removal of kernelmanager.AsyncMappingKernelManager

* Don't test absence of AsyncMultiKernelManager
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants