Skip to content

Commit

Permalink
0.4.x: Lazy loading of widgets packages, for ipywidgets 7 and 8 suppo…
Browse files Browse the repository at this point in the history
…rt (#1482)

* Support for widgets 7

* Bring back try/catch but make it verbose

* Lazy load CSS

* Custom widgets working

* Add comments

* Gitignore entry for yarn cache

* Linter

* Linter

* CI: Update node

* Remove ipyvolume from testing env

* Pin jupyter-client in ui-tests

* Remove interactive test

The output widget shows up properly, though the test fails to run half
the time due to jupyter_client

* Drop unused async keyword

* UI tests for ipywidgets 7

* Oups
  • Loading branch information
martinRenou committed Jul 17, 2024
1 parent 68f422f commit 5934c53
Show file tree
Hide file tree
Showing 11 changed files with 3,295 additions and 2,683 deletions.
15 changes: 14 additions & 1 deletion .github/workflows/ui-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ jobs:
strategy:
matrix:
python-version: [3.8]
node-version: [16.x]
node-version: [18.x]
ipywidgets: [8, 7]
fail-fast: false

steps:
Expand All @@ -18,6 +19,7 @@ jobs:
uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1

- name: Install dependencies
if: ${{ matrix.ipywidgets == 8 }}
run: |
python -m pip install -r requirements-visual-test.txt
python -m pip install ".[test]"
Expand All @@ -27,6 +29,17 @@ jobs:
cd ui-tests
jlpm install
- name: Install dependencies
if: ${{ matrix.ipywidgets == 7 }}
run: |
python -m pip install -r requirements-visual-test.txt
python -m pip install ".[test7]"
jlpm
jlpm build
jupyter labextension develop . --overwrite
cd ui-tests
jlpm install
- name: Launch Voila
run: |
# Mount a volume to overwrite the server configuration
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ lib
voila/labextension
tsconfig.tsbuildinfo

.yarn

ui-tests/playwright-report
ui-tests/test-results
ui-tests/benchmark-results
Expand Down
6 changes: 5 additions & 1 deletion packages/voila/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
"browserslist": ">0.8%, not ie 11, not op_mini all, not dead",
"dependencies": {
"@jupyter-widgets/base": "^6.0.5",
"@jupyter-widgets/base7": "npm:@jupyter-widgets/[email protected]",
"@jupyter-widgets/controls": "^5.0.6",
"@jupyter-widgets/controls7": "npm:@jupyter-widgets/[email protected]",
"@jupyter-widgets/jupyterlab-manager": "^5.0.8",
"@jupyterlab/application": "^3 || ^4",
"@jupyterlab/apputils": "^3 || ^4",
Expand All @@ -27,18 +29,20 @@
"@lumino/signaling": "^1.4.3 || ^2",
"@lumino/virtualdom": "^1.8.0 || ^2",
"@lumino/widgets": "^1.18.0 || ^2",
"jquery": "^3.7.0",
"mathjax-full": "^3.0.0"
},
"devDependencies": {
"@babel/core": "^7.2.2",
"@babel/preset-env": "^7.3.1",
"@types/node": "^20.14.10",
"babel-loader": "^8.0.5",
"css-loader": "^5.0.0",
"file-loader": "^4.0.0",
"npm-run-all": "^4.1.5",
"p-limit": "^2.2.2",
"style-loader": "^2.0.0",
"typescript": "~4.1.3",
"typescript": "^5",
"url-loader": "^1.0.0",
"webpack": "^5",
"webpack-cli": "^3.2.3"
Expand Down
68 changes: 53 additions & 15 deletions packages/voila/src/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,14 @@
* The full license is in the file LICENSE, distributed with this software. *
****************************************************************************/

import $ from 'jquery';
import 'jquery-ui/ui/widgets/slider';

import {
WidgetManager as JupyterLabManager,
WidgetRenderer
} from '@jupyter-widgets/jupyterlab-manager';

import { output } from '@jupyter-widgets/jupyterlab-manager';

import * as base from '@jupyter-widgets/base';
import * as controls from '@jupyter-widgets/controls';

import * as Application from '@jupyterlab/application';
import * as AppUtils from '@jupyterlab/apputils';
import * as CoreUtils from '@jupyterlab/coreutils';
Expand All @@ -40,10 +38,14 @@ import { Widget } from '@lumino/widgets';

import { requireLoader } from './loader';

function windowDefine(name: string, module: any) {
if (typeof window !== 'undefined' && typeof window.define !== 'undefined') {
window.define(name, module);
}
}

if (typeof window !== 'undefined' && typeof window.define !== 'undefined') {
window.define('@jupyter-widgets/base', base);
window.define('@jupyter-widgets/controls', controls);
window.define('@jupyter-widgets/output', output);
window.define('jquery', $);

window.define('@jupyterlab/application', Application);
window.define('@jupyterlab/apputils', AppUtils);
Expand Down Expand Up @@ -105,6 +107,7 @@ export class WidgetManager extends JupyterLabManager {
if (!viewtag?.parentElement) {
return;
}

try {
const widgetViewObject = JSON.parse(viewtag.innerHTML);
const { model_id } = widgetViewObject;
Expand All @@ -126,13 +129,14 @@ export class WidgetManager extends JupyterLabManager {
//
// This workaround may not be necessary anymore with templates that make use
// of progressive rendering.
console.error('Something went wrong', error);
}
});
}

async display_view(msg: any, view: any, options: any): Promise<Widget> {
if (options.el) {
LuminoWidget.Widget.attach(view.luminoWidget, options.el);
LuminoWidget.Widget.attach(view.luminoWidget || view.pWidget, options.el);
}
if (view.el) {
view.el.setAttribute('data-voila-jupyter-widget', '');
Expand Down Expand Up @@ -181,20 +185,54 @@ export class WidgetManager extends JupyterLabManager {
}

private _registerWidgets(): void {
// Lazy loading of either ipywidgets 7 or ipywidgets 8 widgets and CSS
// Depending on what is requested by the kernel, one or the other will load
// IPYWIDGETS 8
this.register({
name: '@jupyter-widgets/base',
version: base.JUPYTER_WIDGETS_VERSION,
exports: base as any
version: '2.0.0',
exports: () => {
const baseWidgets = require('@jupyter-widgets/base') as any;
windowDefine('@jupyter-widgets/base', baseWidgets);
return baseWidgets;
}
});
this.register({
name: '@jupyter-widgets/controls',
version: controls.JUPYTER_CONTROLS_VERSION,
exports: controls as any
version: '2.0.0',
exports: () => {
const controlsWidgets = require('@jupyter-widgets/controls') as any;
windowDefine('@jupyter-widgets/controls', controlsWidgets);
require('@jupyter-widgets/controls/css/widgets-base.css');
return controlsWidgets;
}
});
this.register({
name: '@jupyter-widgets/output',
version: output.OUTPUT_WIDGET_VERSION,
exports: output as any
version: '1.0.0',
exports: async () =>
(await require('@jupyter-widgets/jupyterlab-manager')).output as any
});

// IPYWIDGETS 7
this.register({
name: '@jupyter-widgets/base',
version: '1.2.0',
exports: () => {
const base7Widgets = require('@jupyter-widgets/base7') as any;
windowDefine('@jupyter-widgets/base', base7Widgets);
return base7Widgets;
}
});
this.register({
name: '@jupyter-widgets/controls',
version: '1.5.0',
exports: () => {
const controls7Widget = require('@jupyter-widgets/controls7') as any;
windowDefine('@jupyter-widgets/controls', controls7Widget);
require('@jupyter-widgets/controls7/css/widgets-base.css');
return controls7Widget;
}
});
}

Expand Down
2 changes: 0 additions & 2 deletions packages/voila/style/index.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
@import '@jupyter-widgets/controls/css/widgets-base.css';

@font-face /* 0 */ {
font-family: MJXZERO;
src: url('~mathjax-full/es5/output/chtml/fonts/woff-v2/MathJax_Zero.woff')
Expand Down
11 changes: 11 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,17 @@ test = [
"pytest",
"pytest-tornasync",
]
test7 = [
"notebook==6",
"ipywidgets==7.8.2",
"matplotlib",
"mock",
"numpy",
"pandas",
"papermill",
"pytest",
"pytest-tornasync",
]

[project.scripts]
voila = "voila.app:main"
Expand Down
1 change: 0 additions & 1 deletion requirements-visual-test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@ jupyterlab~=4.0
bqplot
scipy
ipympl==0.9.2
ipyvolume @ git+https://github.com/jtpio/ipyvolume@ipywidgets-8
# jupyterlab_miami_nights==0.3.2
2 changes: 1 addition & 1 deletion tsconfigbase.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@
"skipLibCheck": true,
"strictNullChecks": true,
"target": "es2017",
"types": []
"types": ["node"]
}
}
31 changes: 0 additions & 31 deletions ui-tests/tests/voila.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,24 +227,6 @@ test.describe('Voila performance Tests', () => {
expect(await page.screenshot()).toMatchSnapshot(`${notebookName}.png`);
});

test('Render and benchmark interactive.ipynb', async ({
page,
browserName
}, testInfo) => {
const notebookName = 'interactive';
const testFunction = async () => {
await page.goto(`/voila/render/${notebookName}.ipynb`);
await page.waitForSelector('div.widget-slider.widget-hslider');
await page.fill('div.widget-readout', '8.00');
await page.keyboard.down('Enter');
await page.fill('div.widget-readout >> text=0', '8.00');
await page.keyboard.down('Enter');
await page.mouse.click(0, 0);
};
await addBenchmarkToTest(notebookName, testFunction, testInfo, browserName);
expect(await page.screenshot()).toMatchSnapshot(`${notebookName}.png`);
});

test('Render and benchmark ipympl.ipynb', async ({
page,
browserName
Expand All @@ -258,19 +240,6 @@ test.describe('Voila performance Tests', () => {
expect(await page.screenshot()).toMatchSnapshot(`${notebookName}.png`);
});

test('Render and benchmark ipyvolume.ipynb', async ({
page,
browserName
}, testInfo) => {
const notebookName = 'ipyvolume';
const testFunction = async () => {
await page.goto(`/voila/render/${notebookName}.ipynb`);
await page.waitForSelector('canvas');
};
await addBenchmarkToTest(notebookName, testFunction, testInfo, browserName);
expect(await page.screenshot()).toMatchSnapshot(`${notebookName}.png`);
});

test('Benchmark the multiple widgets notebook', async ({
page,
browserName
Expand Down
2 changes: 0 additions & 2 deletions voila/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -649,8 +649,6 @@ def init_handlers(self) -> List:
])

if JUPYTER_SERVER_2:


handlers.extend(self.identity_provider.get_handlers())

if self.voila_configuration.preheat_kernel:
Expand Down
Loading

0 comments on commit 5934c53

Please sign in to comment.