示例#1
0
 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)
示例#2
0
 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
示例#3
0
    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()
示例#4
0
    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()
示例#5
0
    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