diff --git a/.vscode/settings.json b/.vscode/settings.json index 450b8248..8cda1d87 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,4 +11,7 @@ "[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode", }, + "githubPullRequests.ignoredPullRequestBranches": [ + "main" + ], } \ No newline at end of file diff --git a/packages/react/src/components/output/Output.tsx b/packages/react/src/components/output/Output.tsx index 3dc2c8d7..10e88cdc 100644 --- a/packages/react/src/components/output/Output.tsx +++ b/packages/react/src/components/output/Output.tsx @@ -40,6 +40,7 @@ export type IOutputProps = { showEditor: boolean; showKernelProgressBar?: boolean; toolbarPosition: 'up' | 'middle' | 'none'; + notifyOnComplete? : boolean; }; export const Output = (props: IOutputProps) => { @@ -62,6 +63,7 @@ export const Output = (props: IOutputProps) => { showControl, showEditor, showKernelProgressBar = true, + notifyOnComplete = false, id: sourceId, toolbarPosition, } = props; @@ -121,7 +123,7 @@ export const Output = (props: IOutputProps) => { useEffect(() => { if (adapter) { if (autoRun) { - adapter.execute(code); + adapter.execute(code, notifyOnComplete); } } }, [adapter]); @@ -141,12 +143,12 @@ export const Output = (props: IOutputProps) => { const executeRequest = outputStore.getExecuteRequest(sourceId); useEffect(() => { if (adapter && executeRequest && executeRequest === id) { - adapter.execute(code); + adapter.execute(code, notifyOnComplete); } }, [executeRequest, adapter]); useEffect(() => { if (adapter && executeTrigger > 0) { - adapter.execute(code); + adapter.execute(code, notifyOnComplete); } }, [executeTrigger]); useEffect(() => { @@ -244,6 +246,7 @@ Output.defaultProps = { metadata: {}, }, ], + notifyOnComplete : false, toolbarPosition: 'up', } as Partial; diff --git a/packages/react/src/components/output/OutputAdapter.ts b/packages/react/src/components/output/OutputAdapter.ts index 36c50214..af94b8cb 100755 --- a/packages/react/src/components/output/OutputAdapter.ts +++ b/packages/react/src/components/output/OutputAdapter.ts @@ -5,6 +5,7 @@ */ import { IOutput } from '@jupyterlab/nbformat'; +import { JSONObject } from '@lumino/coreutils'; import { IOutputAreaModel, OutputArea, OutputAreaModel } from '@jupyterlab/outputarea'; import { IRenderMime, RenderMimeRegistry, standardRendererFactories } from '@jupyterlab/rendermime'; import { rendererFactory as jsonRendererFactory } from '@jupyterlab/json-extension'; @@ -79,10 +80,11 @@ export class OutputAdapter { this.initKernel(); } - public async execute(code: string) { + public async execute(code: string, notifyOnComplete? : boolean) { if (this._kernel) { this.clear(); - const done = execute(this._id, code, this._outputArea, this._kernel); + const metadata : JSONObject = {}; + const done = execute(this._id, code, this._outputArea, this._kernel, metadata, notifyOnComplete); await done; } } diff --git a/packages/react/src/components/output/OutputExecutor.ts b/packages/react/src/components/output/OutputExecutor.ts index 66d34531..05a4b831 100755 --- a/packages/react/src/components/output/OutputExecutor.ts +++ b/packages/react/src/components/output/OutputExecutor.ts @@ -17,7 +17,8 @@ export async function execute( code: string, output: OutputArea, kernel: Kernel, - metadata?: JSONObject + metadata?: JSONObject, + notifyOnComplete? : boolean ): Promise { // Override the default for `stop_on_error`. let stopOnError = true; @@ -33,6 +34,7 @@ export async function execute( { model: output.model, stopOnError, + notifyOnComplete : notifyOnComplete } ); const future = kernelExecutor!.future; diff --git a/packages/react/src/jupyter/kernel/Kernel.ts b/packages/react/src/jupyter/kernel/Kernel.ts index bfe5adff..25183f19 100755 --- a/packages/react/src/jupyter/kernel/Kernel.ts +++ b/packages/react/src/jupyter/kernel/Kernel.ts @@ -194,6 +194,7 @@ export class Kernel { stopOnError, storeHistory, allowStdin, + notifyOnComplete = false }: { model?: IOutputAreaModel; iopubMessageHooks?: IOPubMessageHook[]; @@ -202,12 +203,14 @@ export class Kernel { stopOnError?: boolean; storeHistory?: boolean; allowStdin?: boolean; + notifyOnComplete? : boolean } = {} ): KernelExecutor | undefined { if (this._kernelConnection) { const kernelExecutor = new KernelExecutor({ connection: this._kernelConnection, model, + notifyOnComplete, }); kernelExecutor.execute(code, { iopubMessageHooks, diff --git a/packages/react/src/jupyter/kernel/KernelExecutor.ts b/packages/react/src/jupyter/kernel/KernelExecutor.ts index 3bbe6461..334933a1 100644 --- a/packages/react/src/jupyter/kernel/KernelExecutor.ts +++ b/packages/react/src/jupyter/kernel/KernelExecutor.ts @@ -41,6 +41,11 @@ export type IKernelExecutorOptions = { * Outputs model to populate with the execution results. */ model?: IOutputAreaModel; + /** + * Flag defining if notification about model changes + * must only be made when execution complete + */ + notifyOnComplete? : boolean; } export class KernelExecutor { @@ -54,13 +59,15 @@ export class KernelExecutor { private _outputsChanged = new Signal(this); private _future?: JupyterKernel.IFuture; private _shellMessageHooks = new Array(); + private _notifyOnComplete : boolean = false; - public constructor({ connection, model }: IKernelExecutorOptions) { + public constructor({ connection, model, notifyOnComplete }: IKernelExecutorOptions) { this._executed = new PromiseDelegate(); this._kernelConnection = connection; this._model = model ?? new OutputAreaModel(); this._outputs = []; this._kernelState = kernelsStore.getState(); + this._notifyOnComplete = !!notifyOnComplete; } /** @@ -118,6 +125,10 @@ export class KernelExecutor { // Wait for future to be done before resolving the exectud promise. this._future.done.then(() => { kernelsStore.getState().setExecutionPhase(this._kernelConnection.id, ExecutionPhase.completed); + // We emit model changes only when execution completed + if (this._notifyOnComplete) { + this._modelChanged.emit(this._model); + } this._executed.resolve(this._model); }); return this._executed.promise; @@ -205,13 +216,17 @@ export class KernelExecutor { this._outputs.push(message.content as IExecuteResult); this._outputsChanged.emit(this._outputs); this._model.add(output); - this._modelChanged.emit(this._model); + if (!this._notifyOnComplete) { + this._modelChanged.emit(this._model); + } break; case 'display_data': this._outputs.push(message.content as IDisplayData); this._outputsChanged.emit(this._outputs); this._model.add(output); - this._modelChanged.emit(this._model); + if (!this._notifyOnComplete) { + this._modelChanged.emit(this._model); + } break; case 'stream': case 'error': @@ -232,7 +247,9 @@ export class KernelExecutor { this._outputsChanged.emit(this._outputs); // FIXME this needs more advanced analysis see OutputArea this._model.add(output); - this._modelChanged.emit(this._model); + if (!this._notifyOnComplete) { + this._modelChanged.emit(this._model); + } break; case 'status': const executionState = (message.content as any).execution_state as KernelMessage.Status;