def setup_template_dirs(self): if self.voila_configuration.template: collect_template_paths(self.nbconvert_template_paths, self.static_paths, self.template_paths, self.voila_configuration.template) # look for possible template-related config files template_conf_dir = [ os.path.join(k, '..') for k in self.nbconvert_template_paths ] conf_paths = [ os.path.join(d, 'conf.json') for d in template_conf_dir ] for p in conf_paths: # see if config file exists if os.path.exists(p): # load the template-related config with open(p) as json_file: conf = json.load(json_file) # update the overall config with it, preserving CLI config priority if 'traitlet_configuration' in conf: recursive_update( conf['traitlet_configuration'], self.voila_configuration.config.VoilaConfiguration) # pass merged config to overall voila config self.voila_configuration.config.VoilaConfiguration = Config( conf['traitlet_configuration']) self.log.debug('using template: %s', self.voila_configuration.template) self.log.debug('nbconvert template paths:\n\t%s', '\n\t'.join(self.nbconvert_template_paths)) self.log.debug('template paths:\n\t%s', '\n\t'.join(self.template_paths)) self.log.debug('static paths:\n\t%s', '\n\t'.join(self.static_paths)) if self.notebook_path and not os.path.exists(self.notebook_path): raise ValueError('Notebook not found: %s' % self.notebook_path)
def get(self, section_name): """Get the config from all config sections.""" config = {} # step through back to front, to ensure front of the list is top priority for p in self.read_config_path[::-1]: cm = BaseJSONConfigManager(config_dir=p) recursive_update(config, cm.get(section_name)) return config
async def get(self, path=None): # if the handler got a notebook_path argument, always serve that notebook_path = self.notebook_path or path if self.notebook_path and path: # when we are in single notebook mode but have a path self.redirect_to_file(path) return if self.voila_configuration.enable_nbextensions: # generate a list of nbextensions that are enabled for the classical notebook # a template can use that to load classical notebook extensions, but does not have to notebook_config = self.config_manager.get('notebook') # except for the widget extension itself, since Voilà has its own load_extensions = notebook_config.get('load_extensions', {}) if 'jupyter-js-widgets/extension' in load_extensions: load_extensions['jupyter-js-widgets/extension'] = False if 'voila/extension' in load_extensions: load_extensions['voila/extension'] = False nbextensions = [ name for name, enabled in load_extensions.items() if enabled ] else: nbextensions = [] notebook = await self.load_notebook(notebook_path) if not notebook: return self.cwd = os.path.dirname(notebook_path) path, basename = os.path.split(notebook_path) notebook_name = os.path.splitext(basename)[0] # Adding request uri to kernel env self.kernel_env = os.environ.copy() self.kernel_env['SCRIPT_NAME'] = self.request.path self.kernel_env[ 'PATH_INFO'] = '' # would be /foo/bar if voila.ipynb/foo/bar was supported self.kernel_env['QUERY_STRING'] = str(self.request.query) self.kernel_env['SERVER_SOFTWARE'] = 'voila/{}'.format(__version__) self.kernel_env['SERVER_PROTOCOL'] = str(self.request.version) host, port = split_host_and_port(self.request.host.lower()) self.kernel_env['SERVER_PORT'] = str(port) if port else '' self.kernel_env['SERVER_NAME'] = host # we can override the template via notebook metadata or a query parameter template_override = None if 'voila' in notebook.metadata and self.voila_configuration.allow_template_override in [ 'YES', 'NOTEBOOK' ]: template_override = notebook.metadata['voila'].get('template') if self.voila_configuration.allow_template_override == 'YES': template_override = self.get_argument("voila-template", template_override) if template_override: self.template_paths = collect_template_paths( ['voila', 'nbconvert'], template_override) template_name = template_override or self.voila_configuration.template theme = self.voila_configuration.theme if 'voila' in notebook.metadata and self.voila_configuration.allow_theme_override in [ 'YES', 'NOTEBOOK' ]: theme = notebook.metadata['voila'].get('theme', theme) if self.voila_configuration.allow_theme_override == 'YES': theme = self.get_argument("voila-theme", theme) # render notebook to html resources = { 'base_url': self.base_url, 'nbextensions': nbextensions, 'theme': theme, 'template': template_name, 'metadata': { 'name': notebook_name } } # include potential extra resources extra_resources = self.voila_configuration.config.VoilaConfiguration.resources # if no resources get configured from neither the CLI nor a config file, # extra_resources is a traitlets.config.loader.LazyConfigValue object # This seems to only happy with the notebook server and traitlets 5 # Note that we use string checking for backward compatibility if 'DeferredConfigString' in str(type(extra_resources)): from .configuration import VoilaConfiguration extra_resources = VoilaConfiguration.resources.from_string( extra_resources) if not isinstance(extra_resources, dict): extra_resources = extra_resources.to_dict() if extra_resources: recursive_update(resources, extra_resources) self.exporter = VoilaExporter( template_paths=self.template_paths, template_name=template_name, config=self.traitlet_config, contents_manager=self.contents_manager, # for the image inlining theme=theme, # we now have the theme in two places base_url=self.base_url, ) if self.voila_configuration.strip_sources: self.exporter.exclude_input = True self.exporter.exclude_output_prompt = True self.exporter.exclude_input_prompt = True # These functions allow the start of a kernel and execution of the notebook after (parts of) the template # has been rendered and send to the client to allow progressive rendering. # Template should first call kernel_start, and then decide to use notebook_execute # or cell_generator to implement progressive cell rendering extra_context = { 'kernel_start': self._jinja_kernel_start, 'cell_generator': self._jinja_cell_generator, 'notebook_execute': self._jinja_notebook_execute, } # Compose reply self.set_header('Content-Type', 'text/html') self.set_header('Cache-Control', 'no-cache, no-store, must-revalidate') self.set_header('Pragma', 'no-cache') self.set_header('Expires', '0') # render notebook in snippets, and flush them out to the browser can render progresssively async for html_snippet, resources in self.exporter.generate_from_notebook_node( notebook, resources=resources, extra_context=extra_context): self.write(html_snippet) self.flush( ) # we may not want to consider not flusing after each snippet, but add an explicit flush function to the jinja context # yield # give control back to tornado's IO loop, so it can handle static files or other requests self.flush()
async def get(self, path=None): # if the handler got a notebook_path argument, always serve that notebook_path = self.notebook_path or path if self.notebook_path and path: # when we are in single notebook mode but have a path self.redirect_to_file(path) return if self.voila_configuration.enable_nbextensions: # generate a list of nbextensions that are enabled for the classical notebook # a template can use that to load classical notebook extensions, but does not have to notebook_config = self.config_manager.get('notebook') # except for the widget extension itself, since voila has its own load_extensions = notebook_config.get('load_extensions', {}) if 'jupyter-js-widgets/extension' in load_extensions: load_extensions['jupyter-js-widgets/extension'] = False if 'voila/extension' in load_extensions: load_extensions['voila/extension'] = False nbextensions = [ name for name, enabled in load_extensions.items() if enabled ] else: nbextensions = [] self.notebook = await self.load_notebook(notebook_path) if not self.notebook: return self.cwd = os.path.dirname(notebook_path) # render notebook to html resources = { 'base_url': self.base_url, 'nbextensions': nbextensions, 'theme': self.voila_configuration.theme } # include potential extra resources extra_resources = self.voila_configuration.config.VoilaConfiguration.resources # if no resources get configured from neither the CLI nor a config file, # extra_resources is a traitlets.config.loader.LazyConfigValue object if not isinstance(extra_resources, dict): extra_resources = extra_resources.to_dict() if extra_resources: recursive_update(resources, extra_resources) self.exporter = VoilaExporter( template_path=self.nbconvert_template_paths, config=self.traitlet_config, contents_manager=self.contents_manager # for the image inlining ) if self.voila_configuration.strip_sources: self.exporter.exclude_input = True self.exporter.exclude_output_prompt = True self.exporter.exclude_input_prompt = True # These functions allow the start of a kernel and execution of the notebook after (parts of) the template # has been rendered and send to the client to allow progressive rendering. # Template should first call kernel_start, and then decide to use notebook_execute # or cell_generator to implement progressive cell rendering extra_context = { # NOTE: we can remove the lambda is we use jinja's async feature, which will automatically await the future 'kernel_start': lambda: self._jinja_kernel_start().result( ), # pass the result (not the future) to the template 'cell_generator': self._jinja_cell_generator, 'notebook_execute': self._jinja_notebook_execute, } # Currenly _jinja_kernel_start is executed from a different thread, which causes the websocket connection from # the frontend to fail. Instead, we start it beforehand, and just return the kernel_id in _jinja_kernel_start self.kernel_id = await tornado.gen.maybe_future( self.kernel_manager.start_kernel( kernel_name=self.notebook.metadata.kernelspec.name, path=self.cwd)) # Compose reply self.set_header('Content-Type', 'text/html') # render notebook in snippets, and flush them out to the browser can render progresssively async for html_snippet, resources in self.exporter.generate_from_notebook_node( self.notebook, resources=resources, extra_context=extra_context): self.write(html_snippet) self.flush( ) # we may not want to consider not flusing after each snippet, but add an explicit flush function to the jinja context # yield # give control back to tornado's IO loop, so it can handle static files or other requests self.flush()
def get(self, path=None): # if the handler got a notebook_path argument, always serve that notebook_path = self.notebook_path or path if self.voila_configuration.enable_nbextensions: # generate a list of nbextensions that are enabled for the classical notebook # a template can use that to load classical notebook extensions, but does not have to notebook_config = self.config_manager.get('notebook') # except for the widget extension itself, since voila has its own load_extensions = notebook_config.get('load_extensions', {}) if 'jupyter-js-widgets/extension' in load_extensions: load_extensions['jupyter-js-widgets/extension'] = False if 'voila/extension' in load_extensions: load_extensions['voila/extension'] = False nbextensions = [ name for name, enabled in load_extensions.items() if enabled ] else: nbextensions = [] notebook = yield self.load_notebook(notebook_path) # Launch kernel and execute notebook cwd = os.path.dirname(notebook_path) kernel_id = yield tornado.gen.maybe_future( self.kernel_manager.start_kernel( kernel_name=notebook.metadata.kernelspec.name, path=cwd)) km = self.kernel_manager.get_kernel(kernel_id) result = executenb(notebook, km=km, cwd=cwd, config=self.traitlet_config) # render notebook to html resources = { 'kernel_id': kernel_id, 'base_url': self.base_url, 'nbextensions': nbextensions, 'theme': self.voila_configuration.theme } # include potential extra resources extra_resources = self.voila_configuration.resources if extra_resources: recursive_update(resources, extra_resources) exporter = VoilaExporter( template_path=self.nbconvert_template_paths, config=self.traitlet_config, contents_manager=self.contents_manager # for the image inlining ) if self.voila_configuration.strip_sources: exporter.exclude_input = True exporter.exclude_output_prompt = True exporter.exclude_input_prompt = True # Filtering out empty cells. def filter_empty_code_cells(cell): return ( cell.cell_type != 'code' or # keep non-code cells (cell.outputs and not exporter.exclude_output ) # keep cell if output not excluded and not empty or not exporter.exclude_input # keep cell if input not excluded ) result.cells = list(filter(filter_empty_code_cells, result.cells)) html, resources = exporter.from_notebook_node(result, resources=resources) # Compose reply self.set_header('Content-Type', 'text/html') self.write(html)
async def initialize(self, **kwargs) -> None: """ Initialize the notebook generator. """ notebook_path = self.notebook_path if self.voila_configuration.enable_nbextensions: # generate a list of nbextensions that are enabled for the classical notebook # a template can use that to load classical notebook extensions, but does not have to notebook_config = self.config_manager.get('notebook') # except for the widget extension itself, since Voilà has its own load_extensions = notebook_config.get('load_extensions', {}) if 'jupyter-js-widgets/extension' in load_extensions: load_extensions['jupyter-js-widgets/extension'] = False if 'voila/extension' in load_extensions: load_extensions['voila/extension'] = False nbextensions = [ name for name, enabled in load_extensions.items() if enabled ] else: nbextensions = [] self.notebook = await self.load_notebook(notebook_path) self.cwd = os.path.dirname(notebook_path) _, basename = os.path.split(notebook_path) notebook_name = os.path.splitext(basename)[0] # we can override the template via notebook metadata or via # input parameter template_override = None if ('voila' in self.notebook.metadata and self.voila_configuration.allow_template_override in ['YES', 'NOTEBOOK']): template_override = self.notebook.metadata['voila'].get('template') if self.voila_configuration.allow_template_override == 'YES': template_arg = kwargs.get('template', None) template_override = (template_arg if template_arg is not None else template_override) if template_override: self.template_paths = collect_template_paths( ['voila', 'nbconvert'], template_override) self.template_name = template_override or self.voila_configuration.template theme_override = self.voila_configuration.theme if ('voila' in self.notebook.metadata and self.voila_configuration.allow_theme_override in ['YES', 'NOTEBOOK']): theme_override = self.notebook.metadata['voila'].get( 'theme', theme_override) if self.voila_configuration.allow_theme_override == 'YES': theme_arg = kwargs.get('theme', None) theme_override = theme_arg if theme_arg is not None else theme_override self.theme = theme_override # render notebook to html self.resources = { 'base_url': self.base_url, 'nbextensions': nbextensions, 'theme': self.theme, 'template': self.template_name, 'metadata': { 'name': notebook_name }, } # include potential extra resources extra_resources = self.voila_configuration.config.VoilaConfiguration.resources # if no resources get configured from neither the CLI nor a config file, # extra_resources is a traitlets.config.loader.LazyConfigValue object # This seems to only happy with the notebook server and traitlets 5 # Note that we use string checking for backward compatibility if 'DeferredConfigString' in str(type(extra_resources)): from .configuration import VoilaConfiguration extra_resources = VoilaConfiguration.resources.from_string( extra_resources) if not isinstance(extra_resources, dict): extra_resources = extra_resources.to_dict() if extra_resources: recursive_update(self.resources, extra_resources) self.exporter = VoilaExporter( template_paths=self.template_paths, template_name=self.template_name, config=self.traitlet_config, contents_manager=self.contents_manager, # for the image inlining theme=self.theme, # we now have the theme in two places base_url=self.base_url, ) if self.voila_configuration.strip_sources: self.exporter.exclude_input = True self.exporter.exclude_output_prompt = True self.exporter.exclude_input_prompt = True