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

Notebook kernels #101963

Closed
5 tasks
rebornix opened this issue Jul 9, 2020 · 5 comments
Closed
5 tasks

Notebook kernels #101963

rebornix opened this issue Jul 9, 2020 · 5 comments
Assignees
Milestone

Comments

@rebornix
Copy link
Member

rebornix commented Jul 9, 2020

We introduced the concept of vscode.NotebookKernel previously to allow multiple notebook execution contributions and the responsibilities of Content Provider, Renderer and Kernels are much more clear. The proposal we had was a provider like kernel contribution:

export namespace notebook {
    export function registerNotebookKernel(
        id: string,
        selectors: GlobPattern[],
        kernel: NotebookKernel
    ): Disposable;
}

Extensions can register kernels for specific files (filtered by selectors: GlobPattern[], e.g., *.ipynb), it works fine for simple cases (like GitHub Issue Notebook, which only has one kernel for all *.github-issues files). However it's not clear how multiple kernels contribution really work with this API, here are scenarios we need to take into accounts when polishing the design of the kernel API

  • Kernel registration
    • Dynamic multiple kernel reigstration. Current kernel registrations are static (through contribtions in package.json) but sometimes extensions can know what kernels are available only after it's activated.
    • How are we going to support kernels per document?
    • Event for available kernels change
  • Kernel activation
    • Some kernels need to be activated before the first execution (e.g., jupyter kenel with ipywidgets support). We might want to introduce an activation method for the kernel, resolveNotebook(document: NotebookDocument, webview: NotebookCommunication): Promise<void>;
    • If users switch kernels, is previous kernel still active? Should we kill/interupt the kernel?
  • Kernel picker & active kernel indicator
@rebornix rebornix added this to the July 2020 milestone Jul 9, 2020
@rebornix rebornix self-assigned this Jul 9, 2020
@rebornix
Copy link
Member Author

rebornix commented Jul 13, 2020

As we need to support dynamic kernel registration and potentially support kernels per document, one proposal is provider-like api. The shape and concepts of the API would be similar to Code Actions to some extent (a provider can provide code fixes for code ranges in a document, and users can pick code fixes from all providers, or quickly apply fixes if there is a preferred fix). Let's sketch and see if it meets the requirements.

Firstly, extensions can register kernel providers for a notebook document by selector, which extensions can filter notebook documents by either view type or file extensions.

export interface NotebookDocumentFilter {
    viewType?: string;
    filenamePattern?: GlobPattern;
    excludeFileNamePattern?: GlobPattern;
}

export function registerNotebookKernelProvider(
    selector: NotebookDocumentFilter,
    provider: NotebookKernelProvider
): Disposable;

The kernel provider is responsible for

  1. provide available kernels for a document, this operation should be fast
  2. when a kernel is selected, the core will call NotebookKernelProvider#resolveKernel. Extensions should then handle the initialization of the kernel
    • Kernel initialization would include: starting the kernel, building communication between the kernel and the output webview (through webview: NotebookCommunication), etc.
  3. when extensions detect more available extensions, it should trigger onDidChangeKernels
export interface NotebookKernelProvider<T extends NotebookKernel = NotebookKernel> {
    onDidChangeKernels?: Event<void>;
    provideKernels(document: NotebookDocument, token: CancellationToken): ProviderResult<T[]>;
    resolveKernel?(kernel: T, document: NotebookDocument, webview: NotebookCommunication, token: CancellationToken): ProviderResult<T>;
}

A notebook kernel contains following information

  • label, description: human readable primary and secondary description text
  • preloads[]: preload javascript dependencies
  • isPreferred: when true, the kernel will be selected automatically (if there is no other preferred kernels from other extensions)
  • execute*: methods for executing cells
export interface NotebookKernel {
    label: string;
    description?: string;
    preloads?: Uri[];
    isPreferred?: boolean;
    executeCell(document: NotebookDocument, cell: NotebookCell, token: CancellationToken): Promise<void>;
    executeAllCells(document: NotebookDocument, token: CancellationToken): Promise<void>;
}

