From 3c37d121a885117f4521d51e3f73f40f4199e8c6 Mon Sep 17 00:00:00 2001 From: Steven Hiscocks Date: Mon, 22 Jun 2020 13:07:16 +0100 Subject: [PATCH] Initial changes to add ThebeLab support to notebook style galleries These initial changes allow for notebook style galleries to be run with ThebeLab, allow custom configuration for ThebeLab to be passed with details on repo, etc. Documentation and tests still required. --- doc/_static/theme_override.css | 4 ++ doc/conf.py | 6 +++ setup.py | 1 + sphinx_gallery/_static/gallery.css | 5 +++ sphinx_gallery/_static/thebelab_badge.svg | 1 + sphinx_gallery/gen_gallery.py | 13 +++++++ sphinx_gallery/gen_rst.py | 45 ++++++++++++++++++----- sphinx_gallery/tests/test_full.py | 6 ++- sphinx_gallery/tests/test_gen_rst.py | 14 ++++--- 9 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 sphinx_gallery/_static/thebelab_badge.svg diff --git a/doc/_static/theme_override.css b/doc/_static/theme_override.css index b4aa9daf4..68edf28f2 100644 --- a/doc/_static/theme_override.css +++ b/doc/_static/theme_override.css @@ -32,4 +32,8 @@ a.sphx-glr-backref-module-sphinx_gallery { .anim-state label { display: inline-block; +} + +.thebelab-cell { + font-size: 12px; } \ No newline at end of file diff --git a/doc/conf.py b/doc/conf.py index b34bc7033..3bc440f70 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -361,6 +361,12 @@ def setup(app): # each code block 'capture_repr': ('_repr_html_', '__repr__'), 'matplotlib_animations': True, + 'thebelab': { + "requestKernel": True, + "binderOptions": { + "repo": "sphinx-gallery/sphinx-gallery.github.io", + }, + }, } # Remove matplotlib agg warnings from generated doc when using plt.show diff --git a/setup.py b/setup.py index 7b120c820..867f47567 100644 --- a/setup.py +++ b/setup.py @@ -44,6 +44,7 @@ '_static/gallery-dataframe.css', '_static/no_image.png', '_static/broken_example.png', + '_static/thebelab_badge.svg', ]}, scripts=['bin/copy_sphinxgallery.sh', 'bin/sphx_glr_python_to_jupyter.py'], url="https://sphinx-gallery.github.io", diff --git a/sphinx_gallery/_static/gallery.css b/sphinx_gallery/_static/gallery.css index 848774f4c..290243761 100644 --- a/sphinx_gallery/_static/gallery.css +++ b/sphinx_gallery/_static/gallery.css @@ -111,6 +111,11 @@ div.sphx-glr-footer { text-align: center; } +div.sphx-glr-thebelab-badge { + margin: 1em auto; + vertical-align: middle; +} + div.sphx-glr-download { margin: 1em auto; vertical-align: middle; diff --git a/sphinx_gallery/_static/thebelab_badge.svg b/sphinx_gallery/_static/thebelab_badge.svg new file mode 100644 index 000000000..f8305ac99 --- /dev/null +++ b/sphinx_gallery/_static/thebelab_badge.svg @@ -0,0 +1 @@ +run withrun withThebe Lab & binderThebe Lab & binder \ No newline at end of file diff --git a/sphinx_gallery/gen_gallery.py b/sphinx_gallery/gen_gallery.py index 2a35c2788..986bc2ab0 100644 --- a/sphinx_gallery/gen_gallery.py +++ b/sphinx_gallery/gen_gallery.py @@ -19,6 +19,7 @@ import os import pathlib from xml.sax.saxutils import quoteattr, escape +import json from sphinx.errors import ConfigError, ExtensionError from sphinx.util.console import red @@ -77,6 +78,7 @@ 'inspect_global_variables': True, 'css': _KNOWN_CSS, 'matplotlib_animations': False, + 'thebelab': None, } logger = sphinx_compatibility.getLogger('sphinx-gallery') @@ -317,6 +319,17 @@ def call_memory(func): % (css, _KNOWN_CSS)) if gallery_conf['app'] is not None: # can be None in testing gallery_conf['app'].add_css_file(css + '.css') + if gallery_conf['thebelab'] is not None: + gallery_conf['thebelab']['selector'] = \ + ".sphx-glr-code>:not(.sphx-glr-output)" + gallery_conf['thebelab']['outputSelector'] = ".sphx-glr-output" + gallery_conf['thebelab']['predefinedOutput'] = True + gallery_conf['app'].add_js_file( + None, + body=json.dumps(gallery_conf['thebelab']), + type="text/x-thebe-config") + gallery_conf['app'].add_js_file( + "https://unpkg.com/thebelab@latest/lib/index.js") return gallery_conf diff --git a/sphinx_gallery/gen_rst.py b/sphinx_gallery/gen_rst.py index 5e45a4d3d..1a1e62cce 100644 --- a/sphinx_gallery/gen_rst.py +++ b/sphinx_gallery/gen_rst.py @@ -167,6 +167,23 @@ def __exit__(self, type_, value, tb): {0}\n
\n
""" +# Elements for ThebeLab +thebelab_badge = """\n + .. container:: sphx-glr-thebelab-badge + + .. image:: {0} + :target: # + :width: 260px + + .. raw:: html + + +""" + def codestr2rst(codestr, lang='python', lineno=None): """Return reStructuredText code block from code string""" @@ -871,15 +888,20 @@ def rst_blocks(script_blocks, output_blocks, file_conf, gallery_conf): code_rst = codestr2rst(bcontent, lang=gallery_conf['lang'], lineno=lineno) + '\n' + example_rst += ".. container:: sphx-glr-code\n" if is_example_notebook_like: - example_rst += code_rst - example_rst += code_output + example_rst += indent(code_rst, ' '*4) + if code_output.strip(): + example_rst += " .. container:: sphx-glr-output\n" + example_rst += indent(code_output, ' '*8) else: - example_rst += code_output + if code_output.strip(): + example_rst += "\n .. container:: sphx-glr-output\n" + example_rst += indent(code_output, ' '*8) if 'sphx-glr-script-out' in code_output: # Add some vertical space after output example_rst += "\n\n|\n\n" - example_rst += code_rst + example_rst += indent(code_rst, ' '*4) else: block_separator = '\n\n' if not bcontent.endswith('\n') else '\n' example_rst += bcontent + block_separator @@ -927,16 +949,21 @@ def save_rst_example(example_rst, example_file, time_elapsed, example_rst += ("**Estimated memory usage:** {0: .0f} MB\n\n" .format(memory_used)) - # Generate a binder URL if specified - binder_badge_rst = '' + # Generate a binder or ThebeLab URL if specified/required + badge_rst = '' + if gallery_conf['thebelab'] and "sphx-glr-code" in example_rst: + badge_rel_path = os.path.relpath( + os.path.join(glr_path_static(), 'thebelab_badge.svg'), + gallery_conf['src_dir']) + badge_rst += thebelab_badge.format( + "/" + badge_rel_path.replace(os.path.sep, '/')) if len(binder_conf) > 0: - binder_badge_rst += gen_binder_rst(example_file, binder_conf, - gallery_conf) + badge_rst += gen_binder_rst(example_file, binder_conf, gallery_conf) fname = os.path.basename(example_file) example_rst += CODE_DOWNLOAD.format(fname, replace_py_ipynb(fname), - binder_badge_rst, + badge_rst, ref_fname) example_rst += SPHX_GLR_SIG diff --git a/sphinx_gallery/tests/test_full.py b/sphinx_gallery/tests/test_full.py index f952eadb6..d4ea3339c 100644 --- a/sphinx_gallery/tests/test_full.py +++ b/sphinx_gallery/tests/test_full.py @@ -352,8 +352,10 @@ def test_logging_std_nested(sphinx_app): sphinx_app.srcdir, 'auto_examples', 'plot_log.rst') with codecs.open(log_rst, 'r', 'utf-8') as fid: lines = fid.read() - assert '.. code-block:: none\n\n is in the same cell' in lines - assert '.. code-block:: none\n\n is not in the same cell' in lines + assert ' .. code-block:: none\n\n'\ + ' is in the same cell' in lines + assert ' .. code-block:: none\n\n'\ + ' is not in the same cell' in lines def _assert_mtimes(list_orig, list_new, different=(), ignore=()): diff --git a/sphinx_gallery/tests/test_gen_rst.py b/sphinx_gallery/tests/test_gen_rst.py index 3adca0380..e41eab964 100644 --- a/sphinx_gallery/tests/test_gen_rst.py +++ b/sphinx_gallery/tests/test_gen_rst.py @@ -195,11 +195,12 @@ def test_rst_empty_code_block(gallery_conf, tmpdir): Paragraph 1 +.. container:: sphx-glr-code -.. code-block:: python + .. code-block:: python - # just a comment""" + # just a comment""" def test_script_vars_globals(gallery_conf, tmpdir): @@ -463,11 +464,12 @@ def test_pattern_matching(gallery_conf, log_collector, req_pil): gallery_conf.update(image_scrapers=(), reset_modules=()) gallery_conf.update(filename_pattern=re.escape(os.sep) + 'plot_0') - code_output = ('\n Out:\n\n .. code-block:: none\n' + code_output = ('\n Out:\n\n' + ' .. code-block:: none\n' '\n' - ' Óscar output\n' - ' log:Óscar\n' - ' $\\langle n_\\uparrow n_\\downarrow \\rangle$' + ' Óscar output\n' + ' log:Óscar\n' + ' $\\langle n_\\uparrow n_\\downarrow \\rangle$' ) warn_output = 'RuntimeWarning: WarningsAbound' # create three files in tempdir (only one matches the pattern)