Skip to content

Commit 22bea83

Browse files
committed
Add option to install dependencies from CLI
1 parent 0a856b6 commit 22bea83

File tree

3 files changed

+66
-25
lines changed

3 files changed

+66
-25
lines changed

packages/voilite/src/app.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,16 @@ export class VoiliteApp extends JupyterFrontEnd<IShell> {
170170
const notebookSrc = JSON.parse(
171171
PageConfig.getOption('notebookSrc')
172172
) as ICellData[];
173-
173+
const packagesSrc = PageConfig.getOption('packagesSrc');
174174
kernel.statusChanged.connect(async (_, status) => {
175175
if (!executed && status === 'idle') {
176176
executed = true;
177-
await App.executeCells(notebookSrc, rendermime, connection.kernel!);
177+
await App.executeCells({
178+
packages: packagesSrc,
179+
source: notebookSrc,
180+
rendermime,
181+
kernel: connection.kernel!
182+
});
178183
}
179184
});
180185
}
@@ -218,12 +223,20 @@ export namespace App {
218223
default: JupyterFrontEndPlugin<any> | JupyterFrontEndPlugin<any>[];
219224
}
220225

221-
export async function executeCells(
222-
source: ICellData[],
223-
rendermime: RenderMimeRegistry,
224-
kernel: IKernelConnection
225-
): Promise<void> {
226-
// const cellKeys = Object.keys(cells).map(key => parseInt(key));
226+
export async function executeCells(options: {
227+
packages?: string;
228+
source: ICellData[];
229+
rendermime: RenderMimeRegistry;
230+
kernel: IKernelConnection;
231+
}): Promise<void> {
232+
const { packages, source, rendermime, kernel } = options;
233+
if (packages && packages.length > 0) {
234+
window.update_loading_text(0, 0, 'Installing dependencies');
235+
const future = kernel.requestExecute({
236+
code: packages
237+
});
238+
await future.done;
239+
}
227240
const cellCount = source.length;
228241
for (let idx = 0; idx < cellCount; idx++) {
229242
const cell = source[idx];

voila/voilite/app.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import logging
44
import os
55
import shutil
6-
from copy import deepcopy
76
from pathlib import Path
87
from typing import Dict
98
from typing import List as TypeList
@@ -67,6 +66,7 @@ class Voilite(Application):
6766
'theme': 'VoilaConfiguration.theme',
6867
'base_url': 'Voilite.base_url',
6968
'contents': 'Voilite.contents',
69+
'packages': 'Voilite.packages',
7070
}
7171

7272
output_prefix = Unicode(
@@ -79,6 +79,16 @@ class Voilite(Application):
7979
'files', config=True, help=_('Name of the user contents directory')
8080
)
8181

82+
packages = List(
83+
[],
84+
config=True,
85+
help=_('List of packages to be installed in the voilite enviroment'),
86+
)
87+
88+
config_file_paths = List(
89+
Unicode(), config=True, help=_('Paths to search for voilite.(py|json)')
90+
)
91+
8292
@default('log_level')
8393
def _default_log_level(self):
8494
return logging.INFO
@@ -90,6 +100,10 @@ def _default_root_dir(self):
90100
else:
91101
return os.getcwd()
92102

103+
@default('config_file_paths')
104+
def _config_file_paths_default(self):
105+
return [os.getcwd()]
106+
93107
def setup_template_dirs(self):
94108
if self.voilite_configuration.template:
95109
template_name = self.voilite_configuration.template
@@ -147,6 +161,7 @@ def initialize(self, argv=None):
147161
raise ValueError(
148162
'provided more than 1 argument: %r' % self.extra_args
149163
)
164+
self.load_config_file('voilite', path=self.config_file_paths)
150165
self.voilite_configuration = VoilaConfiguration(parent=self)
151166
self.setup_template_dirs()
152167

@@ -196,7 +211,8 @@ def ignore_func(dir, files: TypeList[str]) -> TypeList[str]:
196211
return [
197212
f
198213
for f in files
199-
if os.path.isfile(os.path.join(dir, f)) and f.endswith('voila.js')
214+
if os.path.isfile(os.path.join(dir, f))
215+
and f.endswith('voila.js')
200216
]
201217

202218
for root in self.static_paths:
@@ -267,31 +283,29 @@ def convert_notebook(
267283
nb_path: str,
268284
page_config: Dict,
269285
output_prefix: str,
286+
packages: TypeList[str],
270287
output_name: str = None,
271288
) -> None:
272289
nb_name = output_name
273290

274291
nb_path = Path(nb_path)
275292
if not nb_name:
276293
nb_name = f'{nb_path.stem}.html'
277-
278-
page_config_copy = deepcopy(page_config)
279-
280294
with open(nb_path) as f:
281295
nb = nbformat.read(f, 4)
282-
src = [
296+
nb_src = [
283297
{
284298
'cell_source': cell['source'],
285299
'cell_type': cell['cell_type'],
286300
}
287301
for cell in nb['cells']
288302
]
289-
page_config_copy['notebookSrc'] = src
290-
291303
voilite_exporter = VoiliteExporter(
292304
voilite_config=self.voilite_configuration,
293-
page_config=page_config_copy,
305+
page_config=page_config,
294306
base_url=self.base_url,
307+
nb_src=nb_src,
308+
packages=packages,
295309
)
296310
content, _ = voilite_exporter.from_filename(nb_path)
297311
output_dir = os.path.join(output_prefix, nb_path.parent)
@@ -317,6 +331,7 @@ def convert_directory(self, page_config):
317331
nb,
318332
page_config,
319333
os.path.join(self.output_prefix, 'voila', 'render'),
334+
self.packages,
320335
)
321336

322337
def start(self):
@@ -347,6 +362,7 @@ def start(self):
347362
self.notebook_path,
348363
page_config,
349364
self.output_prefix,
365+
self.packages,
350366
'index.html',
351367
)
352368
else:

voila/voilite/exporter.py

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
#############################################################################
99

1010

11-
from traitlets import default
11+
from copy import deepcopy
1212

13-
from nbconvert.filters.highlight import Highlight2HTML
1413
from jupyter_server.services.contents.largefilemanager import LargeFileManager
15-
1614
from nbconvert.exporters import TemplateExporter
15+
from nbconvert.filters.highlight import Highlight2HTML
1716
from nbconvert.preprocessors import ClearOutputPreprocessor
17+
from traitlets import default
1818

1919
from ..exporter import VoilaExporter
2020
from ..paths import collect_template_paths
@@ -25,14 +25,14 @@ def __init__(self, *args, **kwargs):
2525
kwargs.setdefault('base_url', '/')
2626
kwargs.setdefault('contents_manager', LargeFileManager())
2727

28-
self.voilite_configuration = kwargs.get('voilite_config')
28+
super().__init__(*args, **kwargs)
2929

30+
self.voilite_configuration = kwargs.get('voilite_config')
3031
self.page_config = kwargs.get('page_config', {})
3132
self.theme = self.voilite_configuration.theme
3233
self.template_name = self.voilite_configuration.template
33-
# template_paths?
34-
35-
super().__init__(*args, **kwargs)
34+
self.packages = kwargs.get('packages', [])
35+
self.nb_src = kwargs.get('nb_src', [])
3636

3737
# TODO
3838
# Investigate why this doesnt work
@@ -66,6 +66,7 @@ def from_notebook_node(self, nb, resources=None, **kwargs):
6666
nb_copy, resources = super(TemplateExporter, self).from_notebook_node(
6767
nb, resources, **kwargs
6868
)
69+
6970
resources['base_url'] = self.base_url
7071
resources.setdefault('raw_mimetypes', self.raw_mimetypes)
7172
resources['global_content_filter'] = {
@@ -97,7 +98,7 @@ def notebook_execute(nb, kernel_id):
9798
resources=resources,
9899
**extra_context,
99100
static_url=self.static_url,
100-
page_config=self.page_config
101+
page_config=self.update_page_config(self.page_config),
101102
):
102103
html.append(html_snippet)
103104

@@ -120,3 +121,14 @@ def _init_resources(self, resources):
120121
resources['include_lab_theme'] = lambda x: ''
121122

122123
return resources
124+
125+
def update_page_config(self, page_config):
126+
page_config_copy = deepcopy(page_config)
127+
128+
page_config_copy['notebookSrc'] = self.nb_src
129+
if len(self.packages) > 0:
130+
packages_src = 'import piplite\n'
131+
packages_src += f'await piplite.install({self.packages})\n'
132+
page_config_copy['packagesSrc'] = packages_src
133+
134+
return page_config_copy

0 commit comments

Comments
 (0)