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 committed Aug 3, 2020
1 parent bfa0e8b commit 9187a37
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 10 deletions.
32 changes: 32 additions & 0 deletions js/src/errorwidget.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as widgets from "@jupyter-widgets/base";

// create a Widget Model that captures an error object
export
function createErrorWidget(error) {
class ErrorWidget extends widgets.DOMWidgetModel {
constructor(attributes, options) {
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);
console.log(attributes);
}
}
return ErrorWidget;
}

export
class ErrorWidgetView extends widgets.DOMWidgetView {
render() {
console.log('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>`;
}
}
43 changes: 33 additions & 10 deletions js/src/manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { MessageLoop } from '@phosphor/messaging';

import { requireLoader } from './loader';
import { batchRateMap } from './utils';
import * as errorwidget from './errorwidget';

if (typeof window !== "undefined" && typeof window.define !== "undefined") {
window.define("@jupyter-widgets/base", base);
Expand All @@ -49,6 +50,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 @@ -109,20 +112,35 @@ export class WidgetManager extends JupyterLabManager {
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);
return new Promise((resolve) => {
super.loadClass(className, moduleName, moduleVersion).then((cls) => {
resolve(cls)
}, (err) => {
// If we failed, fallback to the buildin error widget
resolve(errorwidget.createErrorWidget(err));
});
});
}
else {
// TODO: code duplicate from HTMLWidgetManager, consider a refactor
return this.loader(moduleName, moduleVersion).then((module) => {
if (module[className]) {
return module[className];
}
else {
return Promise.reject("Class " + className + " not found in module " + moduleName + "@" + moduleVersion);
}
})
return new Promise((resolve) => {
this.loader(moduleName, moduleVersion).then((module) => {
if (module[className]) {
resolve(module[className]);
}
else {
// If we failed to find the widget, fallback to the buildin error widget
const error = new Error("Class " + className + " not found in module " + moduleName + "@" + moduleVersion);
resolve(errorwidget.createErrorWidget(error));
}
}, (error) => {
// If we failed to load the module, fallback to the buildin error widget
resolve(errorwidget.createErrorWidget(error));
})
});
}
}

Expand All @@ -145,6 +163,11 @@ export class WidgetManager extends JupyterLabManager {
version: output.OUTPUT_WIDGET_VERSION,
exports: output
});
this.register({
name: 'voila-errorwidget',
version: '1.0.0',
exports: errorwidget
});
}

async _build_models() {
Expand Down

0 comments on commit 9187a37

Please sign in to comment.