Skip to content

Commit

Permalink
Updates to the Jupyter Extension API (#14308)
Browse files Browse the repository at this point in the history
  • Loading branch information
DonJayamanne authored Sep 12, 2023
1 parent 7a738b3 commit 2f76851
Show file tree
Hide file tree
Showing 15 changed files with 133 additions and 204 deletions.
2 changes: 1 addition & 1 deletion api/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ if (process.env.npm_config_tag === 'proposed') {
return imports + [`declare module '@vscode/jupyter-extension' {`, `${tab}${line}`].join(EOL);
}
if (foundFirstExport) {
`${tab}${line}`;
return `${tab}${line}`;
}
return line;
})
Expand Down
52 changes: 8 additions & 44 deletions src/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ export interface JupyterAPI {
* Extensions can use this API to provide a list of Jupyter Servers to VS Code users with custom authentication schemes.
* E.g. one could provide a list of Jupyter Servers that require Kerberos authentication or other.
*/
createJupyterServerCollection(id: string, label: string): JupyterServerCollection;
createJupyterServerCollection(
id: string,
label: string,
serverProvider: JupyterServerProvider
): JupyterServerCollection;
}

/**
Expand All @@ -38,24 +42,7 @@ export interface JupyterServerConnectionInformation {
* If a {@link token token} is not provided, then headers will be used to connect to the server.
*/
readonly headers?: Record<string, string>;
/**
* The local directory that maps to the remote directory of the Jupyter Server.
* E.g. assume you start Jupyter Notebook on a remote machine with --notebook-dir=/foo/bar,
* and you have a file named /foo/bar/sample.ipynb, /foo/bar/sample2.ipynb and the like.
* Next assume you have local directory named /users/xyz/remoteServer with the files with the same names, sample.ipynb and sample2.ipynb
*
*
* Using this setting one can map the local directory to the remote directory.
* With the previous example in mind, the value of this property would be Uri.file('/users/xyz/remoteServer').
*
* This results in Jupyter Session names being generated in a way thats is consistent with Jupyter Notebook/Lab.
* I.e. the session names map to the relative path of the notebook file.
* Taking the previous example into account, if one were to start a Remote Kernel for the local file `/users/xyz/remoteServer/sample2.ipynb`,
* then the name of the remote Jupyter Session would be `sample2.ipynb`.
*
* This is useful in the context where the remote Jupyter Server is running on the same machine as VS Code, but the files are mapped on different directories.
*/
readonly mappedRemote?: Uri;

/**
* Returns the sub-protocols to be used. See details of `protocols` here https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/WebSocket
* Useful if there is a custom authentication scheme that needs to be used for WebSocket connections.
Expand Down Expand Up @@ -89,25 +76,6 @@ export interface JupyterServer {
readonly connectionInformation?: JupyterServerConnectionInformation;
}

/**
* Represents a Jupyter Server with certain information that cannot be `undefined`.
* For instance the {@link connectionInformation} cannot be `undefined` as this is required to connect to the server.
*/
export interface ResolvedJupyterServer {
/**
* Unique identifier for this server.
*/
readonly id: string;
/**
* A human-readable string representing the name of the Server.
*/
readonly label: string;
/**
* Information required to Connect to the Jupyter Server.
*/
readonly connectionInformation: JupyterServerConnectionInformation;
}

/**
* Provider of Jupyter Servers.
*/
Expand All @@ -126,7 +94,7 @@ export interface JupyterServerProvider {
* Returns the connection information for the Jupyter server.
* It is OK to return the given `server`. When no result is returned, the given `server` will be used.
*/
resolveJupyterServer(server: JupyterServer, token: CancellationToken): ProviderResult<ResolvedJupyterServer>;
resolveJupyterServer(server: JupyterServer, token: CancellationToken): ProviderResult<JupyterServer>;
}

/**
Expand All @@ -153,7 +121,7 @@ export interface JupyterServerCommandProvider {
* Returns a list of commands to be displayed to the user.
* @param value The value entered by the user in the quick pick.
*/
provideCommands(value: string | undefined, token: CancellationToken): Promise<JupyterServerCommand[]>;
provideCommands(value: string | undefined, token: CancellationToken): ProviderResult<JupyterServerCommand[]>;
/**
* Invoked when a {@link JupyterServerCommand command} has been selected.
* @param command The {@link JupyterServerCommand command} selected by the user.
Expand Down Expand Up @@ -186,10 +154,6 @@ export interface JupyterServerCollection {
* A link to a resource containing more information. This can be read and updated by the extension.
*/
documentation?: Uri;
/**
* Provider of {@link JupyterServer Jupyter Servers}. This can be read and updated by the extension.
*/
serverProvider?: JupyterServerProvider;
/**
* Provider of {@link JupyterServerCommand Commands}. This can be read and updated by the extension.
*/
Expand Down
21 changes: 0 additions & 21 deletions src/api.deprecated.d.ts

