7
7
import tempfile
8
8
import json
9
9
import logging
10
+ import gettext
11
+
12
+ import jinja2
10
13
11
14
import tornado .ioloop
12
15
import tornado .web
13
16
14
- from pathlib import Path
15
-
16
17
from traitlets .config .application import Application
17
18
from traitlets import Unicode , Integer , Bool , default
18
19
19
- from jupyter_server .utils import url_path_join , url_escape
20
- from jupyter_server .base .handlers import JupyterHandler
21
20
from jupyter_server .services .kernels .kernelmanager import MappingKernelManager
22
- from jupyter_server .services .kernels .handlers import KernelHandler , MainKernelHandler , ZMQChannelsHandler
23
-
24
- from jupyter_client .jsonutil import date_default
25
-
26
- import nbformat
27
- from nbconvert .preprocessors .execute import executenb
28
- from nbconvert import HTMLExporter
29
-
30
- ROOT = Path (os .path .dirname (__file__ ))
31
- DEFAULT_STATIC_ROOT = ROOT / 'static'
32
- TEMPLATE_ROOT = ROOT / 'templates'
33
-
34
- class VoilaHandler (JupyterHandler ):
35
-
36
- def initialize (self , notebook = None , strip_sources = False ):
37
- self .notebook = notebook
38
- self .strip_sources = strip_sources
39
-
40
- @tornado .web .authenticated
41
- @tornado .gen .coroutine
42
- def get (self ):
21
+ from jupyter_server .services .kernels .handlers import KernelHandler , ZMQChannelsHandler
22
+ from jupyter_server .base .handlers import path_regex
23
+ from jupyter_server .services .contents .largefilemanager import LargeFileManager
43
24
44
- # Ignore requested kernel name and make use of the one specified in the notebook.
45
- kernel_name = self .notebook .metadata .get ('kernelspec' , {}).get ('name' , self .kernel_manager .default_kernel_name )
46
-
47
- # Launch kernel and execute notebook.
48
- kernel_id = yield tornado .gen .maybe_future (self .kernel_manager .start_kernel (kernel_name = kernel_name ))
49
- km = self .kernel_manager .get_kernel (kernel_id )
50
- result = executenb (self .notebook , km = km )
51
-
52
- # render notebook to html
53
- resources = dict (kernel_id = kernel_id )
54
- html , resources = HTMLExporter (template_file = str (TEMPLATE_ROOT / 'voila.tpl' ), exclude_input = self .strip_sources ,
55
- exclude_output_prompt = self .strip_sources , exclude_input_prompt = self .strip_sources
56
- ).from_notebook_node (result , resources = resources )
57
-
58
- # Compose reply
59
- self .set_header ('Content-Type' , 'text/html' )
60
- self .write (html )
25
+ from .paths import ROOT , STATIC_ROOT , TEMPLATE_ROOT
26
+ from .handler import VoilaHandler
27
+ from .treehandler import VoilaTreeHandler
28
+ from .watchdog import WatchDogHandler
61
29
62
30
_kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
63
31
@@ -73,7 +41,7 @@ class Voila(Application):
73
41
)
74
42
option_description = Unicode (
75
43
"""
76
- notebook_filename :
44
+ notebook_path :
77
45
File name of the Jupyter notebook to display.
78
46
"""
79
47
)
@@ -84,15 +52,21 @@ class Voila(Application):
84
52
config = True ,
85
53
help = 'Port of the voila server. Default 8866.'
86
54
)
55
+ autoreload = Bool (
56
+ False ,
57
+ config = True ,
58
+ help = 'Will autoreload to server and the page when a template, js file or Python code changes'
59
+ )
87
60
static_root = Unicode (
88
- str (DEFAULT_STATIC_ROOT ),
61
+ str (STATIC_ROOT ),
89
62
config = True ,
90
63
help = 'Directory holding static assets (HTML, JS and CSS files).'
91
64
)
92
65
aliases = {
93
66
'port' : 'Voila.port' ,
94
67
'static' : 'Voila.static_root' ,
95
- 'strip_sources' : 'Voila.strip_sources'
68
+ 'strip_sources' : 'Voila.strip_sources' ,
69
+ 'autoreload' : 'Voila.autoreload'
96
70
}
97
71
connection_dir_root = Unicode (
98
72
config = True ,
@@ -116,14 +90,7 @@ def _default_log_level(self):
116
90
117
91
def parse_command_line (self , argv = None ):
118
92
super (Voila , self ).parse_command_line (argv )
119
- try :
120
- notebook_filename = self .extra_args [0 ]
121
- except IndexError :
122
- self .log .critical ('Bad command line parameters.' )
123
- self .log .critical ('Missing NOTEBOOK_FILENAME parameter.' )
124
- self .log .critical ('Run `voila --help` for help on command line parameters.' )
125
- exit (1 )
126
- self .notebook_filename = notebook_filename
93
+ self .notebook_path = self .extra_args [0 ] if len (self .extra_args ) == 1 else None
127
94
128
95
def start (self ):
129
96
connection_dir = tempfile .mkdtemp (
@@ -143,21 +110,11 @@ def start(self):
143
110
]
144
111
)
145
112
146
- notebook = nbformat .read (self .notebook_filename , as_version = 4 )
147
-
148
113
handlers = [
149
- (
150
- r'/' ,
151
- VoilaHandler ,
152
- {
153
- 'notebook' : notebook ,
154
- 'strip_sources' : self .strip_sources
155
- }
156
- ),
157
114
(r'/api/kernels/%s' % _kernel_id_regex , KernelHandler ),
158
115
(r'/api/kernels/%s/channels' % _kernel_id_regex , ZMQChannelsHandler ),
159
116
(
160
- r"/static/(.*)" ,
117
+ r"/voila/ static/(.*)" ,
161
118
tornado .web .StaticFileHandler ,
162
119
{
163
120
'path' : self .static_root ,
@@ -166,10 +123,41 @@ def start(self):
166
123
)
167
124
]
168
125
126
+ if self .notebook_path :
127
+ handlers .append ((
128
+ r'/' ,
129
+ VoilaHandler ,
130
+ {
131
+ 'notebook_path' : self .notebook_path ,
132
+ 'strip_sources' : self .strip_sources
133
+ }
134
+ ))
135
+ else :
136
+ handlers .extend ([
137
+ ('/' , VoilaTreeHandler ),
138
+ ('/voila/tree' + path_regex , VoilaTreeHandler ),
139
+ ('/voila/render' + path_regex , VoilaHandler , {'strip_sources' : self .strip_sources }),
140
+ ])
141
+ if self .autoreload :
142
+ handlers .append (('/voila/watchdog' + path_regex , WatchDogHandler ))
143
+
144
+ jenv_opt = {"autoescape" : True } # we might want extra options via cmd line like notebook server
145
+ env = jinja2 .Environment (loader = jinja2 .FileSystemLoader (str (TEMPLATE_ROOT )), extensions = ['jinja2.ext.i18n' ], ** jenv_opt )
146
+ nbui = gettext .translation ('nbui' , localedir = str (ROOT / 'i18n' ), fallback = True )
147
+ env .install_gettext_translations (nbui , newstyle = False )
148
+
149
+ contents_manager = LargeFileManager () # TODO: make this configurable like notebook
150
+
151
+
169
152
app = tornado .web .Application (
170
153
handlers ,
171
154
kernel_manager = kernel_manager ,
172
- allow_remote_access = True
155
+ allow_remote_access = True ,
156
+ autoreload = self .autoreload ,
157
+ voila_jinja2_env = env ,
158
+ static_path = '/' ,
159
+ server_root_dir = '/' ,
160
+ contents_manager = contents_manager
173
161
)
174
162
175
163
app .listen (self .port )
0 commit comments