Lastly the extensions can listen to active kernel change event if it wants to any kind of clean up when a kernel becomes disconnected.

export namespace notebook {
    export let activeNotebookKernel: NotebookKernel | undefined;
    export const onDidChangeActiveKernel: Event<void>;
}

With this provider like API, all available kernels for a document would be listed in a flat list if there are more than one kernels. For simple scenarios like GitHub Issue Notebook, the extension will contribute both content provider and kernel provider, which provides a preferred kernel, and then the kernel can be automatically selected and users won't be bothered by it.

Check list

This proposal meets the criteria listed above

  • dynamic kernel registration: through onDidChangeKernels
  • kernels per document: through provideKernels(document: NotebookDocument, token: CancellationToken)
  • kernel activation: resolveKernel
  • kernel disconnection: extensions can implement this by listening to onDidChangeActiveKernel

@jrieken
Copy link
Member

jrieken commented Jul 14, 2020

Reads nicely!

Firstly, extensions can register kernel providers for a document (selector).

That's a notebook document, right? I wonder if document selector is the right thing as it is often associated with selecting a language. Maybe we should introduce a notebook selector - which is similar but allows to select a notebook view type.

@rebornix
Copy link
Member Author

Maybe we should introduce a notebook selector - which is similar but allows to select a notebook view type.

Good suggestion, updated the proposal accordingly.

@rebornix
Copy link
Member Author

The API proposal above meets the requirements for resolving available notebook kernels from multiple kernel providers but while adopting this in Jupyter notebooks, we (@DonJayamanne, me and more) found that it's challenging to implement a Jupyter compliant notebook kernel provider as Jupyter stores kernel information in every document. In other words, traditional Jupyter stores user's kernel selection directly in notebook documents. It's not clear who is the source of truth of kernel selection with current proposal.

The problem can be tackled in multiple approaches, one of them is changing the semantics of isPreferred from recommendation to selection. When a notebook document is opened, we will find the right kernel in following sequence

  • Find the preferred kernel provider
    • User setting, user can explicitly associate a kernel provider (extension) with a notebook view type. Once set the kernel provider will also be used. Note that one extension can only register one kernel provider.
    • Otherwise, find the kernel provider from the same extension which registers notebook content provider
    • Otherwise, use the first registered one
  • Find the kernel from the kernel provider
    • Find isPreferred: true kernel from kernel provider
    • User selection we stored in the core

Scenarios:

1. Content provider and single kernel from the same extension

  • The extension registers itself as notebook content provider and kernel provider, the kernel provdier only returns one kernel, it doesn't need to set isPreferred

2. Content provider and multiple kernels from the same extension

  • The extension registers itself as notebook content provider and kernel provider, the core will always try to find the best matched kernel from this extension
  • The extension can use isPreferred to tell vscode which kernel should be picked.
  • As long as the extension provides isPreferred kernel, it's also responsible for storing user's preference (by listening to active kernel change event)
  • If there is no isPreferred kernel, vscode tracks the user selection

3. Content provider and multiple kernels from extension A, additional kernel providers from extension B

  • The extension A registers itself as notebook content provider and kernel provider A. Extension B registers itself as kernel provider B.
  • Extension A can use isPreferred to tell vscode which kernel should be picked. It will override the user selection stored in the core
  • When users choose a kernel from extension B, we will remember it for current session (per session, per document). When the workspace is reloaded, we will still load the kernel from extension A, except
    • Users change the kernelProvider-viewType association setting

4. Content provider from extension A, kernel providers from extension B and C

  • We will choose kernel from the first registered kernel, except that users set kernelProvider-viewType association

@rebornix
Copy link
Member Author

image

@rebornix rebornix closed this as completed Aug 3, 2020
@rebornix rebornix reopened this Aug 25, 2020
@github-actions github-actions bot locked and limited conversation to collaborators Oct 9, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants