Skip to content

Commit

Permalink
Figure out from the server the widgets version that should be injected
Browse files Browse the repository at this point in the history
This has the downside of not letting the kernel decide the widgets
version, which is a regression from Voila 0.4.x. But that should be
fine.
  • Loading branch information
martinRenou committed Oct 16, 2024
1 parent 23fed69 commit f38e2b9
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 113 deletions.
49 changes: 2 additions & 47 deletions packages/voila/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import {
IFederatedExtensionData,
activePlugins,
createModule,
isIpywidgets7extension,
loadComponent,
shouldUseMathJax2
} from './tools';
Expand Down Expand Up @@ -75,52 +74,7 @@ async function main() {
})
);

console.log('lab extensions!', extensions);

// Extract out @voila-dashboards/widget-manager packages
// we'll include them back later depending on the requested version
const widgetsManager7Extension = extensions.splice(
extensions.findIndex(
(ext) =>
ext.status === 'fulfilled' &&
ext.value.name === '@voila-dashboards/widgets-manager7'
),
1
)[0];
const widgetsManager8Extension = extensions.splice(
extensions.findIndex(
(ext) =>
ext.status === 'fulfilled' &&
ext.value.name === '@voila-dashboards/widgets-manager8'
),
1
)[0];
const officialWidgetsManagerExtension = extensions.splice(
extensions.findIndex(
(ext) =>
ext.status === 'fulfilled' &&
ext.value.name === '@jupyter-widgets/jupyterlab-manager'
),
1
)[0];
// Load @jupyter-widgets/jupyterlab-manager if it's there, and spot if it's widgets 7 or 8
if (
officialWidgetsManagerExtension &&
officialWidgetsManagerExtension.status === 'fulfilled'
) {
const ext = officialWidgetsManagerExtension.value;

if (ext.extension) {
const module = await createModule(ext.name, ext.extension);
if (isIpywidgets7extension(module)) {
extensions.push(widgetsManager7Extension);
} else {
extensions.push(widgetsManager8Extension);
// Also bring back the official extension which registers the widgets
extensions.push(officialWidgetsManagerExtension);
}
}
}
console.log('labextensions data', extensionData, extensions);

extensions.forEach((p) => {
if (p.status === 'rejected') {
Expand Down Expand Up @@ -187,6 +141,7 @@ async function main() {
});
app.registerPluginModules(mods);
await app.start();
console.log('__webpack_share_scopes__.default', __webpack_share_scopes__.default);
window.jupyterapp = app;
}

Expand Down
92 changes: 48 additions & 44 deletions packages/voila/src/plugins/outputs/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,56 +35,60 @@ export const renderOutputsPlugin: JupyterFrontEndPlugin<void> = {
app: JupyterFrontEnd,
rendermime: IRenderMimeRegistry
): Promise<void> => {
// TODO: Typeset a fake element to get MathJax loaded, remove this hack once
// MathJax 2 is removed.
await rendermime.latexTypesetter?.typeset(document.createElement('div'));

// Render latex in markdown cells
const mdOutput = document.body.querySelectorAll('div.jp-MarkdownOutput');
mdOutput.forEach((md) => {
rendermime.latexTypesetter?.typeset(md as HTMLElement);
});
// Render code cell
const cellOutputs = document.body.querySelectorAll(
'script[type="application/vnd.voila.cell-output+json"]'
);
cellOutputs.forEach(async (cellOutput) => {
const model = JSON.parse(cellOutput.innerHTML);

const mimeType = rendermime.preferredMimeType(model.data, 'any');

if (!mimeType) {
return null;
}
const output = rendermime.createRenderer(mimeType);
output.renderModel(model).catch((error) => {
// Manually append error message to output
const pre = document.createElement('pre');
pre.textContent = `Javascript Error: ${error.message}`;
output.node.appendChild(pre);

// Remove mime-type-specific CSS classes
pre.className = 'lm-Widget jp-RenderedText';
pre.setAttribute('data-mime-type', 'application/vnd.jupyter.stderr');
app.started.then(() => {
// TODO: Typeset a fake element to get MathJax loaded, remove this hack once
// MathJax 2 is removed.
rendermime.latexTypesetter?.typeset(document.createElement('div'));

// Render latex in markdown cells
const mdOutput = document.body.querySelectorAll('div.jp-MarkdownOutput');
mdOutput.forEach((md) => {
rendermime.latexTypesetter?.typeset(md as HTMLElement);
});
// Render code cell
const cellOutputs = document.body.querySelectorAll(
'script[type="application/vnd.voila.cell-output+json"]'
);
cellOutputs.forEach(async (cellOutput) => {
const model = JSON.parse(cellOutput.innerHTML);

const mimeType = rendermime.preferredMimeType(model.data, 'any');
console.log('mimetype', mimeType, rendermime);

output.addClass('jp-OutputArea-output');
if (!mimeType) {
return null;
}
const output = rendermime.createRenderer(mimeType);
console.log('rendering model', model);
output.renderModel(model).catch((error) => {
// Manually append error message to output
const pre = document.createElement('pre');
pre.textContent = `Javascript Error: ${error.message}`;
output.node.appendChild(pre);

if (cellOutput.parentElement) {
const container = cellOutput.parentElement;
// Remove mime-type-specific CSS classes
pre.className = 'lm-Widget jp-RenderedText';
pre.setAttribute('data-mime-type', 'application/vnd.jupyter.stderr');
});

container.removeChild(cellOutput);
output.addClass('jp-OutputArea-output');

// Attach output
Widget.attach(output, container);
}
});
if (cellOutput.parentElement) {
const container = cellOutput.parentElement;

const node = document.getElementById('rendered_cells');
if (node) {
const cells = new RenderedCells({ node });
app.shell.add(cells, 'main');
}
container.removeChild(cellOutput);

// Attach output
Widget.attach(output, container);
}
});

const node = document.getElementById('rendered_cells');
if (node) {
const cells = new RenderedCells({ node });
app.shell.add(cells, 'main');
}
})
}
};

Expand Down
21 changes: 0 additions & 21 deletions packages/voila/src/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,27 +38,6 @@ export async function createModule(
}
}

export function isIpywidgets7extension(extension: any) {
// Handle commonjs or es2015 modules
let exports;
if (Object.prototype.hasOwnProperty.call(extension, '__esModule')) {
exports = extension.default;
} else {
// CommonJS exports.
exports = extension;
}

const plugins = Array.isArray(exports) ? exports : [exports];
const pluginIds = plugins.map((plugin) => {
return plugin.id;
});

return (
pluginIds.includes('@jupyter-widgets/jupyterlab-manager:plugin') &&
pluginIds.length === 1
);
}

/**
* Iterate over active plugins in an extension.
*
Expand Down
1 change: 1 addition & 0 deletions packages/widgets_manager7/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ const widgetManager: JupyterFrontEndPlugin<IJupyterWidgetRegistry> = {
);
(app as any).widgetManager = manager;

console.log('manager promise!', manager);
rendermime.removeMimeType(WIDGET_MIMETYPE);
rendermime.addFactory(
{
Expand Down
5 changes: 5 additions & 0 deletions packages/widgets_manager7/src/manager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { WidgetModel } from '@jupyter-widgets/base';
import { WidgetManager } from '@jupyter-widgets/jupyterlab-manager';
import { ISignal, Signal } from '@lumino/signaling';
import { INotebookModel } from '@jupyterlab/notebook';

export class VoilaWidgetManager extends WidgetManager {
register_model(model_id: string, modelPromise: Promise<WidgetModel>): void {
Expand All @@ -21,6 +22,10 @@ export class VoilaWidgetManager extends WidgetManager {
this._registeredModels.delete(modelId);
}

restoreWidgets(notebook: INotebookModel): Promise<void> {
return Promise.resolve();
}

private _modelRegistered = new Signal<VoilaWidgetManager, string>(this);
private _registeredModels = new Set<string>();
}
33 changes: 32 additions & 1 deletion voila/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@
from copy import deepcopy
from functools import partial
from pathlib import Path
from packaging.version import Version
from typing import Awaitable, Dict, List

import websockets
from jupyter_core.paths import jupyter_path
from jupyter_server.config_manager import recursive_update
from jupyter_server.utils import url_path_join
from jupyterlab_server.config import get_page_config as gpc
from jupyterlab_server.config import get_federated_extensions
from markupsafe import Markup

from ._version import __version__
Expand Down Expand Up @@ -137,16 +139,45 @@ def get_page_config(base_url, settings, log, voila_configuration: VoilaConfigura
required_extensions = []
federated_extensions = deepcopy(page_config["federated_extensions"])

page_config["federated_extensions"] = filter_extension(
filtered_extensions = filter_extension(
federated_extensions=federated_extensions,
disabled_extensions=disabled_extensions,
required_extensions=required_extensions,
extension_allowlist=voila_configuration.extension_allowlist,
extension_denylist=voila_configuration.extension_denylist,
)

extensions = maybe_inject_widgets_manager_extension(filtered_extensions, labextensions_path)

page_config["federated_extensions"] = extensions
return page_config


def maybe_inject_widgets_manager_extension(federated_extensions: List[Dict], labextensions_path: List[str]):
"""If the @jupyter-widgets/jupyterlab-manager is installed on the server. Inject our own manager."""
labextensions = get_federated_extensions(labextensions_path)

if '@jupyter-widgets/jupyterlab-manager' not in labextensions:
return federated_extensions

widgets_version = labextensions['@jupyter-widgets/jupyterlab-manager']['version']

if Version(widgets_version) >= Version('5.0.0'):
# ipywidgets 8 or more, remove widgets-manager7
return [
x
for x in federated_extensions
if x["name"] != '@voila-dashboards/widgets-manager7'
]
else:
# ipywidgets 7, remove widgets-manager8
return [
x
for x in federated_extensions
if x["name"] != '@voila-dashboards/widgets-manager8'
]


def filter_extension(
federated_extensions: List[Dict],
disabled_extensions: List[str] = [],
Expand Down

0 comments on commit f38e2b9

Please sign in to comment.