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

Draft proposal for new kernel providers mechanism and associated changes #45

Closed
wants to merge 1 commit into from

Conversation

takluyver
Copy link
Member

@takluyver takluyver commented Dec 4, 2019

As requested on the steering-council mailing list.

I've restructured what I'm saying to make it clearer that moving the two repositories into the Jupyter org is only one part of this.

Votes for approval:

@SylvainCorlay
Copy link
Member

Thanks for opening this.

@betatim
Copy link
Member

betatim commented Dec 5, 2019

I like the idea. In particular because the "putting environment name in the kernel name" pattern is getting more and more use. I think the notebook extensions that offer this are used by people who want an easy way of switching between environments. The problem occurs when they then send their notebook to someone else. Often it takes a long time (if they haven't run into this problem before) for both sides to figure out that this is what happened.

One question: using entrypoints as the discovery mechanism is cool. How would this work if neither your frontend nor your notebook server are written in Python? I am not sure this is a problem right now but I could imagine a world where a UI is written in JS, running a JS kernel and no Python is ever involved (or replace JS with some other cool new language not yet invented). Is there a nice way for them to also use this mechanism?

@minrk
Copy link
Member

minrk commented Dec 5, 2019

@betatim this is effectively a Python-Jupyter-client feature for new kernel discovery methods/dynamic kernel creation (i.e. env-kernels, remote kernels, etc.). The existing 'kernelspec-file' mechanism that is easily shared across implementations continues to exist and be shared by all frontends, and is one such discovery mechanism.

I'd love to have a comment from nteract folks like @mpacer or @rgbkrk on whether adding features like this that are Python-frontend-specific is seen as an issue from their side. One mitigation could be a Python CLI for listing/launching available kernels.

@fperez
Copy link
Member

fperez commented Dec 6, 2019

Should we also broadcast this a bit wider to see what reactions there are from the Julia and R spaces? Which other ecosystems do we know of that have both active Jupyter-using communities and enough "environment management" tools and traditions for this complexity to arise? If there are any, perhaps we can try to ping their channels for input?

@SylvainCorlay SylvainCorlay self-requested a review December 7, 2019 22:31
@SylvainCorlay
Copy link
Member

I approve this JEP. We were already looking at Thomas' jupyter_kernel_mgmt and jupyter_protocol before all of this because they correspond to what we needed for Voilà. I would be really happy to see this move to the Jupyter organization.

@takluyver
Copy link
Member Author

using entrypoints as the discovery mechanism is cool. How would this work if neither your frontend nor your notebook server are written in Python?

It does essentially tie part of the ecosystem to Python. Of course, it would be possible to have interfaces (command line, HTTP, ZMQ, etc.) which would allow non-Python code to use this mechanism. Or, as Min mentions, you could reimplement particular kernel discovery mechanisms in another language (or hardcode a way to launch a specific kind of kernel). But to fully support this kernel discovery framework, you would have to have some Python code running.

I would argue that it's easier to define language-agnostic interfaces for discovering & launching kernels around the Python code in jupyter_kernel_mgmt than to define the extension mechanism itself using only language-agnostic interfaces. Put another way: applications that want to discover & launch kernels have to call some Python code, but the alternative approach would mean that they must be prepared to call code in multiple arbitrary languages, probably including Python.

@MSeal
Copy link
Contributor

MSeal commented Dec 17, 2019

Would the intent for kernels with the same language but differing kernel implementations be that you give the notebook a different language name in the metadata? e.g. with my kernel for environment X can only run in environment X but in X there's multiple extensions of a base language kernel -- should I name my kernel language python_variant_A to avoid trying to run it with a same-language kernel that won't work? I was little confused how this would interface with kernel-ids vs kernel names, especially since the specialization can't run in other variants successfully but the kernel-id wouldn't be in the metadata to help differentiate (unless I misunderstood the JEP). As a specific example, we have some PySpark specific kernels for a few versions of Spark that behave this way where it's got a lot more than parameterization of a python kernel going on, but all the user code passed is python code so it's language is technically python. Another I've seen elsewhere was a GPU PyTorch kernel alongside the normal python kernel on the same jupyter box but with different backing environments.

Thoughts?

@MSeal
Copy link
Contributor

MSeal commented Dec 17, 2019

It does essentially tie part of the ecosystem to Python.

I think entrypoint registration is a nice feature but forcing python on all kernel management seems constraining. I do see the argument for simplification, but since everything else is language agnostic in jupyter contracts I think this would add friction to low-level developers. What's stopping kernel discovering by scanning shared filesystem paths as an alternative as a crude way to support kernel command discovery?

@MSeal
Copy link
Contributor

MSeal commented Dec 17, 2019

I think it'd be beneficial to document the boundaries of usage with examples, with the associated metadata representations, when this moves forward. Especially for cases where it's expected that the stack developer supporting the pattern needs to manage their business rules around how to couple a notebook to a kernel. The risk here is having notebooks accidentally run with the wrong kernel and not having visibility into what's wrong or why it's occurring. Independent of how associations are made, we should probably include a path for better visibility into how a kernel was chosen and how to fix when a wrong kernel is being picked by the ecosystem.

@MSeal
Copy link
Contributor

MSeal commented Dec 18, 2019

Hoping those comments can be constructive for improving the doc. I'm not opposed to the idea in general but those things popped out to me as I was reading which probably have been thought through but I didn't get from the proposal document.

@Zsailer
Copy link
Member

Zsailer commented Dec 18, 2019

Following @jasongrout's point of order in #44, I updated the top comment to include a similar process for voting from the steering council.

@takluyver
Copy link
Member Author

Would the intent for kernels with the same language but differing kernel implementations be that you give the notebook a different language name in the metadata?

No! That's going back to what we have now, but calling the field 'language name' instead of 'kernel name', so it's more confusing.

The idea is that kernel names do not have a global shared meaning. E.g. I might create a conda environment (and associated kernel) called beta. If that's embedded in a notebook I pass to you, it will look for a beta kernel on your computer - and either you don't have one, or it doesn't mean the same thing.

I haven't worked out in detail what should happen instead - I'm hoping to make it flexible enough to allow for experimentation in how kernels are chosen for existing notebooks. E.g. maybe a notebook extension could embed dependencies in the metadata, and then when opening the notebook, find or create an environment that has the necessary dependencies. Maybe another extension could check for Docker image IDs.

This bit is definitely the most hand-wavy part of the proposal - it's an unsolved problem which I'm hoping people will play around and find good answers for. But describing kernels as 'languages' is precisely the mistake I'm trying to get away from.

What's stopping kernel discovering by scanning shared filesystem paths as an alternative as a crude way to support kernel command discovery?

We've got something like this at the moment with kernelspecs.

Of course, you could have another layer of filesystem-based discovery like this to launch kernel providers, which then launch kernels. But how does your kernel provider then give your application back something like what JKM calls a manager, which exposes operations like sending signals and waiting for the kernel process to terminate?

I think inter-language interop here is something that sounds nice, but doesn't actually solve many problems in practice. If you allow kernel providers in any language, applications have to use them as subprocesses in general. But if that's the case, specifying that they're in Python is no extra burden.

(I'll read the further responses soon)

@kevin-bates
Copy link
Member

The risk here is having notebooks accidentally run with the wrong kernel and not having visibility into what's wrong or why it's occurring. Independent of how associations are made, we should probably include a path for better visibility into how a kernel was chosen and how to fix when a wrong kernel is being picked by the ecosystem.

I've been thinking about this lately and wondering if the provider of a given kernel-type could "advertise" the kernel-type's capabilities. This idea is similar to the dependencies that Thomas speaks of but more about the environment in the kernel (notebook code) will run rather than what the code in the notebook does. The primary capability being 'language_name' of course, but if these advertised capabilities of the selected kernel were persisted in the notebook file, then upon its transfer to another system, a selection process could occur if the language/provider/name tuple isn't found.

Since kernel-types are not aware of the actual code used in the notebook, they can only provide an idea of what they're capable of. However, assuming the kernel-type was a good match when the notebook ran initially, then, on a different system, that notebook could be more easily paired to a kernel-type that matches language and most, if not all, advertised capabilities with its persisted capabilities.

In addition, I wouldn't view things like 'kubernetes' or 'docker' or 'YARN' so much as capabilities. These, IMO, are more about the deployment model and the notebook will likely not contain code specific to these entities.

I view capabilities as things related to the executable code that resides in the notebook: 'python', 'tensorflow', 'numpy', 'spark', 'gpu', 'memory' would be examples of capabilities available to a kernel-type of language 'python'.

This capabilities discussion is probably more related to JEP #46.

@echarles
Copy link
Member

We've got something like this at the moment with kernelspecs.

In other words, JKM ensures a backwards compatibility. Any kernel written in any language having today valid json file at the correct place will be discovered and available for usage. The kernelspecs providers does not need the kernel implementation to provide any additional python entrypoints.

Based on this, my understanding is that this proposals supports any kernel language (JS, R, Julia...) just like today.

@captainsafia
Copy link
Member

Thanks for opening this, @takluyver! I've been lurking on the kernel management work you've been doing over the past few months since I've seen this problem (ambiguity of kernelspecs) affect quite a few people at the organization I work out.

A convention that kernel type IDs, unique names identifying specific kernel
types in the provider system, should be exposed in user interfaces where
kernels are listed.

The nteract core state model currently does something similar to this. Each kernelspec has a unique reference associated with it that can be used for correlations between other data types in the state model. For example, a kernelspec with ref A can be associated to kernels B and C and content D. I've found this model to be a nifty way to reason about kernelsepcs and have seen other developers building notebook UIs benefit from this abstraction.

Relaxing the relationship between a notebook file and a specific named
kernel, which may not exist when the notebook is sent to another computer.

I like the model that @kevin-bates presented of associating capabilities (which in my mind I am mapping as dependencies) with language types within the new kernel type definition. Although I'm not sure how it'll work out practically with dependency versions and such.

  1. In most cases, it's still important to match the programming language of the
    code in the notebook with the kernel that will run it.

Ideally, the new kernel type model would provide a simple heuristic for associating a notebook with its matching kernel. I've had to write some pretty gnarly logic to resolve by kernelspec name, then codemirror mode, then display name, and a variety of other things to try and get something that would reliably match kernelspecs in the past.

I'd love to have a comment from nteract folks like @mpacer or @rgbkrk on whether adding features like this that are Python-frontend-specific is seen as an issue from their side. One mitigation could be a Python CLI for listing/launching available kernels.

I'll chime in on this, @minrk. A CLI is fine, there are packages in the nteract ecosystem that are essentially NodeJS wrappers around the Jupyter CLI. Ideally though, I would try to go for what @takluyver proposed and have a common standard that folks can implement in Python, NodeJS, Go, C#, and so on. I'd be happy to help validate if this is language agnostic enough by building a NodeJS implementation.

@rgbkrk
Copy link
Member

rgbkrk commented Jan 6, 2020

I would try to go for what @takluyver proposed and have a common standard that folks can implement in Python, NodeJS, Go, C#, and so on

Same thinking here.

Copy link
Contributor

@MSeal MSeal left a comment

Choose a reason for hiding this comment

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

Thanks for replying to the comments @takluyver ! And thanks for pushing a change forward. I think enough folks have chimed in and the concerns I raised are mostly covered or thought through.

@meeseeksmachine
Copy link

This pull request has been mentioned on Jupyter Community Forum. There might be relevant details there:

https://discourse.jupyter.org/t/sustainability-of-the-ipynb-nbformat-document-format/3051/4

@ivanov
Copy link
Member

ivanov commented Aug 27, 2020

I'm approving this in the interest of moving things forward.

I am glad that this proposal embraces backward compatibility with the current system. I have concerns, for example, about the added complexity, especially for new users. The phrase "It was a mistake to hide this simple concept behind a confusing 'user friendly' veneer." elevates the particular kind of advanced user who has multiple environments as a primary target user. But every Jupyter user started with one environment, and legion of them continue to use exactly one environment. So there's certainly a trade off with how visible this machinery should or could be in the user interfaces.

@takluyver
Copy link
Member Author

Trying to move this forwards again: the remaining people who haven't voted are @jasongrout @jhamrick @mpacer and @Ruv7 .

Did we set a quorum level to declare a vote like this complete with <100% of people voting?

The phrase "It was a mistake to hide this simple concept behind a confusing 'user friendly' veneer." elevates the particular kind of advanced user...

I have concerns about complexity as well, but I was trying to say that the previous design tried to be 'user friendly' in a very superficial way, and ends up being user unfriendly in a much more important way. We focused on hiding underscores etc. behind a 'display name', but it's easy to end up with multiple kernels with the same display name, which is far more confusing.

If you only ever have one kernel available, you can't run into that problem. But I don't think I'm proposing anything that would make the experience worse in that case.

@willingc
Copy link
Member

@MSeal FYI.

@takluyver I'm going to work off of @choldraf's suggestion re: acceptance. Seems reasonable to do so here too. Feel free to cast your vote on #62 too 😄

@MSeal
Copy link
Contributor

MSeal commented Sep 23, 2020

I'm still not 100% sure what will need to change in jupyter_client / nbclient / notebook_server to support the concept. Replacing jupyter_client with jupyter_kernel_mgmt is non-trivial at this point and changing that is very likely to cause a host of issues for nbconvert, nbclient, and papermill -- though @kevin-bates knows this well I believe. I also would like to know what the spec changes would be so it's clear when implementing support in different layers how the nbformat and kernel communication would be different. e.g. Where does the kernel id go? It's noted it should be unique but not at what level of enforcement of uniqueness should be held. If a kernel id doesn't exist in a system, it's implied we should fail but not explicitly said. Are we committing to that? The sharing a notebook between users with slightly different environments will start failing if we do that. I'm not sure how to communicate cleanly to a novice user how to fix this in development their flow.

Not trying to say put on the brakes on the proposal, but as one of the maintainers of a lot of things that are impacted I worry about things braking because these points aren't clear -- to me at least -- and I'm not going to be able to spend much time in the next couple months helping with understanding / applying said change.

Long winded, but what's the expectation beyond moving those repositories into jupyter org upon merging this proposal? Is it worth having a walk through on how the higher level abstractions need to change to adopt the proposal post-merge?

@MSeal
Copy link
Contributor

MSeal commented Sep 23, 2020

To be clear I'm for interface improvements here and agree with the basis of the argument for the proposal. I was hoping more of these things would pop out from discussion in this thread and didn't want to only be prompting on potential problems, but if it's going to be merged I'll maybe ask for some more clarity on the implementation half of the proposal (can be in a different thread or context if needed).

@kevin-bates
Copy link
Member

@takluyver - thank you for kicking the proposal!

@MSeal, thanks for your additional questions and concerns. I've taken the liberty to answer these as best as I can. Thomas, please feel free to correct me or comment as you see fit.

I'm still not 100% sure what will need to change in jupyter_client / nbclient / notebook_server to support the concept.

There are no plans to switch to this mechanism in the notebook server. It will continue to use jupyter_client for its kernel management and communication layer. Jupyter server, on the other hand, does have plans to adopt the kernel provider framework in its 2.0 release.

Replacing jupyter_client with jupyter_kernel_mgmt is non-trivial at this point and changing that is very likely to cause a host of issues for nbconvert, nbclient, and papermill

I'm not familiar with how those applications instantiate and interact with jupyter_client's KernelManager, but, you're right, it may be non-trivial. Updating the notebook (jupyter) server certainly was, but the gain in flexibility is significant. I think as applications move to adopt kernel providers, we'll find the need to simplify things. For example, I wouldn't mind seeing the new KernelInterface class in jupyter_server be moved into jupyter_kernel_mgmt itself, I believe that would provide a better experience for application developers and general adoption.

We will likely need to support two forms of kernel management during these times since this kind of change cannot be made overnight. The backward compatibility support, for the most part, is sufficient, although those notebooks reliant on custom KernelManagers and/or KernelSpecManagers will likely be prompted for kernel changes as their existing kernelspecs will not be found or work correctly. I call out some areas of compatibility concerns in the jupyter_server PR referenced above.

I also would like to know what the spec changes would be so it's clear when implementing support in different layers how the nbformat and kernel communication would be different. e.g. Where does the kernel id go?

By kernel id are you referring to the Kernel Type ID that will prefix the kernel name upon return from the server? I'm assuming so. In that case, the kernel_name field used today will contain a prefix with a '/' separator and followed by kernel name (similar to today). Default kernelspecs, those returned by today's KernelSpecManager will be prefixed with 'spec/'. Should an application (responsible for launching a kernel) receive a kernel name that is not prefixed, the application must assume a 'spec/' prefix, triggering use of the default kernel provider for the launch of those named kernels. This prefix is essentially the Provider ID and every Provider will register its _Provider ID_. The KernelFinder's launch()` method will then use the prefix to identify the provider and pass launch control to that provider.

Likewise, applications that have not adopted kernel providers and encounter a notebook last used via a Provider-based scheme will need to handle the prefixed kernel_name. This is probably something we could place into jupyter_client's start_kernel() method to, at least, strip 'spec/' from the name but probably can't do much more than that. If a custom provider's kernel name is encountered, there may not be much that can happen until providers are adopted by that application. As a result, those applications will break against those notebooks until the kernel name is updated and that's an issue.

It's noted it should be unique but not at what level of enforcement of uniqueness should be held.

This is a good question. The current thought is that uniqueness will be at the package level (i.e., let the package-registry be the registry).

If a kernel id doesn't exist in a system, it's implied we should fail but not explicitly said. Are we committing to that?

I believe this should be up to the application. This isn't necessarily anything the JKM framework would provide or decide. Some applications may decide to best-match the language, proliferating today's behavior. Others may prompt the user to install Kernel Provider XYZ or change the kernel themselves (caveat emptor).

The sharing a notebook between users with slightly different environments will start failing if we do that. I'm not sure how to communicate cleanly to a novice user how to fix this in development their flow.

This is the age-old question in this space (from the brief history I have witnessed). I believe Kernel Providers get us closer to a resolution because they bring the kernel lifecycle-management (which includes construction of the environment in which the kernel runs) into the picture. KPs seem like a step forward, although, I agree, they don't solve everything.

Is it worth having a walk through on how the higher level abstractions need to change to adopt the proposal post-merge?

I think if we get another application or two migrated, we'll see the necessary patterns, then we can apply any necessary refatoring to make migration easier, and, yes, a walk-through would be good at that point or during.

@willingc
Copy link
Member

willingc commented Sep 24, 2020

@takluyver I'm for the change but I'm a bit concerned about increasing confusion about kernels which are already confusing for many users. Was there ever any discussion about using the name "Environment Provider" instead of "Kernel Provider"?

@MSeal
Copy link
Contributor

MSeal commented Sep 24, 2020

Thanks for the clarifying details @kevin-bates ! If it's alright I might respond inline with follow-up questions / things that might want to be dug into as a Q&A here or as a post-merge effort.

By kernel id are you referring to the Kernel Type ID that will prefix the kernel name upon return from the server? ...

Thanks for the clearer description. Can some of these details to the JEP so it's clearer?

It's noted it should be unique but not at what level of enforcement of uniqueness should be held.

This is a good question. The current thought is that uniqueness will be at the package level (i.e., let the package-registry be the registry).

So should the prefix default to being the package name? This would be nice because you would know the package is unique within a distribution. Though packages could have multiple kernels, so is there guidance in that situation? Or am I misunderstanding the naming convention?

We will likely need to support two forms of kernel management during these times since this kind of change cannot be made overnight.

Understood. I just want to avoid fragmentation with no clear plan around convergence. It's already pretty hard to debug and explain what's wrong at the kernel interface layer in issues and having two potential paths for an extended time will be a maintenance burden. I think if we had a partial implementation running for nbclient to use this proposal it'd give a better idea of the gaps to convergence. Would / could we establish a timetable for deprecation of the older pattern?

As a result, those applications will break against those notebooks until the kernel name is updated and that's an issue.

Agreed. That might be a hard sell as custom kernels in the wild will not be happy with us.

If a kernel id doesn't exist in a system, it's implied we should fail but not explicitly said. Are we committing to that?

I believe this should be up to the application. This isn't necessarily anything the JKM framework would provide or decide. Some applications may decide to best-match the language, proliferating today's behavior. Others may prompt the user to install Kernel Provider XYZ or change the kernel themselves (caveat emptor).

That will mean inconsistent behavior between applications in a critical path of execution. For me I envision a lot of "it works this way in XYZ application, why doesn't papermill do the same?" with no good resolution because if we make it work like XYZ then it'll be different than application ABC's behavior. Am I being overly concerned here or is it clearer to other's how the difference in execution will be handled well?

This is the age-old question in this space (from the brief history I have witnessed). I believe Kernel Providers get us closer to a resolution because they bring the kernel lifecycle-management (which includes construction of the environment in which the kernel runs) into the picture. KPs seem like a step forward, although, I agree, they don't solve everything.

I personally think that we need clear instructions or tooling changes for kernel lifecycle-management if we change how it works today and that without really solid how-to's and simple operations we'll make it impossible for non-experts to resolve kernel issues when sharing notebooks. I recognize this might be too large an ask for the JEP itself, but denoting it as a requirement to be worked out before services adopt KPs might be warranted. As an example I don't really understand how a .NET or Scala kernel will be available to the provider or how to ensure it is available at runtime in a consistent manner. I don't believe telling them they have to write a parallel python package is going to go over well if that's the plan (e.g. recent Julia kernel conversations) and if it is what we're committing to it needs to be Super clear for a non-python developer how to walk through that imo.

Hopefully this is a useful set of discussion points for everyone else as well, but thank you for engaging on the questions / concerns for my sake at least. I believe it will help a lot down the road for application support.

@kevin-bates
Copy link
Member

Thanks for your responses and questions. My responses follow:

By kernel id are you referring to the Kernel Type ID that will prefix the kernel name upon return from the server? ...

Thanks for the clearer description. Can some of these details to the JEP so it's clearer?

I'm not sure what else to add. Kernel Type ID seems fairly well-defined. Perhaps a comment stating something to the effect:

With this proposal, the kernel_name returned in the discovered kernel specification would reflect the Kernel Type ID rather than the kernel name itself.

Would that help?

It's noted it should be unique but not at what level of enforcement of uniqueness should be held.

This is a good question. The current thought is that uniqueness will be at the package level (i.e., let the package-registry be the registry).

So should the prefix default to being the package name? This would be nice because you would know the package is unique within a distribution. Though packages could have multiple kernels, so is there guidance in that situation? Or am I misunderstanding the naming convention?

I think you're conflating kernel implementations with providers and it might help to think about this as @willingc suggests, environment providers. A given Kernel Provider will not necessarily be tied to a particluar kernel. For example, a Kubernetes provider would be responsible for managing the kernel lifecycle of various kernels within a k8s cluster. This single provider might return discovery results across several kernel implementations.

Would / could we establish a timetable for deprecation of the older pattern?

I think this would be a good idea, although I think such a timetable should include input from various stakeholders - i.e., any application that currently operates on notebook files and uses jupyter_client for its kernel management. After thinking about things, I think we might be able to buy ourselves more time by NOT prefixing kernel names with 'spec/' for the default cases. This would allow older applications to continue working against notebook files created via a provider-based framework. (Suggested by @echarles in today's Jupyter Server dev meeting.)

As a result, those applications will break against those notebooks until the kernel name is updated and that's an issue.

Agreed. That might be a hard sell as custom kernels in the wild will not be happy with us.

Hmm - I think you're mixing kernel implementation again here. Any custom kernels that are discovered and managed by the default framework today will continue working with the kernel provider framework tomorrow. Only applications reliant on overriding KernelSpecManager and/or KernelManager are required to develop a correspond Kernel Provider to preserve whatever functionality their "manager" implementations provided.

If a kernel id doesn't exist in a system, it's implied we should fail but not explicitly said. Are we committing to that?

I believe this should be up to the application. This isn't necessarily anything the JKM framework would provide or decide. Some applications may decide to best-match the language, proliferating today's behavior. Others may prompt the user to install Kernel Provider XYZ or change the kernel themselves (caveat emptor).

That will mean inconsistent behavior between applications in a critical path of execution. For me I envision a lot of "it works this way in XYZ application, why doesn't papermill do the same?" with no good resolution because if we make it work like XYZ then it'll be different than application ABC's behavior. Am I being overly concerned here or is it clearer to other's how the difference in execution will be handled well?

This is a valid point for the (presumably) rare times a given provider, outside the built-in default, does not exist. Frankly, I think it will be quite rare to run into a Kernel Type ID that includes a prefix other than 'spec/' (or per the comment above, a prefix at all). How often do you run into folks needing to bring their own Kernel Manager implemenation? It think this issue will be less than that since its likely they'll already have the appropriate Kernel Provider installed.

I personally think that we need clear instructions or tooling changes for kernel lifecycle-management if we change how it works today and that without really solid how-to's and simple operations we'll make it impossible for non-experts to resolve kernel issues when sharing notebooks. I recognize this might be too large an ask for the JEP itself, but denoting it as a requirement to be worked out before services adopt KPs might be warranted. As an example I don't really understand how a .NET or Scala kernel will be available to the provider or how to ensure it is available at runtime in a consistent manner. I don't believe telling them they have to write a parallel python package is going to go over well if that's the plan (e.g. recent Julia kernel conversations) and if it is what we're committing to it needs to be Super clear for a non-python developer how to walk through that imo.

Again, for the 90% case, nothing changes. For a kernel provider implementation that is specific to a particular kernel, I would argue its up to that provider's installation and/or documentation to address necessary requirements and/or provide tooling to configure its resources so its discovery framework can produce the appropriate specifications.

Hopefully this is a useful set of discussion points for everyone else as well, but thank you for engaging on the questions / concerns for my sake at least. I believe it will help a lot down the road for application support.

I completely agree Matt and appreciate your interest, concerns, and questions. Thank you.

@MSeal
Copy link
Contributor

MSeal commented Sep 24, 2020

I really appreciate the patient responses here.

I think you're conflating kernel implementations with providers

I think I get that but I'm unclear what their relationship will become between kernels, providers, and other services and so am probing the edges. The overall role everything will play post this change in relation to each service / library is what's confusing to me. It might be helpful to have the relation between libraries and tools post acceptance flushed out and to know what would have to change for existing kernel authors and library maintainers outside of jupyter_server would need to do for full adoption. e.g. should the default implementation provide all the existing kernels as-is from the provider to avoid compatibility concerns.

If there were jupyter_server meetups that dug into this in more depth, I might have been missing some context and intent here.

@kevin-bates
Copy link
Member

I'm unclear what their relationship will become between kernels, providers, and other services and so am probing the edges.

No worries. I'll start with a question: What's the relationship between kernels and jupyter_client's KernelSpecManager/KernelManager classes? They're essentially orthogonal. The same is true for kernel implementations and providers. The default Kernel Provider is essentially the code from KernelSpecManager and KernelManager and that provider "discovers" available kernel specifications by looking in specific directories of the filesystem. What kernels are configured within each of those specifications is independent of this provider, it doesn't care. Some providers might care and those providers will be aware of the relationship with the kernel in a more tightly-coupled manner. The provider provides a well-known interface (similar to KernelManager's) that applications will use and providers will honor.

I'd also like to point out that a primary intention of this proposal is to permit multiple KernelSpecManager/KernelManager kinds of implementations simultanenously - which is something no Jupyter application permits today.

should the default implementation provide all the existing kernels as-is from the provider to avoid compatibility concerns.

If those kernels "advertise" their availability via kernel.json files in certain directories within the filesystem, then yes, absolutely. Those will "just work" since that's what the default provider implementation is - i.e., today's behavior.

If there were jupyter_server meetups that dug into this in more depth, I might have been missing some context and intent here.

There are weekly jupyter_server dev meetings. We've only had discussions regarding kernel providers on occasion. Anyone is welcome to attend to discuss any related topic.

@MSeal
Copy link
Contributor

MSeal commented Sep 25, 2020

... If those kernels "advertise" their availability via kernel.json files in certain directories within the filesystem, then yes, absolutely. Those will "just work" since that's what the default provider implementation is - i.e., today's behavior.

That helps describe default behavior, thank you.

There are weekly jupyter_server dev meetings. We've only had discussions regarding kernel providers on occasion. Anyone is welcome to attend to discuss any related topic.

Didn't mean any negativity about that meeting therein if that was implied, I just literally hadn't attended them and was probably missing context as a result.

@kevin-bates
Copy link
Member

Didn't mean any negativity about that meeting therein if that was implied, I just literally hadn't attended them and was probably missing context as a result.

I'm sorry Matt if my response was taken in that vein. Reading it back now, I can understand how that may have come across. It was meant to be informative for yourself and others and nothing more. I should have prefaced it with something like, "In case you're interested...", but, honestly was a bit haste to end my day. Have a good evening.

@MSeal
Copy link
Contributor

MSeal commented Sep 25, 2020

I had the same feeling about my earlier statement and didn't assume any ill intent on your response.

You have a good evening too :)

@takluyver
Copy link
Member Author

@willingc

Was there ever any discussion about using the name "Environment Provider" instead of "Kernel Provider"?

I don't think there was.

Environments are a convenient starting point to explain this in a Python context - we're used to creating multiple environments with conda or virtualenv, and that's an easy way to end up with multiple kernel types for one language. But you could also have a kernel type for Python running in a Docker container from image XYZ, or an emulator for a different CPU, or on a specific remote machine. You could call all of these 'environments', but I'm not sure people would readily make that leap from the name.

@willingc
Copy link
Member

@takluyver Thanks. I see the point for the use cases beyond Python. Maybe ExecutionKernelProvider to minimize some of the confusion with kernels and naming. How would you feel about doing an updated pass at the JEP? Happy to pitch in if needed on the writing.

@takluyver
Copy link
Member Author

There's certainly scope for confusion around the term 'kernel', but I'm not sure what confusion is avoided by adding the word 'execution'.

I've tried in this proposal to distinguish 'kernel types' (e.g. Python running in this environment) from 'kernel instances' (e.g. process 1234 listening on these ports). The 'kernel providers' described here could be seen as kernel type providers in the first instance - e.g. a provider for conda environments would offer new kernel types, but kernel instances would be managed as subprocesses by the same code as for the spec provider. But other providers may be more involved in managing kernel instances, e.g. for a kernel instance that is inside a Docker container.

@MSeal :

I just want to avoid fragmentation with no clear plan around convergence. It's already pretty hard to debug and explain what's wrong at the kernel interface layer in issues and having two potential paths for an extended time will be a maintenance burden.

This is a very real concern. Given how long this has been hanging around already, I can't realistically claim that I'm ready to swiftly integrate it with the ecosystem. There were proof of concept PRs for projects like nbconvert, but these have long since gone stale. And given how hard I've found it to explain even to people involved in Jupyter development, confusion in the wider community is all but inevitable. Perhaps it's more sensible to let this drop and look for ways to improve the situation more gradually.

@kevin-bates
Copy link
Member

Perhaps it's more sensible to let this drop and look for ways to improve the situation more gradually.

To be completely honest, I'm feeling the same way (and have been for a while) but felt things needed to be supported given the time spent. I wasn't aware of how prevalent jupyter_client was across so many other applications - each of which would be impacted.

Should this proposal be retracted, I do have ideas for how we could implement different "execution environments" by simply abstracting the process (Popen) layer within jupyter_client itself. It would mean we'd embrace the kernel.json framework - allowing configuration extensions (via the existing metadata stanza) to convey necessary customizations - including parameterization.

@MSeal
Copy link
Contributor

MSeal commented Oct 1, 2020

by simply abstracting the process (Popen) layer within jupyter_client itself

That does sound promising and a smaller incremental change to the ecosystem

@takluyver
Copy link
Member Author

I still think it would be valuable to have a more flexible way to discover kernel types - e.g. allowing something like nb_conda_kernels as an addition to standard kernel discovery, rather than a replacement of it. But if anything like that is going to happen, it will need someone with a decent overview of the Jupyter world to design and push it.

we could implement different "execution environments" by simply abstracting the process (Popen) layer within jupyter_client itself.

I did initially try to do this as a refinement of jupyter_client. At one point it was going to be jupyter_client 6.0, I think. But the changes I wanted were going to break so much backwards compatibility that it seemed better to give it a new name.

Anyway, sorry to waste everyone's time by pushing for votes on this.

@takluyver takluyver closed this Oct 8, 2020
@willingc
Copy link
Member

willingc commented Oct 8, 2020

I still think it would be valuable to have a more flexible way to discover kernel types

I completely agree.

Anyway, sorry to waste everyone's time by pushing for votes on this.

Your insights are never a waste. I value all that you bring to Jupyter. ❤️

@ellisonbg
Copy link
Contributor

Some additional comments on some of the patterns I am seeing in how the kernel/kernelspec abstraction related to "environments" (either conda/pip envs, or docker containers):

  • I see users using environments as containers for both kernels and the libraries/packages used in those kernels. I would describe this pattern as "environments contain kernels".
  • Some users like the simplicity of having a one-one relationship between an environment and a kernel, but there are other users that have multiple kernels per environment.
  • Right now, the kernel/kernelspec abstraction doesn't know anything about environments. As a result, many extensions are mutating kernel names/ids to add the environment id/name. This gets painful because 1) kernel names get long, 2) the global kernel list is flat and difficult to navigate.
  • To address this some of this complexity, at AWS, we have been adding the environment name to the kernelspec metadata, and then group the kernels by the environment, and allow the user to pick the environment in the launcher using a dropdown - then only the kernels for that environment show. This pattern is working well, and we don't have any barriers to upstreaming this. It would probably be another JEP that describes how kernelspecs and kernel providers can be made to be environment aware in a manner that works for conda/pip/docker, etc.

@kevin-bates
Copy link
Member

by simply abstracting the process (Popen) layer within jupyter_client itself

That does sound promising and a smaller incremental change to the ecosystem

jupyter/jupyter_client#608

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.