Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Encapsulate Markdown Cell in Sphinx directive #45

Closed
wants to merge 9 commits into from
3 changes: 3 additions & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
# If True, the build process is continued even if an exception occurs:
#nbsphinx_allow_errors = True

# If True, this enables wrapping of markdown cells in sphinx-directives:
#nbsphinx_allow_directives = True

# Controls when a cell will time out (defaults to 30; use -1 for no timeout):
#nbsphinx_timeout = 60

Expand Down
56 changes: 56 additions & 0 deletions doc/markdown-cells.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,59 @@
"The linked files are automatically copied to the HTML output directory.\n",
"For LaTeX output, no link is created."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Encapsulate Cell in Sphinx Directive"
]
},
{
"cell_type": "markdown",
"metadata": {
"nbsphinx-directive": true
},
"source": [
"Important\n",
"---------\n",
"This facilitates the use of Sphinx Directives, like admonitions (eg. ``.. important:: ``), with processed markdown inside. \n",
"\n",
"The first line of the cell must contain the admonition type. It also has to be underlined with `-` to be processed correctly and to give similar look within the notebook.\n",
"\n",
"Also the above mentioned markdown like:\n",
"\n",
"- Equations: $\\text{e}^{i\\pi} = -1$, \n",
"- Links: [link to somewhere](#Links-to-Other-Notebooks) or \n",
"- *emphasis*, **boldface** and `preformatted text` \n",
"\n",
"can be used within this cell. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note\n",
"----\n",
"To use this behaviour there exist three possibilities:\n",
"\n",
"- add this metadata switch to the cells metadata to enable for the single cell\n",
" ```python3\n",
" {\n",
" \"nbsphinx-directive\": true\n",
" }\n",
" ```\n",
"- add this metadata switch to the notebooks metadata to enable notebook wide:\n",
" ```python3\n",
" {\n",
" \"nbsphinx\": {\n",
" \"allow_directives\": true\n",
" },\n",
" }\n",
" ```\n",
"- add `nbsphinx_allow_directives = True` in `conf.py` to enable doc-wide"
]
}
],
"metadata": {
Expand All @@ -191,6 +244,9 @@
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.5.1+"
},
"nbsphinx": {
"allow_directives": true
}
},
"nbformat": 4,
Expand Down
37 changes: 35 additions & 2 deletions nbsphinx.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,12 @@
{%- if 'nbsphinx-toctree' in cell.metadata %}
{{ cell | extract_toctree }}
{%- else %}
{%- if nb.metadata.nbsphinx.allow_directives or 'nbsphinx-directive' in cell.metadata %}
{{ cell | wrap_cell}}
{%- else %}
{{ super() }}
{% endif %}
{%- endif %}
{%- endif %}
{% endblock markdowncell %}


Expand Down Expand Up @@ -363,9 +367,11 @@ class Exporter(nbconvert.RSTExporter):

"""

def __init__(self, allow_errors=False, timeout=30, codecell_lexer='none'):
def __init__(self, allow_errors=False, allow_directives=False,
timeout=30, codecell_lexer='none'):
"""Initialize the Exporter."""
self._allow_errors = allow_errors
self._allow_directives = allow_directives
self._timeout = timeout
self._codecell_lexer = codecell_lexer
loader = jinja2.DictLoader({'nbsphinx-rst.tpl': RST_TEMPLATE})
Expand All @@ -375,6 +381,7 @@ def __init__(self, allow_errors=False, timeout=30, codecell_lexer='none'):
'markdown2rst': markdown2rst,
'get_empty_lines': _get_empty_lines,
'extract_toctree': _extract_toctree,
'wrap_cell': _wrap_cell,
})

def from_notebook_node(self, nb, resources=None, **kw):
Expand All @@ -398,6 +405,14 @@ def from_notebook_node(self, nb, resources=None, **kw):
allow_errors=allow_errors, timeout=timeout)
nb, resources = pp.preprocess(nb, resources)

# check allow directive
if self._allow_directives:
allow_directives = self._allow_directives
else:
allow_directives = nbsphinx_metadata.get('allow_directives', False)
nbsphinx_metadata['allow_directives'] = allow_directives
nb.metadata['nbsphinx'] = nbsphinx_metadata

# Call into RSTExporter
rststr, resources = super(Exporter, self).from_notebook_node(
nb, resources, **kw)
Expand Down Expand Up @@ -441,6 +456,7 @@ def parse(self, inputstring, document):
resources['unique_key'] = env.docname.replace('/', '_')

exporter = Exporter(allow_errors=env.config.nbsphinx_allow_errors,
allow_directives=env.config.nbsphinx_allow_directives,
timeout=env.config.nbsphinx_timeout,
codecell_lexer=env.config.nbsphinx_codecell_lexer)

Expand Down Expand Up @@ -667,6 +683,22 @@ def _extract_toctree(cell):
return '\n '.join(lines)


def _wrap_cell(cell):
"""Wrap cell content from Markdown cell in sphinx directive."""
text = cell.source.split('\n', 2)

# wrap only if at least 3 lines exist
# and if first line corresponds to second line
if (len(text) > 2) and (text[1] == ('-' * len(text[0]))):
text = "".join([".. ", text[0].lower(), ":: \n\n ",
markdown2rst(text[2])
.replace("\n", '\n '), "\n"])
else:
text = markdown2rst(cell.source)

return text


def _get_empty_lines(text):
"""Get number of empty lines before and after code."""
before = len(text) - len(text.lstrip('\n'))
Expand Down Expand Up @@ -931,6 +963,7 @@ def setup(app):
_add_notebook_parser(app)

app.add_config_value('nbsphinx_allow_errors', False, rebuild='')
app.add_config_value('nbsphinx_allow_directives', False, rebuild='')
app.add_config_value('nbsphinx_timeout', 30, rebuild='')
app.add_config_value('nbsphinx_codecell_lexer', 'none', rebuild='env')

Expand Down