Skip to content

Commit

Permalink
feat: when a widget fails, show a fallback error widget
Browse files Browse the repository at this point in the history
  • Loading branch information
maartenbreddels authored and SylvainCorlay committed Feb 4, 2021
1 parent 4cf9585 commit a4dff1f
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 3 deletions.
30 changes: 30 additions & 0 deletions packages/voila/src/errorwidget.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as widgets from '@jupyter-widgets/base';

// Create a Widget Model that captures an error object
export function createErrorWidget(error: string) : any {
class ErrorWidget extends widgets.DOMWidgetModel {
constructor(attributes?: any, options?: any) {
attributes = {
...attributes,
_view_name: 'ErrorWidgetView',
_view_module: 'voila-errorwidget',
_model_module_version: '1.0.0',
_view_module_version: '1.0.0',
failed_module: attributes._view_module,
failed_model_name: attributes._model_name,
error: error
};
super(attributes, options);
}
}
return ErrorWidget as any;
}

export class ErrorWidgetView extends widgets.DOMWidgetView {
render() {
const module = this.model.get('failed_module');
const name = this.model.get('failed_model_name');
const error = String(this.model.get('error').stack);
this.el.innerHTML = `Failed to load widget '${name}' from module '${module}', error:<pre>${error}</pre>`;
}
}
27 changes: 24 additions & 3 deletions packages/voila/src/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ import * as PhosphorCommands from '@phosphor/commands';
import * as PhosphorDomutils from '@phosphor/domutils';

import { MessageLoop } from '@phosphor/messaging';
import { Widget } from '@phosphor/widgets';

import { requireLoader } from './loader';
import { batchRateMap } from './utils';
import { Widget } from '@phosphor/widgets';
import * as errorwidget from './errorwidget';

if (typeof window !== 'undefined' && typeof window.define !== 'undefined') {
window.define('@jupyter-widgets/base', base);
Expand All @@ -53,6 +54,8 @@ if (typeof window !== 'undefined' && typeof window.define !== 'undefined') {
window.define('@phosphor/algorithm', PhosphorAlgorithm);
window.define('@phosphor/commands', PhosphorCommands);
window.define('@phosphor/domutils', PhosphorDomutils);

window.define('voila-errorwidget', errorwidget);
}

const WIDGET_MIMETYPE = 'application/vnd.jupyter.widget-view+json';
Expand Down Expand Up @@ -129,11 +132,24 @@ export class WidgetManager extends JupyterLabManager {
className: string,
moduleName: string,
moduleVersion: string
): Promise<any> {
try {
return await this._loadClass(className, moduleName, moduleVersion);
} catch (error) {
return errorwidget.createErrorWidget(error);
}
}

async _loadClass(
className: string,
moduleName: string,
moduleVersion: string
): Promise<any> {
if (
moduleName === '@jupyter-widgets/base' ||
moduleName === '@jupyter-widgets/controls' ||
moduleName === '@jupyter-widgets/output'
moduleName === '@jupyter-widgets/output' ||
moduleName === 'voila-errorwidget'
) {
return super.loadClass(className, moduleName, moduleVersion);
} else {
Expand All @@ -142,7 +158,7 @@ export class WidgetManager extends JupyterLabManager {
if (module[className]) {
return module[className];
} else {
return Promise.reject(
throw Error(
'Class ' +
className +
' not found in module ' +
Expand Down Expand Up @@ -175,6 +191,11 @@ export class WidgetManager extends JupyterLabManager {
version: output.OUTPUT_WIDGET_VERSION,
exports: output as any
});
this.register({
name: 'voila-errorwidget',
version: '1.0.0',
exports: errorwidget as any
});
}

async _build_models(): Promise<{ [key: string]: base.WidgetModel }> {
Expand Down

0 comments on commit a4dff1f

Please sign in to comment.