This file was deleted.

4 changes: 4 additions & 0 deletions src/api.internal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ declare module './api' {
* Used internally by Jupyter Extension to detect changes to the JupyterServerProvider.
*/
onDidChangeProvider: Event<void>;
/**
* Used internal by Jupyter extension to tarck the Server Provider.
*/
readonly serverProvider: JupyterServerProvider;
}
export interface IJupyterUriProvider {
/**
Expand Down
4 changes: 2 additions & 2 deletions src/api.proposed.kernelStarupHook.d.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// https://github.com/microsoft/vscode-jupyter/issues/13893

import type { Session } from '@jupyterlab/services';
import type { CancellationToken, Uri } from 'vscode';

declare module './api' {
export interface JupyterServerProvider {
/**
* Note: For Synapse, https://github.com/microsoft/vscode-jupyter/issues/13893
*
* Invoked after a kernel has been started, allowing the contributing extension to perform startup
* actions on the kernel.
* This is only invoked for kernels
Expand Down
29 changes: 29 additions & 0 deletions src/api.proposed.mappedRemoteDirectory.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { Uri } from 'vscode';

declare module './api' {
export interface JupyterServer {
/**
* Note: Required for AzML, perhaps CodeSpaces and Pengs personal extension.
*
* The local directory that maps to the remote directory of the Jupyter Server.
* E.g. assume you start Jupyter Notebook on a remote machine with --notebook-dir=/foo/bar,
* and you have a file named /foo/bar/sample.ipynb, /foo/bar/sample2.ipynb and the like.
* Next assume you have local directory named /users/xyz/remoteServer with the files with the same names, sample.ipynb and sample2.ipynb
*
*
* Using this setting one can map the local directory to the remote directory.
* With the previous example in mind, the value of this property would be Uri.file('/users/xyz/remoteServer').
*
* This results in Jupyter Session names being generated in a way thats is consistent with Jupyter Notebook/Lab.
* I.e. the session names map to the relative path of the notebook file.
* Taking the previous example into account, if one were to start a Remote Kernel for the local file `/users/xyz/remoteServer/sample2.ipynb`,
* then the name of the remote Jupyter Session would be `sample2.ipynb`.
*
* This is useful in the context where the remote Jupyter Server is running on the same machine as VS Code, but the files are mapped on different directories.
*/
readonly mappedRemoteDirectory?: Uri;
}
}
5 changes: 3 additions & 2 deletions src/api.proposed.removeJupyterServer.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@

import { Uri } from 'vscode';

// Ability to remove a Jupyter server is internal to the Jupyter Extension & Jupyter Hub extension.

