Skip to content

Commit 132931f

Browse files
committed
refactoring into submodules
1 parent eee6ecb commit 132931f

File tree

7 files changed

+807
-780
lines changed

7 files changed

+807
-780
lines changed

doc/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
extensions = [
2828
'sphinx.ext.mathjax',
29-
'jupyter_sphinx.execute',
29+
'jupyter_sphinx',
3030
]
3131

3232
html_theme = 'alabaster'

jupyter_sphinx/__init__.py

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,235 @@
1+
"""Simple sphinx extension that executes code in jupyter and inserts output."""
2+
13
from ._version import version_info, __version__
4+
from sphinx.util import logging
5+
import docutils
6+
import ipywidgets
7+
import os
8+
from sphinx.util.fileutil import copy_asset
9+
from IPython.lib.lexers import IPythonTracebackLexer, IPython3Lexer
10+
11+
from .ast import JupyterCell, JupyterCellNode, JupyterWidgetViewNode, JupyterWidgetStateNode, WIDGET_VIEW_MIMETYPE, jupyter_download_role
12+
from .execute import JupyterKernelNode, JupyterKernel, ExecuteJupyterCells
13+
from .thebelab import ThebeButton, ThebeButtonNode, ThebeOutputNode, ThebeSourceNode
14+
15+
REQUIRE_URL_DEFAULT = 'https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js'
16+
THEBELAB_URL_DEFAULT = 'https://unpkg.com/thebelab@^0.4.0'
17+
18+
logger = logging.getLogger(__name__)
19+
20+
21+
def builder_inited(app):
22+
"""
23+
2 cases
24+
case 1: ipywidgets 7, with require
25+
case 2: ipywidgets 7, no require
26+
"""
27+
require_url = app.config.jupyter_sphinx_require_url
28+
if require_url:
29+
app.add_js_file(require_url)
30+
embed_url = app.config.jupyter_sphinx_embed_url or ipywidgets.embed.DEFAULT_EMBED_REQUIREJS_URL
31+
else:
32+
embed_url = app.config.jupyter_sphinx_embed_url or ipywidgets.embed.DEFAULT_EMBED_SCRIPT_URL
33+
if embed_url:
34+
app.add_js_file(embed_url)
35+
36+
# add jupyter-sphinx css
37+
app.add_css_file('jupyter-sphinx.css')
38+
# Check if a thebelab config was specified
39+
if app.config.jupyter_sphinx_thebelab_config:
40+
app.add_js_file('thebelab-helper.js')
41+
app.add_css_file('thebelab.css')
42+
43+
44+
def build_finished(app, env):
45+
if app.builder.format != 'html':
46+
return
47+
48+
# Copy stylesheet
49+
src = os.path.join(os.path.dirname(__file__), 'css')
50+
dst = os.path.join(app.outdir, '_static')
51+
copy_asset(src, dst)
52+
53+
thebe_config = app.config.jupyter_sphinx_thebelab_config
54+
if not thebe_config:
55+
return
56+
57+
# Copy all thebelab related assets
58+
src = os.path.join(os.path.dirname(__file__), 'thebelab')
59+
dst = os.path.join(app.outdir, '_static')
60+
copy_asset(src, dst)
61+
62+
63+
def setup(app):
64+
# Configuration
65+
66+
app.add_config_value(
67+
'jupyter_execute_kwargs',
68+
dict(timeout=-1, allow_errors=True, store_widget_state=True),
69+
'env'
70+
)
71+
app.add_config_value(
72+
'jupyter_execute_default_kernel',
73+
'python3',
74+
'env'
75+
)
76+
app.add_config_value(
77+
'jupyter_execute_data_priority',
78+
[
79+
WIDGET_VIEW_MIMETYPE,
80+
'application/javascript',
81+
'text/html',
82+
'image/svg+xml',
83+
'image/png',
84+
'image/jpeg',
85+
'text/latex',
86+
'text/plain'
87+
],
88+
'env',
89+
)
90+
91+
# ipywidgets config
92+
app.add_config_value('jupyter_sphinx_require_url', REQUIRE_URL_DEFAULT, 'html')
93+
app.add_config_value('jupyter_sphinx_embed_url', None, 'html')
94+
95+
# thebelab config, can be either a filename or a dict
96+
app.add_config_value('jupyter_sphinx_thebelab_config', None, 'html')
97+
98+
app.add_config_value('jupyter_sphinx_thebelab_url', THEBELAB_URL_DEFAULT, 'html')
99+
100+
# linenos config
101+
app.add_config_value('jupyter_sphinx_linenos', False, 'env')
102+
app.add_config_value('jupyter_sphinx_continue_linenos', False, 'env')
103+
104+
# Used for nodes that do not need to be rendered
105+
def skip(self, node):
106+
raise docutils.nodes.SkipNode
107+
108+
# Renders the children of a container
109+
render_container = (
110+
lambda self, node: self.visit_container(node),
111+
lambda self, node: self.depart_container(node),
112+
)
113+
114+
# Used to render the container and its children as HTML
115+
def visit_container_html(self, node):
116+
self.body.append(node.visit_html())
117+
self.visit_container(node)
118+
119+
def depart_container_html(self, node):
120+
self.depart_container(node)
121+
self.body.append(node.depart_html())
122+
123+
# Used to render an element node as HTML
124+
def visit_element_html(self, node):
125+
self.body.append(node.html())
126+
raise docutils.nodes.SkipNode
127+
128+
# Used to render the ThebeSourceNode conditionally for non-HTML builders
129+
def visit_thebe_source(self, node):
130+
if node['hide_code']:
131+
raise docutils.nodes.SkipNode
132+
else:
133+
self.visit_container(node)
134+
135+
render_thebe_source = (
136+
visit_thebe_source,
137+
lambda self, node: self.depart_container(node)
138+
)
139+
140+
141+
# JupyterKernelNode is just a doctree marker for the
142+
# ExecuteJupyterCells transform, so we don't actually render it.
143+
app.add_node(
144+
JupyterKernelNode,
145+
html=(skip, None),
146+
latex=(skip, None),
147+
textinfo=(skip, None),
148+
text=(skip, None),
149+
man=(skip, None),
150+
)
151+
152+
# JupyterCellNode is a container that holds the input and
153+
# any output, so we render it as a container.
154+
app.add_node(
155+
JupyterCellNode,
156+
html=render_container,
157+
latex=render_container,
158+
textinfo=render_container,
159+
text=render_container,
160+
man=render_container,
161+
)
162+
163+
# JupyterWidgetViewNode holds widget view JSON,
164+
# but is only rendered properly in HTML documents.
165+
app.add_node(
166+
JupyterWidgetViewNode,
167+
html=(visit_element_html, None),
168+
latex=(skip, None),
169+
textinfo=(skip, None),
170+
text=(skip, None),
171+
man=(skip, None),
172+
)
173+
# JupyterWidgetStateNode holds the widget state JSON,
174+
# but is only rendered in HTML documents.
175+
app.add_node(
176+
JupyterWidgetStateNode,
177+
html=(visit_element_html, None),
178+
latex=(skip, None),
179+
textinfo=(skip, None),
180+
text=(skip, None),
181+
man=(skip, None),
182+
)
183+
184+
# ThebeSourceNode holds the source code and is rendered if
185+
# hide-code is not specified. For HTML it is always rendered,
186+
# but hidden using the stylesheet
187+
app.add_node(
188+
ThebeSourceNode,
189+
html=(visit_container_html, depart_container_html),
190+
latex=render_thebe_source,
191+
textinfo=render_thebe_source,
192+
text=render_thebe_source,
193+
man=render_thebe_source,
194+
)
195+
196+
# ThebeOutputNode holds the output of the Jupyter cells
197+
# and is rendered if hide-output is not specified.
198+
app.add_node(
199+
ThebeOutputNode,
200+
html=(visit_container_html, depart_container_html),
201+
latex=render_container,
202+
textinfo=render_container,
203+
text=render_container,
204+
man=render_container,
205+
)
206+
207+
# ThebeButtonNode is the button that activates thebelab
208+
# and is only rendered for the HTML builder
209+
app.add_node(
210+
ThebeButtonNode,
211+
html=(visit_element_html, None),
212+
latex=(skip, None),
213+
textinfo=(skip, None),
214+
text=(skip, None),
215+
man=(skip, None),
216+
)
217+
218+
app.add_directive('jupyter-execute', JupyterCell)
219+
app.add_directive('jupyter-kernel', JupyterKernel)
220+
app.add_directive('thebe-button', ThebeButton)
221+
app.add_role('jupyter-download:notebook', jupyter_download_role)
222+
app.add_role('jupyter-download:script', jupyter_download_role)
223+
app.add_transform(ExecuteJupyterCells)
224+
225+
# For syntax highlighting
226+
app.add_lexer('ipythontb', IPythonTracebackLexer())
227+
app.add_lexer('ipython', IPython3Lexer())
228+
229+
app.connect('builder-inited', builder_inited)
230+
app.connect('build-finished', build_finished)
231+
232+
return {
233+
'version': __version__,
234+
'parallel_read_safe': True,
235+
}

0 commit comments

Comments
 (0)