|
#############################################################################
|
|
# 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))
|