print("hi!")+
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
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 +++ ``` +2. **An input/output cell**. Jupyter tools define code cells as a combination of inputs and outputs. + For example: + + ```html +print("hi!")+++ ``` + + 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 @@ +++print("hi!")++ ...outputs here... +++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+++++ ++++