mirror of
https://github.com/microsoft/vscode-extension-samples.git
synced 2026-06-13 07:10:26 +08:00
Sample extension using Jupyter Execution API
This commit is contained in:
23
jupyter-kernel-execution-sample/.eslintrc.js
Normal file
23
jupyter-kernel-execution-sample/.eslintrc.js
Normal file
@ -0,0 +1,23 @@
|
||||
/**@type {import('eslint').Linter.Config} */
|
||||
// eslint-disable-next-line no-undef
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
],
|
||||
rules: {
|
||||
'semi': [
|
||||
2,
|
||||
"always"
|
||||
],
|
||||
'@typescript-eslint/no-unused-vars': 0,
|
||||
'@typescript-eslint/no-explicit-any': 0,
|
||||
'@typescript-eslint/explicit-module-boundary-types': 0,
|
||||
'@typescript-eslint/no-non-null-assertion': 0,
|
||||
}
|
||||
};
|
||||
6
jupyter-kernel-execution-sample/.vscode/extensions.json
vendored
Normal file
6
jupyter-kernel-execution-sample/.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"ms-toolsai.jupyter"
|
||||
]
|
||||
}
|
||||
22
jupyter-kernel-execution-sample/.vscode/launch.json
vendored
Normal file
22
jupyter-kernel-execution-sample/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// A launch configuration that compiles the extension and then opens it inside a new window
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}"
|
||||
],
|
||||
"name": "Run Extension",
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "npm: compile",
|
||||
"request": "launch",
|
||||
"trace": true,
|
||||
"type": "extensionHost"
|
||||
}
|
||||
],
|
||||
"version": "0.2.0"
|
||||
}
|
||||
6
jupyter-kernel-execution-sample/.vscode/settings.json
vendored
Normal file
6
jupyter-kernel-execution-sample/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"search.exclude": {
|
||||
"out": true
|
||||
},
|
||||
"typescript.tsc.autoDetect": "off"
|
||||
}
|
||||
13
jupyter-kernel-execution-sample/.vscode/tasks.json
vendored
Normal file
13
jupyter-kernel-execution-sample/.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "compile",
|
||||
"problemMatcher": ["$tsc"],
|
||||
"group": "build"
|
||||
}
|
||||
]
|
||||
}
|
||||
12
jupyter-kernel-execution-sample/.vscodeignore
Normal file
12
jupyter-kernel-execution-sample/.vscodeignore
Normal file
@ -0,0 +1,12 @@
|
||||
.vscode/**
|
||||
.vscode-test/**
|
||||
out/test/**
|
||||
src/**
|
||||
.gitignore
|
||||
.yarnrc
|
||||
vsc-extension-quickstart.md
|
||||
**/tsconfig.json
|
||||
**/.eslintrc.json
|
||||
**/*.map
|
||||
**/*.ts
|
||||
**/*.tsbuildinfo
|
||||
28
jupyter-kernel-execution-sample/README.md
Normal file
28
jupyter-kernel-execution-sample/README.md
Normal file
@ -0,0 +1,28 @@
|
||||
# Jupyter Server Provider Sample
|
||||
|
||||
This is a very simple extension sample demonstrating the use of the Jupyter Extension API allowing other extensions to execute code against Jupyter Kernels.
|
||||
|
||||
- The sample lists finds kernels associated with notebooks that are currently open in the workspace.
|
||||
- The sample the filters the kernels by language, focusing on Python kernels.
|
||||
- Upon selecting a Python kernel, code selected by the user is executed against the selected kernel
|
||||
- The output is displayed in an output panel.
|
||||
- The sample demonstrates the ability to retrieve outputs of various mime types, including streamed output.
|
||||
|
||||
## Running this sample
|
||||
|
||||
1. `cd jupyter-kernel-execution-sample`
|
||||
1. `code .`: Open the folder in VS Code
|
||||
1. Run `npm install` in terminal to install the dependencies
|
||||
1. Run the `Run Extension` target in the Debug View. This will:
|
||||
- Start a task `npm: watch` to compile the code
|
||||
- Run the extension in a new VS Code window
|
||||
1. Open a Jupyter Notebook and select a Python kernel and execute some code.
|
||||
1. Select the command `Jupyter Kernel API: Execute code against a Python Kernel`
|
||||
1. Select the a Kernel and then select the Code to execute.
|
||||
1. Watch the output panel for outputs returned by the kernel.
|
||||
|
||||
### Notes:
|
||||
|
||||
1. Make use of the `language` property of the kernel to ensure the language of the code matches the kernel.
|
||||
2. `getKernel` API can can return `undefined` if the user does not grant the extension access to the kernel.
|
||||
3. Access to kernels for each extension is managed via the command `Manage Access To Jupyter Kernels`.
|
||||
1794
jupyter-kernel-execution-sample/package-lock.json
generated
Normal file
1794
jupyter-kernel-execution-sample/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
53
jupyter-kernel-execution-sample/package.json
Normal file
53
jupyter-kernel-execution-sample/package.json
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "jupyter-kernel-execution-sample",
|
||||
"displayName": "Jupyter Kernel Execution Sample",
|
||||
"description": "Sample extension using Jupyter API to execute code against the Python Kernel",
|
||||
"publisher": "vscode-samples",
|
||||
"version": "0.0.1",
|
||||
"engines": {
|
||||
"vscode": "^1.82.0"
|
||||
},
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onNotebook:jupyter-notebook",
|
||||
"onNotebook:interactive"
|
||||
],
|
||||
"extensionDependencies": [
|
||||
"ms-toolsai.jupyter"
|
||||
],
|
||||
"main": "./out/extension.js",
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "jupyterKernelExecution.listKernels",
|
||||
"title": "Execute code against a Python Kernel",
|
||||
"category": "Jupyter Kernel API"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"commandPalette": [
|
||||
{
|
||||
"command": "jupyterKernelExecution.listKernels",
|
||||
"title": "Execute code against a Python Kernel"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "npm run compile",
|
||||
"compile": "tsc -b",
|
||||
"lint": "eslint src --ext ts",
|
||||
"watch": "tsc -b --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "14.x",
|
||||
"@types/vscode": "^1.82.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.0",
|
||||
"@typescript-eslint/parser": "^6.7.0",
|
||||
"@vscode/jupyter-extension": "^0.0.91",
|
||||
"eslint": "^7.27.0",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
}
|
||||
167
jupyter-kernel-execution-sample/src/extension.ts
Normal file
167
jupyter-kernel-execution-sample/src/extension.ts
Normal file
@ -0,0 +1,167 @@
|
||||
import {
|
||||
CancellationTokenSource,
|
||||
Disposable,
|
||||
ExtensionContext,
|
||||
NotebookCellOutputItem,
|
||||
OutputChannel,
|
||||
QuickPickItem,
|
||||
commands,
|
||||
extensions,
|
||||
window,
|
||||
workspace,
|
||||
} from 'vscode';
|
||||
import { Jupyter, Kernel } from '@vscode/jupyter-extension';
|
||||
import path = require('path');
|
||||
import { TextDecoder } from 'util';
|
||||
|
||||
export function activate(context: ExtensionContext) {
|
||||
const jupyterExt = extensions.getExtension<Jupyter>('ms-toolsai.jupyter');
|
||||
if (!jupyterExt) {
|
||||
throw new Error('Jupyter Extension not installed');
|
||||
}
|
||||
if (!jupyterExt.isActive) {
|
||||
jupyterExt.activate();
|
||||
}
|
||||
const output = window.createOutputChannel('Jupyter Kernel Execution');
|
||||
context.subscriptions.push(output);
|
||||
context.subscriptions.push(
|
||||
commands.registerCommand('jupyterKernelExecution.listKernels', async () => {
|
||||
const kernel = await selectKernel();
|
||||
if (!kernel) {
|
||||
return;
|
||||
}
|
||||
const code = await selectCodeToRunAgainstKernel();
|
||||
if (!code) {
|
||||
return;
|
||||
}
|
||||
await executeCode(kernel, code, output);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const ErrorMimeType = NotebookCellOutputItem.error(new Error('')).mime;
|
||||
const StdOutMimeType = NotebookCellOutputItem.stdout('').mime;
|
||||
const StdErrMimeType = NotebookCellOutputItem.stderr('').mime;
|
||||
const MarkdownMimeType = 'text/markdown';
|
||||
const HtmlMimeType = 'text/html';
|
||||
const textDecoder = new TextDecoder();
|
||||
async function executeCode(kernel: Kernel, code: string, logger: OutputChannel) {
|
||||
logger.show();
|
||||
logger.appendLine(`>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`);
|
||||
logger.appendLine(`Executing code against kernel ${code}`);
|
||||
const tokenSource = new CancellationTokenSource();
|
||||
try {
|
||||
for await (const outputs of kernel.executeCode(code, tokenSource.token)) {
|
||||
for (const output of outputs) {
|
||||
if (output.mime === ErrorMimeType) {
|
||||
const error = JSON.parse(textDecoder.decode(output.data)) as Error;
|
||||
logger.appendLine(
|
||||
`Error executing code ${error.name}: ${error.message},/n ${error.stack}`
|
||||
);
|
||||
} else {
|
||||
logger.appendLine(
|
||||
`${output.mime} Output: ${textDecoder.decode(output.data)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.appendLine('Code execution completed');
|
||||
logger.appendLine(`<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<`);
|
||||
} finally {
|
||||
tokenSource.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
const printHelloWorld = `print('Hello World')`;
|
||||
const throwAnError = `raise Exception('Hello World')`;
|
||||
const displayMarkdown = `from IPython.display import display, Markdown
|
||||
display(Markdown('*some markdown*'))`;
|
||||
const displayHtml = `from IPython.display import display, HTML
|
||||
display(HTML('<div>Hello World</div>'))`;
|
||||
const printToStdErr = `import sys
|
||||
print('Hello World', file=sys.stderr)`;
|
||||
const streamOutput = `import time
|
||||
for i in range(10):
|
||||
print(i)
|
||||
time.sleep(1)`;
|
||||
|
||||
const codeSnippets = new Map([
|
||||
['Print Hello World', printHelloWorld],
|
||||
['Stream Output', streamOutput],
|
||||
['Display Markdown', displayMarkdown],
|
||||
['Display HTML', displayHtml],
|
||||
['Print to StdErr', printToStdErr],
|
||||
['Throw an Error', throwAnError],
|
||||
]);
|
||||
|
||||
async function selectCodeToRunAgainstKernel() {
|
||||
const selection = await window.showQuickPick(Array.from(codeSnippets.keys()), {
|
||||
placeHolder: 'Select code to execute against the kernel',
|
||||
});
|
||||
if (!selection) {
|
||||
return;
|
||||
}
|
||||
return codeSnippets.get(selection);
|
||||
}
|
||||
|
||||
async function selectKernel(): Promise<Kernel | undefined> {
|
||||
const extension = extensions.getExtension<Jupyter>('ms-toolsai.jupyter');
|
||||
if (!extension) {
|
||||
throw new Error('Jupyter extension not installed');
|
||||
}
|
||||
await extension.activate();
|
||||
|
||||
if (workspace.notebookDocuments.length === 0) {
|
||||
window.showErrorMessage(
|
||||
'No notebooks open. Open a notebook, run a cell and then try this command'
|
||||
);
|
||||
return;
|
||||
}
|
||||
const toDispose: Disposable[] = [];
|
||||
|
||||
return new Promise<Kernel | undefined>((resolve) => {
|
||||
const quickPick = window.createQuickPick<QuickPickItem & { kernel: Kernel }>();
|
||||
toDispose.push(quickPick);
|
||||
const quickPickItems: (QuickPickItem & { kernel: Kernel })[] = [];
|
||||
quickPick.title = 'Select a Kernel';
|
||||
quickPick.placeholder = 'Select a Python Kernel to execute some code';
|
||||
quickPick.busy = true;
|
||||
quickPick.show();
|
||||
|
||||
const api = extension.exports;
|
||||
Promise.all(
|
||||
workspace.notebookDocuments.map(async (document) => {
|
||||
const kernel = await api.kernels.getKernel(document.uri);
|
||||
if (kernel && (kernel as any).language === 'python') {
|
||||
quickPickItems.push({
|
||||
label: `Kernel for ${path.basename(document.uri.fsPath)}`,
|
||||
kernel,
|
||||
});
|
||||
quickPick.items = quickPickItems;
|
||||
}
|
||||
})
|
||||
).finally(() => {
|
||||
quickPick.busy = false;
|
||||
if (quickPickItems.length === 0) {
|
||||
quickPick.hide();
|
||||
window.showErrorMessage(
|
||||
'No active kernels associated with any of the open notebooks, try opening a notebook and running a Python cell'
|
||||
);
|
||||
return resolve(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
quickPick.onDidAccept(
|
||||
() => {
|
||||
quickPick.hide();
|
||||
if (quickPick.selectedItems.length > 0) {
|
||||
return resolve(quickPick.selectedItems[0].kernel);
|
||||
}
|
||||
resolve(undefined);
|
||||
},
|
||||
undefined,
|
||||
toDispose
|
||||
);
|
||||
quickPick.onDidHide(() => resolve(undefined), undefined, toDispose);
|
||||
}).finally(() => Disposable.from(...toDispose).dispose());
|
||||
}
|
||||
17
jupyter-kernel-execution-sample/tsconfig.json
Normal file
17
jupyter-kernel-execution-sample/tsconfig.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2020",
|
||||
"lib": [
|
||||
"es2020"
|
||||
],
|
||||
"outDir": "out",
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"rootDir": "src"
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
".vscode-test"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user