diff --git a/docs/source/customizing.ipynb b/docs/source/customizing.ipynb index 4da09ebd1..ec780c74a 100644 --- a/docs/source/customizing.ipynb +++ b/docs/source/customizing.ipynb @@ -206,6 +206,40 @@ "!jupyter nbconvert --to python 'example.ipynb' --stdout --template=simplepython.tpl" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Saving Custom Templates\n", + "\n", + "By default, nbconvert finds templates from a few locations.\n", + "\n", + "The recommended place to save custom templates, so that they are globally accessible to nbconvert, is your jupyter data directories:\n", + "\n", + "- share/jupyter\n", + " - nbconvert\n", + " - templates\n", + " - html\n", + " - latex\n", + "\n", + "The HTML and LaTeX/PDF exporters will search the html and latex subdirectories for templates, respectively.\n", + "\n", + "To find your jupyter configuration directory you can use:\n", + "\n", + "```python\n", + "from jupyter_core.paths import jupyter_path\n", + "print(jupyter_path('nbconvert','templates'))\n", + "```\n", + "\n", + "Additionally,\n", + "\n", + "```python\n", + "TemplateExporter.template_path=['.']\n", + "```\n", + "\n", + "defines an additional list of paths that nbconvert can look for user defined templates. It defaults to searching for custom templates in the current working directory and can be changed through configuration options." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -376,7 +410,13 @@ "source": [ "### A few gotchas\n", "\n", - "Jinja blocks use `{% %}` by default which does not play nicely with LaTeX, so those are replaced by `((* *))` in LaTeX templates." + "Jinja uses `%`, `{`, and `}` for syntax by default which does not play nicely with LaTeX. In LaTeX, we have the following replacements:\n", + "\n", + "| Syntax | Default | LaTeX |\n", + "|----------|---------|---------|\n", + "| block | {% %} | ((* *)) |\n", + "| variable | {{ }} | ((( ))) |\n", + "| comment | {# #} | ((= =)) |" ] }, { @@ -24921,7 +24961,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.1" + "version": "3.7.3" } }, "nbformat": 4, diff --git a/nbconvert/exporters/html.py b/nbconvert/exporters/html.py index cf6d0871b..bf2b3c005 100644 --- a/nbconvert/exporters/html.py +++ b/nbconvert/exporters/html.py @@ -8,6 +8,7 @@ from traitlets import default, Unicode from traitlets.config import Config +from jupyter_core.paths import jupyter_path from jinja2 import contextfilter from nbconvert.filters.highlight import Highlight2HTML @@ -36,6 +37,10 @@ def _file_extension_default(self): def _default_template_path_default(self): return os.path.join("..", "templates", "html") + @default('template_data_paths') + def _template_data_paths_default(self): + return jupyter_path("nbconvert", "templates", "html") + @default('template_file') def _template_file_default(self): return 'full.tpl' diff --git a/nbconvert/exporters/latex.py b/nbconvert/exporters/latex.py index 0c0ee179e..dd16c2f73 100644 --- a/nbconvert/exporters/latex.py +++ b/nbconvert/exporters/latex.py @@ -7,6 +7,7 @@ from traitlets import Unicode, default from traitlets.config import Config +from jupyter_core.paths import jupyter_path from nbconvert.filters.highlight import Highlight2Latex from nbconvert.filters.filter_links import resolve_references @@ -39,6 +40,10 @@ def _default_template_path_default(self): @default('template_skeleton_path') def _template_skeleton_path_default(self): return os.path.join("..", "templates", "latex", "skeleton") + + @default('template_data_paths') + def _template_data_paths_default(self): + return jupyter_path("nbconvert", "templates", "latex") #Extension that the template files use. template_extension = Unicode(".tplx").tag(config=True) diff --git a/nbconvert/exporters/templateexporter.py b/nbconvert/exporters/templateexporter.py index 77059428c..4a3fff78b 100644 --- a/nbconvert/exporters/templateexporter.py +++ b/nbconvert/exporters/templateexporter.py @@ -15,6 +15,8 @@ from traitlets.config import Config from traitlets.utils.importstring import import_item from ipython_genutils import py3compat +from jupyter_core.paths import jupyter_path +from jupyter_core.utils import ensure_dir_exists from jinja2 import ( TemplateNotFound, Environment, ChoiceLoader, FileSystemLoader, BaseLoader, DictLoader @@ -184,6 +186,11 @@ def _raw_template_changed(self, change): help="Path where the template skeleton files are located.", ).tag(affects_environment=True) + template_data_paths = List( + jupyter_path('nbconvert','templates'), + help="Path where templates can be installed too." + ).tag(affects_environment=True) + #Extension that the template files use. template_extension = Unicode(".tpl").tag(config=True, affects_environment=True) @@ -391,7 +398,15 @@ def _create_environment(self): """ here = os.path.dirname(os.path.realpath(__file__)) + additional_paths = self.template_data_paths + for path in additional_paths: + try: + ensure_dir_exists(path, mode=0o700) + except OSError: + pass + paths = self.template_path + \ + additional_paths + \ [os.path.join(here, self.default_template_path), os.path.join(here, self.template_skeleton_path)]