Skip to content

Commit ca2bb89

Browse files
andrii-idlqqqpre-commit-ci[bot]
authored
[1.1.x] Make server extension verification call during extension startup non-blocking (#480) (#486)
* [1.x] Make server extension verification call during extension startup non-blocking (#480) (#481) * Make server extension verification call during extension startup non-blocking (#480) * remove async call, make call sync, refactor into a separate function * don't use abbreviations in the verifyServerExtension function or type naming * Update src/index.tsx Co-authored-by: david qiu <david@qiu.dev> * remove unused type * add @jupyterlab/rendermime-interfaces as a dependency * Remove async from activatePlugin function declaration (makes it not async) * set @jupyterlab/rendermime-interfaces version to ^3.8.0 to support all JLab >= 4 --------- Co-authored-by: david qiu <david@qiu.dev> * Fix translator usage, remove @jupyterlab/rendermime-interfaces dependency (#483) * update snapshots * use lowercase "es" is "es2017" as is convention in Project Jupyter * fix 1.x dependencies * fix package manifest to be compatible with JL3 * Make ErrorBoundary return JSX.Element to avoid type conflicts --------- Co-authored-by: david qiu <david@qiu.dev> * add pytest-cov dependency to pyproject.toml * update pre-commit * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: david qiu <david@qiu.dev> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 10d45a3 commit ca2bb89

11 files changed

+2617
-4059
lines changed

.gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ node_modules/
88
*.tsbuildinfo
99
jupyter_scheduler/labextension
1010

11+
# Yarn 3 cache
12+
.yarn
13+
14+
# Pytest files
15+
coverage
16+
1117
# Integration tests
1218
ui-tests/test-results/
1319
ui-tests/playwright-report/

.pre-commit-config.yaml

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/pre-commit/pre-commit-hooks
3-
rev: v4.3.0
3+
rev: v4.5.0
44
hooks:
55
- id: end-of-file-fixer
66
- id: check-case-conflict
@@ -16,25 +16,25 @@ repos:
1616
- id: trailing-whitespace
1717

1818
- repo: https://github.com/psf/black
19-
rev: 22.10.0
19+
rev: 24.2.0
2020
hooks:
2121
- id: black
2222

2323
- repo: https://github.com/PyCQA/isort
24-
rev: 5.10.1
24+
rev: 5.13.2
2525
hooks:
2626
- id: isort
2727
args: ["--profile", "black"]
2828
files: \.py$
2929

3030
- repo: https://github.com/asottile/pyupgrade
31-
rev: v3.2.0
31+
rev: v3.15.0
3232
hooks:
3333
- id: pyupgrade
3434
args: [--py37-plus]
3535

3636
- repo: https://github.com/pycqa/flake8
37-
rev: 5.0.4
37+
rev: 7.0.0
3838
hooks:
3939
- id: flake8
4040
additional_dependencies:
@@ -46,7 +46,7 @@ repos:
4646
stages: [manual]
4747

4848
- repo: https://github.com/sirosen/check-jsonschema
49-
rev: 0.18.4
49+
rev: 0.28.0
5050
hooks:
5151
- id: check-jsonschema
5252
name: "Check GitHub Workflows"

jupyter_scheduler/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Scheduling API for JupyterLab"""
2+
23
import json
34
from pathlib import Path
45

package.json

+9-7
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,17 @@
6262
"@jupyterlab/docmanager-extension": "^3.4.2",
6363
"@jupyterlab/filebrowser": "^3.4.1",
6464
"@jupyterlab/launcher": "^3.4.4",
65+
"@jupyterlab/rendermime-interfaces": "^3",
6566
"@jupyterlab/services": "^6.4.2",
6667
"@jupyterlab/translation": "^3.4.1",
6768
"@jupyterlab/ui-components": "^3.4.2",
68-
"@lumino/polling": "^1.9.0",
69-
"@lumino/signaling": "^1.10.0",
70-
"@lumino/coreutils": "^1.12.0",
71-
"@lumino/widgets": "^1.32.0",
69+
"@lumino/coreutils": "^1",
70+
"@lumino/polling": "^1",
71+
"@lumino/signaling": "^1",
72+
"@lumino/widgets": "^1",
7273
"@mui/icons-material": "^5.10.9",
7374
"@mui/material": "^5.10.6",
7475
"@mui/system": "^5.10.6",
75-
"@types/react-dom": "^18.0.5",
7676
"cronstrue": "^2.12.0",
7777
"react": "^17.0.1",
7878
"react-dom": "^17.0.1",
@@ -93,6 +93,8 @@
9393
"mkdirp": "^1.0.3",
9494
"npm-run-all": "^4.1.5",
9595
"prettier": "^2.1.1",
96+
"@types/react": "^17.0.1",
97+
"@types/react-dom": "^17.0.1",
9698
"rimraf": "^3.0.2",
9799
"stylelint": "^14.3.0",
98100
"stylelint-config-prettier": "^9.0.3",
@@ -103,8 +105,8 @@
103105
"typescript": "~4.1.3"
104106
},
105107
"resolutions": {
106-
"@types/react": "^17.0.1",
107-
"@types/react-dom": "^18.0.5"
108+
"@lumino/coreutils": "^1",
109+
"@jupyterlab/rendermime-interfaces": "3.0.0 - 3.6.7"
108110
},
109111
"sideEffects": [
110112
"style/*.css",

pyproject.toml

+3-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ dependencies = [
3131
"jupyter_server>=1.6,<3",
3232
"traitlets",
3333
"nbconvert",
34-
"pydantic",
34+
"pydantic~=1.10",
3535
"sqlalchemy<2",
3636
"croniter",
3737
"pytz",
@@ -43,7 +43,8 @@ dependencies = [
4343
[project.optional-dependencies]
4444
test = [
4545
"pytest",
46-
"jupyter_server[test]>=1.6,<3"
46+
"pytest-cov",
47+
"jupyter_server[test]>=1.6,<3",
4748
]
4849
dev = [
4950
"click"

src/components/error-boundary.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export class ErrorBoundary extends React.Component<
2727
this.setState({ hasError: true, error });
2828
}
2929

30-
render(): React.ReactNode {
30+
render(): JSX.Element {
3131
let errorDetail;
3232
if (typeof this.state.error === 'string') {
3333
errorDetail = this.state.error;
@@ -53,6 +53,6 @@ export class ErrorBoundary extends React.Component<
5353
</div>
5454
);
5555
}
56-
return this.props.children;
56+
return <> {this.props.children} </>;
5757
}
5858
}

src/index.tsx

+37-4
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,16 @@ import {
33
JupyterFrontEnd,
44
JupyterFrontEndPlugin
55
} from '@jupyterlab/application';
6-
import { MainAreaWidget, WidgetTracker } from '@jupyterlab/apputils';
6+
import {
7+
Dialog,
8+
MainAreaWidget,
9+
showDialog,
10+
WidgetTracker
11+
} from '@jupyterlab/apputils';
712
import { FileBrowser, IFileBrowserFactory } from '@jupyterlab/filebrowser';
813
import { ILauncher } from '@jupyterlab/launcher';
914
import { INotebookTracker } from '@jupyterlab/notebook';
10-
import { Contents } from '@jupyterlab/services';
15+
import { Contents, ServerConnection } from '@jupyterlab/services';
1116
import { ITranslator } from '@jupyterlab/translation';
1217

1318
import AdvancedOptions from './advanced-options';
@@ -20,6 +25,7 @@ import { SchedulerService } from './handler';
2025
import { IJobsModel, emptyCreateJobModel, JobsView } from './model';
2126
import { NotebookJobsPanel } from './notebook-jobs-panel';
2227
import { Scheduler } from './tokens';
28+
import { SERVER_EXTENSION_404_JSX } from './util/errors';
2329
import { MakeNameValid } from './util/job-name-validation';
2430

2531
export namespace CommandIDs {
@@ -34,6 +40,32 @@ export namespace CommandIDs {
3440
export const NotebookJobsPanelId = 'notebook-jobs-panel';
3541
export { Scheduler } from './tokens';
3642

43+
/**
44+
* Call API to verify that the server extension is actually installed.
45+
*/
46+
async function verifyServerExtension(props: {
47+
api: SchedulerService;
48+
translator: ITranslator;
49+
}) {
50+
const trans = props.translator.load('jupyterlab');
51+
try {
52+
await props.api.getJobs({ max_items: 0 });
53+
} catch (e: unknown) {
54+
// in case of 404, show missing server extension dialog and return
55+
if (
56+
e instanceof ServerConnection.ResponseError &&
57+
e.response.status === 404
58+
) {
59+
showDialog({
60+
title: trans.__('Jupyter Scheduler server extension not found'),
61+
body: SERVER_EXTENSION_404_JSX,
62+
buttons: [Dialog.okButton()]
63+
}).catch(console.warn);
64+
return;
65+
}
66+
}
67+
}
68+
3769
/**
3870
* Initialization data for the jupyterlab-scheduler extension.
3971
*/
@@ -114,19 +146,20 @@ function getDirectoryFromPath(path: string | null): string | null {
114146
return directories.join('/') + (directories.length > 0 ? '/' : '');
115147
}
116148

117-
async function activatePlugin(
149+
function activatePlugin(
118150
app: JupyterFrontEnd,
119151
browserFactory: IFileBrowserFactory,
120152
notebookTracker: INotebookTracker,
121153
translator: ITranslator,
122154
restorer: ILayoutRestorer,
123155
advancedOptions: Scheduler.IAdvancedOptions,
124156
launcher: ILauncher | null
125-
): Promise<void> {
157+
): void {
126158
const { commands } = app;
127159
const trans = translator.load('jupyterlab');
128160
const fileBrowserTracker = browserFactory.tracker;
129161
const api = new SchedulerService({});
162+
verifyServerExtension({ api, translator });
130163
const widgetTracker = new WidgetTracker<MainAreaWidget<NotebookJobsPanel>>({
131164
namespace: 'jupyterlab-scheduler'
132165
});

src/util/errors.tsx

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import React from 'react';
2+
3+
export const SERVER_EXTENSION_404_JSX = (
4+
<div>
5+
<p>
6+
The Jupyter Scheduler extension is installed but it can't be activated. It
7+
looks like the required Jupyter Server extension (
8+
<code>jupyter_scheduler</code>) is not installed or not enabled in this
9+
environment.
10+
</p>
11+
<h3>Why am I seeing this message?</h3>
12+
<p>
13+
If you installed the Jupyter Scheduler extension from the Extension
14+
Manager in JupyterLab, you might have installed only the client extension
15+
and not the server extension. You can install the server extension by
16+
running <code>pip install jupyter_scheduler</code> in the same environment
17+
in which you run JupyterLab.
18+
</p>
19+
<h3>How do I check if the extension is installed?</h3>
20+
<p>
21+
Please ensure that <code>jupyter server extension list</code> includes
22+
jupyter_scheduler and that it is enabled. If it is enabled, please restart
23+
JupyterLab. If the server extension is installed but not enabled, run{' '}
24+
<code>jupyter server extension enable --user --py jupyter_scheduler</code>{' '}
25+
and restart JupyterLab.
26+
</p>
27+
</div>
28+
);

tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"noUnusedLocals": true,
1414
"preserveWatchOutput": true,
1515
"resolveJsonModule": true,
16+
"skipLibCheck": true,
1617
"outDir": "lib",
1718
"rootDir": "src",
1819
"strict": true,

ui-tests/jupyter_server_test_config.py

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
opens the server to the world and provide access to JupyterLab
55
JavaScript objects through the global window variable.
66
"""
7+
78
from tempfile import mkdtemp
89

910
c.ServerApp.port = 8888

0 commit comments

Comments
 (0)