Skip to content

Commit c2c1f8d

Browse files
sok82Sergey Kadnikov
andauthored
Fix for uncaught in promise Runitme exception when executing invalid code (#305)
* Added notifyOnComplete paramater for code execution enabling to send model notifications only when all code execution completed * Fixed missing metadata parameter for OutputExecutor * Fixed JSONObject initialization * Fixed uncaught runtime exception in promise when code execution error occurs in Output * Fix for uncaught runtime exception in promise while executing invalid code --------- Co-authored-by: Sergey Kadnikov <skadnikov@seeneco.ru>
1 parent 64c873f commit c2c1f8d

File tree

4 files changed

+84
-38
lines changed

4 files changed

+84
-38
lines changed

packages/react/src/components/output/OutputAdapter.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,10 @@ export class OutputAdapter {
105105
this._outputArea,
106106
this._kernel,
107107
metadata,
108-
notifyOnComplete
108+
notifyOnComplete,
109+
onCodeExecutionError
109110
);
110-
if (onCodeExecutionError) {
111-
await done.catch(onCodeExecutionError);
112-
} else {
113-
await done.catch(err => {
114-
console.error(err);
115-
});
116-
}
111+
await done;
117112
}
118113
}
119114

packages/react/src/components/output/OutputExecutor.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ export async function execute(
1818
output: OutputArea,
1919
kernel: Kernel,
2020
metadata?: JSONObject,
21-
notifyOnComplete?: boolean
21+
notifyOnComplete?: boolean,
22+
onCodeExecutionError?: (err: any) => void
2223
): Promise<KernelMessage.IExecuteReplyMsg | undefined> {
2324
// Override the default for `stop_on_error`.
2425
let stopOnError = true;
@@ -32,7 +33,8 @@ export async function execute(
3233
const kernelExecutor = kernel.execute(code, {
3334
model: output.model,
3435
stopOnError,
35-
notifyOnComplete: notifyOnComplete,
36+
notifyOnComplete,
37+
onCodeExecutionError,
3638
});
3739
const future = kernelExecutor!.future;
3840
// TODO fix in upstream jupyterlab if possible...

packages/react/src/jupyter/kernel/Kernel.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,8 @@ export class Kernel {
194194
stopOnError,
195195
storeHistory,
196196
allowStdin,
197-
notifyOnComplete = false
197+
notifyOnComplete = false,
198+
onCodeExecutionError,
198199
}: {
199200
model?: IOutputAreaModel;
200201
iopubMessageHooks?: IOPubMessageHook[];
@@ -203,7 +204,8 @@ export class Kernel {
203204
stopOnError?: boolean;
204205
storeHistory?: boolean;
205206
allowStdin?: boolean;
206-
notifyOnComplete? : boolean
207+
notifyOnComplete?: boolean;
208+
onCodeExecutionError?: (err: any) => void;
207209
} = {}
208210
): KernelExecutor | undefined {
209211
if (this._kernelConnection) {

packages/react/src/jupyter/kernel/KernelExecutor.ts

Lines changed: 73 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,13 @@ export type IKernelExecutorOptions = {
4545
* Flag defining if notification about model changes
4646
* must only be made when execution complete
4747
*/
48-
notifyOnComplete? : boolean;
49-
}
48+
notifyOnComplete?: boolean;
49+
/**
50+
* Handler for executed code errors
51+
* @param err Erro data
52+
*/
53+
onCodeExecutionError?: (err: any) => void;
54+
};
5055

5156
export class KernelExecutor {
5257
private _executed: PromiseDelegate<IOutputAreaModel>;
@@ -57,17 +62,27 @@ export class KernelExecutor {
5762
private _outputs: IOutput[];
5863
private _stopOnError: boolean;
5964
private _outputsChanged = new Signal<KernelExecutor, IOutput[]>(this);
60-
private _future?: JupyterKernel.IFuture<KernelMessage.IExecuteRequestMsg, KernelMessage.IExecuteReplyMsg>;
65+
private _future?: JupyterKernel.IFuture<
66+
KernelMessage.IExecuteRequestMsg,
67+
KernelMessage.IExecuteReplyMsg
68+
>;
6169
private _shellMessageHooks = new Array<ShellMessageHook>();
62-
private _notifyOnComplete : boolean = false;
70+
private _notifyOnComplete: boolean = false;
71+
private _onCodeExecutionError?: (err: any) => void;
6372

64-
public constructor({ connection, model, notifyOnComplete }: IKernelExecutorOptions) {
73+
public constructor({
74+
connection,
75+
model,
76+
notifyOnComplete,
77+
onCodeExecutionError,
78+
}: IKernelExecutorOptions) {
6579
this._executed = new PromiseDelegate<IOutputAreaModel>();
6680
this._kernelConnection = connection;
6781
this._model = model ?? new OutputAreaModel();
6882
this._outputs = [];
6983
this._kernelState = kernelsStore.getState();
7084
this._notifyOnComplete = !!notifyOnComplete;
85+
this._onCodeExecutionError = onCodeExecutionError;
7186
}
7287

7388
/**
@@ -105,7 +120,9 @@ export class KernelExecutor {
105120
): Promise<IOutputAreaModel> {
106121
this._stopOnError = stopOnError;
107122
this._shellMessageHooks = shellMessageHooks;
108-
kernelsStore.getState().setExecutionPhase(this._kernelConnection.id, ExecutionPhase.running);
123+
kernelsStore
124+
.getState()
125+
.setExecutionPhase(this._kernelConnection.id, ExecutionPhase.running);
109126
this._future = this._kernelConnection.requestExecute({
110127
code,
111128
allow_stdin: allowStdin,
@@ -124,11 +141,13 @@ export class KernelExecutor {
124141
};
125142
// Wait for future to be done before resolving the exectud promise.
126143
this._future.done.then(() => {
127-
kernelsStore.getState().setExecutionPhase(this._kernelConnection.id, ExecutionPhase.completed);
144+
kernelsStore
145+
.getState()
146+
.setExecutionPhase(this._kernelConnection.id, ExecutionPhase.completed);
128147
// We emit model changes only when execution completed
129148
if (this._notifyOnComplete) {
130149
this._modelChanged.emit(this._model);
131-
}
150+
}
132151
this._executed.resolve(this._model);
133152
});
134153
return this._executed.promise;
@@ -148,31 +167,47 @@ export class KernelExecutor {
148167
};
149168

150169
/**
151-
*
170+
*
152171
*/
153-
get future(): JupyterKernel.IFuture<
154-
KernelMessage.IExecuteRequestMsg,
155-
KernelMessage.IExecuteReplyMsg
156-
> | undefined {
172+
get future():
173+
| JupyterKernel.IFuture<
174+
KernelMessage.IExecuteRequestMsg,
175+
KernelMessage.IExecuteReplyMsg
176+
>
177+
| undefined {
157178
return this._future;
158179
}
159180

160181
/**
161182
* Promise that resolves when the execution is done.
162183
*/
163184
get done(): Promise<void> {
164-
return this._executed.promise.then(() => {
165-
return;
166-
});
185+
return this._executed.promise
186+
.then(() => {
187+
return;
188+
})
189+
.catch(err =>
190+
this._onCodeExecutionError
191+
? this._onCodeExecutionError(err)
192+
: console.error(err)
193+
);
167194
}
168195

169196
/**
170197
* Code execution result as serialized JSON
171198
*/
172199
get result(): Promise<string> {
173-
return this._executed.promise.then(model => {
174-
return outputsAsString(model.toJSON());
175-
});
200+
return this._executed.promise
201+
.then(model => {
202+
console.log('---', model);
203+
return outputsAsString(model.toJSON());
204+
})
205+
.catch(err => {
206+
this._onCodeExecutionError
207+
? this._onCodeExecutionError(err)
208+
: console.error(err);
209+
return '';
210+
});
176211
}
177212

178213
/**
@@ -192,7 +227,8 @@ export class KernelExecutor {
192227
/**
193228
* Signal emitted when the outputs list changes.
194229
*/
195-
get outputsChanged(): ISignal<KernelExecutor, IOutput[]> {0
230+
get outputsChanged(): ISignal<KernelExecutor, IOutput[]> {
231+
0;
196232
return this._outputsChanged;
197233
}
198234

@@ -234,7 +270,12 @@ export class KernelExecutor {
234270
this._model.add(output);
235271
this._modelChanged.emit(this._model);
236272
if (this._stopOnError) {
237-
kernelsStore.getState().setExecutionPhase(this._kernelConnection.id, ExecutionPhase.completed_with_error);
273+
kernelsStore
274+
.getState()
275+
.setExecutionPhase(
276+
this._kernelConnection.id,
277+
ExecutionPhase.completed_with_error
278+
);
238279
}
239280
break;
240281
case 'clear_output':
@@ -251,10 +292,14 @@ export class KernelExecutor {
251292
}
252293
break;
253294
case 'status':
254-
const executionState = (message.content as any).execution_state as KernelMessage.Status;
295+
const executionState = (message.content as any)
296+
.execution_state as KernelMessage.Status;
255297
const connectionStatus = this._kernelConnection.connectionStatus;
256298
const kernelState = toKernelState(connectionStatus!, executionState);
257-
this._kernelState.setExecutionState(this._kernelConnection.id, kernelState);
299+
this._kernelState.setExecutionState(
300+
this._kernelConnection.id,
301+
kernelState
302+
);
258303
break;
259304
default:
260305
break;
@@ -275,8 +320,11 @@ export class KernelExecutor {
275320
break;
276321
case 'error':
277322
{
278-
const { ename, evalue, traceback } = content as KernelMessage.IReplyErrorContent;
279-
this._executed.reject(`${ename}: ${evalue}\n${(traceback ?? []).join('\n')}`);
323+
const { ename, evalue, traceback } =
324+
content as KernelMessage.IReplyErrorContent;
325+
this._executed.reject(
326+
`${ename}: ${evalue}\n${(traceback ?? []).join('\n')}`
327+
);
280328
}
281329
break;
282330
}
@@ -301,7 +349,6 @@ export class KernelExecutor {
301349
}
302350
}
303351
};
304-
305352
}
306353

307354
export default KernelExecutor;

0 commit comments

Comments
 (0)