| | import { |
| | CancellationTokenSource, |
| | Disposable, |
| | ExtensionContext, |
| | NotebookCellOutputItem, |
| | OutputChannel, |
| | QuickPickItem, |
| | commands, |
| | extensions, |
| | window, |
| | workspace, |
| | } from 'vscode'; |
| | import { Jupyter, Kernel } from '@vscode/jupyter-extension'; |
| | import * as path from '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 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 output of kernel.executeCode(code, tokenSource.token)) { |
| | for (const outputItem of output.items) { |
| | if (outputItem.mime === ErrorMimeType) { |
| | const error = JSON.parse(textDecoder.decode(outputItem.data)) as Error; |
| | logger.appendLine( |
| | `Error executing code ${error.name}: ${error.message},/n ${error.stack}` |
| | ); |
| | } else { |
| | logger.appendLine( |
| | `${outputItem.mime} Output: ${textDecoder.decode(outputItem.data)}` |
| | ); |
| | } |
| | } |
| | } |
| | logger.appendLine('Code execution completed'); |
| | logger.appendLine(`<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<`); |
| | } catch (ex) { |
| | logger.appendLine(`Code execution failed with an error '${ex}'`); |
| | 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.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()); |
| | } |
| |
|