diff --git a/CHANGELOG.md b/CHANGELOG.md index d3e9552..70c3e24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## v0.1.0 +## v0.1.0 - 2022-02-05 ([full changelog](https://github.com/executablebooks/sphinx-thebe/compare/v0.0.10...4d1a60c5126ce633b1a36de43b4990b2f4d08730)) @@ -8,3 +8,24 @@ `thebe` will now "lazily load" its javascript only when a bootstrap button is pressed, rather than loading the Javascript on each page. This saves on bandwidth and pageload speed, since Thebe is generally _not_ used on a page even if it _could_ be used. + +## v0.0.10 - 2021-08-24 + +([full changelog](https://github.com/executablebooks/sphinx-thebe/compare/v0.0.9...e18d1bf94a8fa79476f035a349bd63d03bba83e7)) + +This is a minor release to conditionally load the JS on pages that have a "launch button". +This will save some load time on non-interactive pages. + +### Enhancements made + +- Option to conditionally load on pages, see [documentation for details](https://sphinx-thebe.readthedocs.io/en/latest/configure.html#only-load-js-on-certain-pages) [#30](https://github.com/executablebooks/sphinx-thebe/pull/30) ([@choldgraf](https://github.com/choldgraf)) + +### Other merged PRs + +- PIN: thebe v0.5.1 [#31](https://github.com/executablebooks/sphinx-thebe/pull/31) ([@choldgraf](https://github.com/choldgraf)) + +## v0.0.9 - 2021-08-21 + +### Updates + +- `sphinx-thebe` now uses the correct and latest version of thebe, since it has been renamed from `thebelab` to `thebe`. diff --git a/docs/changelog.md b/docs/changelog.md index e3dee76..8261b35 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,22 +1,2 @@ -# Changelog - -## v0.0.10 - 2021-08-24 - -([full changelog](https://github.com/executablebooks/sphinx-thebe/compare/v0.0.9...e18d1bf94a8fa79476f035a349bd63d03bba83e7)) - -This is a minor release to conditionally load the JS on pages that have a "launch button". -This will save some load time on non-interactive pages. - -### Enhancements made - -- Option to conditionally load on pages, see [documentation for details](https://sphinx-thebe.readthedocs.io/en/latest/configure.html#only-load-js-on-certain-pages) [#30](https://github.com/executablebooks/sphinx-thebe/pull/30) ([@choldgraf](https://github.com/choldgraf)) - -### Other merged PRs - -- PIN: thebe v0.5.1 [#31](https://github.com/executablebooks/sphinx-thebe/pull/31) ([@choldgraf](https://github.com/choldgraf)) - -## v0.0.9 - 2021-08-21 - -### Updates - -- `sphinx-thebe` now uses the correct and latest version of thebe, since it has been renamed from `thebelab` to `thebe`. +```{include} ../CHANGELOG.md +``` \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index 30bbac4..686f232 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -38,15 +38,15 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ["sphinx_thebe", "myst_parser", "sphinx_panels"] +extensions = ["myst_nb", "sphinx_copybutton", "sphinx_panels", "sphinx_thebe"] thebe_config = { "repository_url": "https://github.com/binder-examples/jupyter-stacks-datascience", "path_to_docs": "docs", # "repository_branch": "master", # "selector": ".thebe", - # "selector_input": , - # "selector_output": , + # "selector_input": "", + # "selector_output": "", # "codemirror-theme": "blackboard", # Doesn't currently work # "always_load": True, # To load thebe on every page } diff --git a/docs/configure.md b/docs/configure.md index 25ce467..aad6c9e 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -2,8 +2,9 @@ thebe-kernel: ir --- -# Configuring thebe +# Configuration +(configure:selector)= ## Change the HTML selector to mark interactive cells By default, `sphinx-thebe` will be run on any cells with the `thebe` class. diff --git a/docs/contribute.md b/docs/contribute.md index 8aa1231..af278b9 100644 --- a/docs/contribute.md +++ b/docs/contribute.md @@ -1,7 +1,16 @@ -# Contributing to Sphinx-Thebe +# Contribute to `sphinx-thebe` Thanks for your interest in contributing to `sphinx-thebe`, your contributions are welcome and appreciated 🎉. This page contains some information to help you get started. +## Design philosophy + +`sphinx-thebe` is designed to be a simple bridge between Sphinx and `thebe`. +It should not add a lot of extra functionality on top of Thebe, beyond special-casing its configuration for Sphinx objects and its build system. +It's primary goal is to make it pain-free to load `thebe.js` and apply it to websites generated by Sphinx with reasonable default choices. + +`sphinx-thebe` adds default configuration to support major Jupyter Notebook extensions in the Sphinx ecosystem. +Currently this means [MyST-NB](https://myst-nb.readthedocs.io/), but in the future we'd also like to support [nbsphinx](https://nbsphinx.readthedocs.io/) by default. + ## Contributing guide See the [ExecutableBooks developer guidelines](https://executablebooks.org/en/latest/contributing.html) for conventions and practices around developing `sphinx-thebe`. @@ -14,6 +23,13 @@ See the [ExecutableBooks developer guidelines](https://executablebooks.org/en/la - The sphinx package is contained in `sphinx_thebe/`. The `__init__.py` file contains the code that activates and loads the proper CSS and JS when Sphinx is run. - CSS and Javascript assets are in `sphinx_thebe/_static`. These handle the activation of `thebe` on a page via a button-click, and also make minor modification to the page's DOM to make it work well with `thebe`. +## Code style + +The JavaScript and CSS assets for this repository follow the default values for [prettier](https://prettier.io/). + +Python code follows `black` and `pep8` as described in [the EBP contributing guide](https://executablebooks.org/en/latest/contributing.html#coding-style). + + ## Installation for development To install `sphinx-thebe` for development clone and install `sphinx-thebe` locally: diff --git a/docs/examples/notebooks.md b/docs/examples/notebooks.md new file mode 100644 index 0000000..9d50ff1 --- /dev/null +++ b/docs/examples/notebooks.md @@ -0,0 +1,59 @@ +--- +jupytext: + formats: md:myst + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Jupyter Notebooks + +This page was written for [MyST-NB](https://myst-nb.readthedocs.io/). +It demonstrates `sphinx-thebe`'s usage with Jupyter Notebooks. + +Activate Thebe by clicking the launch button below. +You should then be able to run and edit the code cell in the notebook. + +```{thebe-button} Launch thebe +``` + +The outputs should be displayed below, but they will be collected by `sphinx-thebe` when it is activated so that they are cleared when you first run the cell. + +```{code-cell} +import numpy as np +import matplotlib.pyplot as plt + +# Create some fake data +data = np.random.randn(3, 1000) + +# Create a figure +fig, ax = plt.subplots() + +# Plot data +ax.scatter(data[0], data[1], c=np.abs(data[2]), s=np.abs(data[2])*100) +``` + +## Code style + +Thebe uses CodeMirror in the background, which uses different styles than pygments, which is used for static code syntax highlighting. + +The below code block is **static** and will not be converted with `thebe`. +We include it in order to compare the active Thebe cell's syntax highlighting with an inactive cell. + +``` +import numpy as np +import matplotlib.pyplot as plt + +# Create some fake data +data = np.random.randn(3, 1000) + +# Create a figure +fig, ax = plt.subplots() + +# Plot data +ax.scatter(data[0], data[1], c=np.abs(data[2]), s=np.abs(data[2])*100) +``` diff --git a/docs/examples/rst.rst b/docs/examples/rst.rst deleted file mode 100644 index 29266cf..0000000 --- a/docs/examples/rst.rst +++ /dev/null @@ -1,19 +0,0 @@ -================ -Using rST syntax -================ - -This documentation is written in `MyST Markdown `_, -so the syntax shown is a little bit different than many Sphinx sites, which are written -in reStructuredText. However, ``sphinx-thebe`` works fine in both. Here is the syntax -for using ``sphinx-thebe`` with rST: - -Adding a ``thebe`` class to a code block: - -.. code-block:: python - :class: thebe - - print("hi!") - -And adding a button: - -.. thebe-button:: Click me! diff --git a/docs/index.md b/docs/index.md index e9b74d7..bb6ad31 100644 --- a/docs/index.md +++ b/docs/index.md @@ -13,6 +13,8 @@ Make your code cells interactive with a kernel provided by [Thebe](http://thebe.readthedocs.org/) and [Binder](https://mybinder.org). +It uses the excellent [thebe project](http://thebe.readthedocs.org/), and pre-configures `thebe` to be compatible with common Jupyter-related patterns in the Sphinx ecosystem, such as [MyST-NB](https://myst-nb.readthedocs.io/). + For example, click the button below. Notice that the code block beneath becomes editable and runnable! @@ -27,15 +29,10 @@ print("hi") See [](use.md) for more information about what you can do with `sphinx-thebe`. -```{note} -This package is a Sphinx wrapper around the excellent [thebe project](http://thebe.readthedocs.org/), -a javascript tool to convert static code cells into interactive cells backed -by a kernel. -``` ## Install -To install `sphinx-thebe` first clonse and install it: +To install `sphinx-thebe` first clone and install it: ``` pip install sphinx-thebe @@ -61,7 +58,7 @@ For more information on using `sphinx-thebe`, see [](use.md). :hidden: use configure +examples/notebooks contribute -examples/rst changelog ``` diff --git a/docs/use.md b/docs/use.md index 43f845e..c818922 100644 --- a/docs/use.md +++ b/docs/use.md @@ -1,4 +1,8 @@ -# Using `sphinx-thebe` +# Use `sphinx-thebe` + +`sphinx-thebe` uses remote Jupyter kernels to execute your page's code and return the +results, and [Binder](https://mybinder.org) to run the infrastructure for execution. +You can do nearly anything with `sphinx-thebe` that you could do from within a Jupyter Notebook cell. ## Get started @@ -6,11 +10,6 @@ There are two steps to using `sphinx-thebe`. First, you must mark certain parts of your page as "ready for thebe". Next, you must insert a button onto the page to tell Thebe to initialize. -:::{admonition} Using reStructuredText vs. MyST Markdown -:class: tip -The examples on this page use **MyST Markdown syntax**, a form of markdown that works with Sphinx directives. You can also use reStructuredText if you wish. For information about reStructuredText vs. MyST Markdown, see [the MyST Parser documentation](https://myst-parser.readthedocs.io/en/latest/using/syntax.html) as well as [](examples/rst) for some tips. -::: - ### Mark elements for thebe By default, thebe will be run on any elements in your documentation that contain @@ -19,8 +18,9 @@ the class `thebe` and that have a ` element underneath them. You can add code blocks like so: ```` -```{code-block} +```{code-block} python :class: thebe +print("hello world!") ``` ```` @@ -37,36 +37,34 @@ directive: ``` ```` -The button looks like this: +### An example -```{thebe-button} +Here is what it looks like when you add the two snippets above in one example: + +First the code block: + +```{code-block} python +:class: thebe +print("hello world!") ``` -Clicking this button will activate Thebe on the page. If you'd like to manually -add your own button (e.g. with your own extension or theme), see [](add-custom-button). +And now the Thebe button: -```{note} -By default, `sphinx-thebe` will serve the Binder environment for the -[jupyter-stacks-datascience repository](https://github.com/binder-examples/jupyter-stacks-datascience). -See [](configure.md) for information on choosing your own environment. +```{thebe-button} ``` -## What can I do with `sphinx-thebe`? +Clicking this button will activate Thebe on the page. +If you'd like to manually add your own button (e.g. with your own extension or theme), see [](add-custom-button). -`sphinx-thebe` uses Jupyter kernels to execute your page's code and return the -results, and Binder in order to run the infrastructure for execution. This means that -you can do nearly anything with `sphinx-thebe` that you could do from within a -Jupyter Notebook cell. +### Customize your environment -```{admonition} You can customize your environment -:class: tip +By default, `sphinx-thebe` will serve the Binder environment for the +[jupyter-stacks-datascience repository](https://github.com/binder-examples/jupyter-stacks-datascience). +See [](configure.md) for information on choosing your own environment. -You can customize the environment that powers your interactive code sessions using -a Binder repository. This may allow for different kinds of functionality depending on -the libraries that are installed. See [](configure.md) for more information. -``` +## A few examples -For example: +For example, click the button below (if you have not already clicked the button at the top of the page) and see the sections underneath to watch `sphinx-thebe` in action: ```{thebe-button} Launch examples below! ``` @@ -100,6 +98,52 @@ import matplotlib.pyplot as plt plt.scatter(*data, c=data[0], s=200) ``` +## Structure of a `thebe` cell + +`sphinx-thebe` can work with two basic code cell structures: + +1. **A single code cell**. In this case, there is just a single code cell that has content that should be made executable, like so: + + ```html +
+
print("hi!")
+
+ ``` +2. **An input/output cell**. Jupyter tools define code cells as a combination of inputs and outputs. + For example: + + ```html +
+
+
print("hi!")
+
+
+ ...outputs here... +
+
+ ``` + + In this case, `sphinx-thebe` will treat the `cell_output` in a special fashion, so that it is cleared when you first run its corresponding input code. + + +(use:selectors)= +## Selectors `sphinx-thebe` looks for by default + +By default `sphinx-thebe` will look for two types of code blocks to turn into interactive cells: + +- Cells that match a custom class you can add manually. + It will match: + - Whole cells: match `.thebe` + - Cell inputs: match `pre` + - Cell outputs: match `.output` +- Cells that match [MyST-NB code cells](https://myst-nb.readthedocs.io/). + It will match: + - Whole cells: match `.cell` + - Cell inputs: match `.cell_input` + - Cell outputs: match `.cell_output` + +To customize the selectors that `sphinx-thebe` looks for, see [](configure:selector). + ## Interactive outputs Interactive outputs work with `sphinx-thebe` **if their web dependencies are loaded**. diff --git a/setup.py b/setup.py index eb823ab..5204d88 100644 --- a/setup.py +++ b/setup.py @@ -50,11 +50,12 @@ install_requires=["sphinx>=3.5,<5"], extras_require={ "sphinx": [ - "myst-parser[sphinx]", + "matplotlib", + "myst-nb", "sphinx-book-theme", - "jupyter-sphinx", + "sphinx-copybutton", "sphinx-panels", ], - "testing": ["pytest", "pytest-regressions", "beautifulsoup4"], + "testing": ["matplotlib", "pytest", "pytest-regressions", "beautifulsoup4"], }, ) diff --git a/sphinx_thebe/__init__.py b/sphinx_thebe/__init__.py index a5a6281..9bc8929 100644 --- a/sphinx_thebe/__init__.py +++ b/sphinx_thebe/__init__.py @@ -26,9 +26,9 @@ def init_thebe_default_config(app, env, docnames): thebe_config = app.config.thebe_config defaults = { "always_load": False, - "selector": ".thebe", + "selector": ".thebe,.cell", "selector_input": "pre", - "selector_output": ".output", + "selector_output": ".output, .cell_output", } for key, val in defaults.items(): if key not in thebe_config: diff --git a/sphinx_thebe/_static/sphinx-thebe.js b/sphinx_thebe/_static/sphinx-thebe.js index 0309401..7626dbb 100644 --- a/sphinx_thebe/_static/sphinx-thebe.js +++ b/sphinx_thebe/_static/sphinx-thebe.js @@ -69,6 +69,7 @@ var modifyDOMForThebe = () => { // Clean up the language to make it work w/ CodeMirror and add it to the cell dataLanguage = detectLanguage(kernelName); + // Re-arrange the cell and add metadata if (codeCellText) { codeCellText.setAttribute("data-language", dataLanguage); codeCellText.setAttribute("data-executable", "true"); @@ -79,6 +80,11 @@ var modifyDOMForThebe = () => { $(codeCellOutput).insertAfter(codeCellText); } } + + // Remove sphinx-copybutton blocks, which are common in Sphinx + codeCell.querySelectorAll("button.copybtn").forEach((el) => { + el.remove(); + }); }); }; diff --git a/tests/test_build.py b/tests/test_build.py index 5042b5f..471f707 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -1,7 +1,7 @@ from bs4 import BeautifulSoup from pathlib import Path from subprocess import run -from shutil import copytree, rmtree +from shutil import copy, copytree, rmtree import pytest @@ -18,14 +18,18 @@ class SphinxBuild: path_html = path_build.joinpath("html") path_pg_index = path_html.joinpath("index.html") path_pg_config = path_html.joinpath("configure.html") + path_pg_ntbk = path_html.joinpath("examples/notebooks.html") cmd_base = ["sphinx-build", ".", "_build/html", "-a", "-W"] def copy(self, path=None): """Copy the specified book to our tests folder for building.""" if path is None: path = path_docs + path_changelog = path / "../CHANGELOG.md" if not self.path_tmp_docs.exists(): copytree(path, self.path_tmp_docs) + # Copy since it's loaded with an `include` directive + copy(path_changelog, self.path_tmp) def build(self, cmd=None): """Build the test book""" @@ -69,6 +73,20 @@ def test_sphinx_thebe(file_regression, sphinx_build): lb_text = "\n\n".join([ii.prettify() for ii in launch_buttons]) file_regression.check(lb_text, basename="launch_buttons", extension=".html") + # Check for MyST-NB cell structure to make sure it stays the same + # If this breaks, we'll need to update our default cell selectors + soup_nb = BeautifulSoup( + Path(sphinx_build.path_pg_ntbk).read_text(), "html.parser" + ) + cell = soup_nb.select(".cell")[0] + + # Remove the *content* of input/output since we only care about the DOM structure + cell.select(".cell_input pre")[0].clear() + cell.select(".cell_output")[0].clear() + file_regression.check( + cell.prettify(), basename="myst-nb__cell", extension=".html" + ) + def test_lazy_load(file_regression, sphinx_build): """Test building with thebe.""" diff --git a/tests/test_build/myst-nb__cell.html b/tests/test_build/myst-nb__cell.html new file mode 100644 index 0000000..41f0cd1 --- /dev/null +++ b/tests/test_build/myst-nb__cell.html @@ -0,0 +1,11 @@ +
+
+
+
+

+   
+
+
+
+
+
diff --git a/tox.ini b/tox.ini index f40241c..209fde2 100644 --- a/tox.ini +++ b/tox.ini @@ -11,12 +11,12 @@ # then then deleting compiled files has been found to fix it: `find . -name \*.pyc -delete` [tox] -envlist = py37-sphinx3 +envlist = py39-sphinx3 [testenv] usedevelop = true -[testenv:py{36,37,38,39}-sphinx{3,4}] +[testenv:py{37,38,39}-sphinx{3,4}] extras = sphinx,testing deps = sphinx3: sphinx>=3,<4 @@ -39,6 +39,8 @@ commands_post = echo "open file://{toxinidir}/docs/_build/{posargs:html}/index.h [testenv:docs-live] description = Build the documentation and launch browser extras = sphinx +deps = + sphinx-autobuild setenv = SKIP_CONTRIBUTE = true SKIP_TEAM = true