declare module './api' {
export interface JupyterServerProvider {
/**
* Note: For Internal Jupyter Server Provider and JupyterHub extension.
* Ability to remove a Jupyter server is internal to the Jupyter Extension & Jupyter Hub extension.
*
* Display a `trash` icon next to each server in the quick pick.
* Allowing the user to remove this server.
* Currently used only by the Jupyter Extension.
Expand Down
43 changes: 15 additions & 28 deletions src/kernels/jupyter/connection/jupyterServerProviderRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,10 @@ import { traceError } from '../../../platform/logging';
import { JVSC_EXTENSION_ID } from '../../../platform/common/constants';

export class JupyterServerCollectionImpl extends Disposables implements JupyterServerCollection {
private _serverProvider?: JupyterServerProvider;
private _commandProvider?: JupyterServerCommandProvider;
documentation?: Uri | undefined;
private _onDidChangeProvider = new EventEmitter<void>();
onDidChangeProvider = this._onDidChangeProvider.event;
set serverProvider(value: JupyterServerProvider | undefined) {
this._serverProvider = value;
this._onDidChangeProvider.fire();
}
get serverProvider(): JupyterServerProvider | undefined {
return this._serverProvider;
}
set commandProvider(value: JupyterServerCommandProvider | undefined) {
this._commandProvider = value;
this._onDidChangeProvider.fire();
Expand All @@ -50,7 +42,8 @@ export class JupyterServerCollectionImpl extends Disposables implements JupyterS
constructor(
public readonly extensionId: string,
public readonly id: string,
public label: string
public label: string,
public readonly serverProvider: JupyterServerProvider
) {
super();
}
Expand Down Expand Up @@ -112,26 +105,15 @@ class JupyterUriProviderAdaptor extends Disposables implements IJupyterUriProvid
}
const token = new CancellationTokenSource();
try {
let items: JupyterServerCommand[] = [];
if (this.provider.commandProvider.provideCommands) {
items = await this.provider.commandProvider.provideCommands(value || '', token.token);
if (
(!items || !items.length) &&
Array.isArray(this.provider.commandProvider.commands) &&
this.provider.commandProvider.commands.length
) {
items = this.provider.commandProvider.commands;
}
} else {
items = this.provider.commandProvider.commands || [];
}
const items =
(await Promise.resolve(this.provider.commandProvider.provideCommands(value || '', token.token))) || [];
if (!value) {
this.commands.clear();
}
items.forEach((c) => this.commands.set(c.label || c.title || '', c));
items.forEach((c) => this.commands.set(c.label, c));
return items.map((c) => {
return {
label: stripCodicons(c.label || c.title),
label: stripCodicons(c.label),
description: stripCodicons(c.description),
command: c
};
Expand Down Expand Up @@ -196,7 +178,7 @@ class JupyterUriProviderAdaptor extends Disposables implements IJupyterUriProvid
displayName: server.label,
token: info.token || '',
authorizationHeader: info.headers,
mappedRemoteNotebookDir: info.mappedRemote?.toString(),
mappedRemoteNotebookDir: server.mappedRemoteDirectory?.toString(),
webSocketProtocols: info.webSocketProtocols
};
}
Expand All @@ -215,7 +197,7 @@ class JupyterUriProviderAdaptor extends Disposables implements IJupyterUriProvid
displayName: server.label,
token: info.token || '',
authorizationHeader: info.headers,
mappedRemoteNotebookDir: info.mappedRemote?.toString(),
mappedRemoteNotebookDir: (result || server).mappedRemoteDirectory?.toString(),
webSocketProtocols: info.webSocketProtocols
};
}
Expand Down Expand Up @@ -302,15 +284,20 @@ export class JupyterServerProviderRegistry extends Disposables implements IJupyt
super();
disposables.push(this);
}
createJupyterServerCollection(extensionId: string, id: string, label: string): JupyterServerCollection {
createJupyterServerCollection(
extensionId: string,
id: string,
label: string,
serverProvider: JupyterServerProvider
): JupyterServerCollection {
const extId = `${extensionId}#${id}`;
if (this._collections.has(extId)) {
// When testing we might have a duplicate as we call the registration API in ctor of a test.
if (extensionId !== JVSC_EXTENSION_ID) {
throw new Error(`Jupyter Server Provider with id ${extId} already exists`);
}
}
const collection = new JupyterServerCollectionImpl(extensionId, id, label);
const collection = new JupyterServerCollectionImpl(extensionId, id, label, serverProvider);
this._collections.set(extId, collection);
let uriRegistration: IDisposable | undefined;
let adapter: JupyterUriProviderAdaptor | undefined;
Expand Down
9 changes: 7 additions & 2 deletions src/kernels/jupyter/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
} from '../types';
import { ClassType } from '../../platform/ioc/types';
import { ContributedKernelFinderKind, IContributedKernelFinder } from '../internalTypes';
import { IJupyterServerUri, IJupyterUriProvider, JupyterServerCollection } from '../../api';
import { IJupyterServerUri, IJupyterUriProvider, JupyterServerCollection, JupyterServerProvider } from '../../api';
import { IQuickPickItemProvider } from '../../platform/common/providerBasedQuickPick';

export type JupyterServerInfo = {
Expand Down Expand Up @@ -308,5 +308,10 @@ export const IJupyterServerProviderRegistry = Symbol('IJupyterServerProviderRegi
export interface IJupyterServerProviderRegistry {
onDidChangeCollections: Event<{ added: JupyterServerCollection[]; removed: JupyterServerCollection[] }>;
readonly jupyterCollections: readonly JupyterServerCollection[];
createJupyterServerCollection(extensionId: string, id: string, label: string): JupyterServerCollection;
createJupyterServerCollection(
extensionId: string,
id: string,
label: string,
serverProvider: JupyterServerProvider
): JupyterServerCollection;
}
Loading

0 comments on commit 2f76851

Please sign in to comment.