############################################################################# # Copyright (c) 2018, Voila Contributors # # Copyright (c) 2018, QuantStack # # # # Distributed under the terms of the BSD 3-Clause License. # # # # The full license is in the file LICENSE, distributed with this software. # ############################################################################# import os import json from jupyter_core.paths import jupyter_path ROOT = os.path.dirname(__file__) STATIC_ROOT = os.path.join(ROOT, 'static') # if the directory above us contains the following paths, it means we are installed in dev mode (pip install -e .) DEV_MODE = os.path.exists(os.path.join(ROOT, '../setup.py')) and os.path.exists(os.path.join(ROOT, '../share')) def collect_template_paths( nbconvert_template_paths, static_paths, tornado_template_paths, template_name='default'): """ Voila supports custom templates for rendering notebooks. For a specified template name, `collect_template_paths` collects - nbconvert template paths, - static paths, - tornado template paths, by looking in the standard Jupyter data directories (PREFIX/share/jupyter/voila/templates) with different prefix values (user directory, sys prefix, and then system prefix) which allows users to override templates locally. The function will recursively load the base templates upon which the specified template may be based. """ # We look at the usual jupyter locations, and for development purposes also # relative to the package directory (first entry, meaning with highest precedence) search_directories = [] if DEV_MODE: search_directories.append(os.path.abspath(os.path.join(ROOT, '..', 'share', 'jupyter', 'voila', 'templates'))) search_directories.extend(jupyter_path('voila', 'templates')) found_at_least_one = False for search_directory in search_directories: template_directory = os.path.join(search_directory, template_name) if os.path.exists(template_directory): found_at_least_one = True conf = {} conf_file = os.path.join(template_directory, 'conf.json') if os.path.exists(conf_file): with open(conf_file) as f: conf = json.load(f) # For templates that are not named 'default', we assume the default base_template is 'default' # that means that even the default template could have a base_template when explicitly given. if template_name != 'default' or 'base_template' in conf: collect_template_paths( nbconvert_template_paths, static_paths, tornado_template_paths, conf.get('base_template', 'default')) extra_nbconvert_path = os.path.join(template_directory, 'nbconvert_templates') nbconvert_template_paths.insert(0, extra_nbconvert_path) extra_static_path = os.path.join(template_directory, 'static') static_paths.insert(0, extra_static_path) extra_template_path = os.path.join(template_directory, 'templates') tornado_template_paths.insert(0, extra_template_path) # We don't look at multiple directories, once a directory with a given name is found at a # given level of precedence (for instance user directory), we don't look further (for instance # in sys.prefix) break if not found_at_least_one: paths = "\n\t".join(search_directories) raise ValueError('No template sub-directory with name %r found in the following paths:\n\t%s' % (template_name, paths))