A few weeks ago, I started having a look again at Styled Exercises in Jupyter Book (but still not JupyterLab…). I had intended to include a description of sphinx-exercise
in that post, but hit “publish” too quickly. Since that post, things have also moved on a bit in the sphinx-exercise
package; but they seem settled now, so here’s a quick review of some the things you can get up to with sphinx-exercise
rendered elements in Jupyter Book HTML publications.
The first thing to note is that you can have “question” and “answer” style exercise blocks.
The “question” style exercise blocks are used to define an activity or exercise.
The exercise block is defined using an {exercise}
directive. This should include a unique :label:
attribute that provides an identifier for the exercise, and might optionally include a :class
attribute to allow particular styling of the element. The class attribute can also call on “off-the-shelf” classes shuch as "dropdown"
. The directive can also include title text in the same way that an {admonition}
block does.
Exercises are numbered by default and automatically generated based on a simple exercise count throughout the whole book; the :nonumber:
flag attribute can be set to disable auto-numbering. Currently, I donlt think “chapterised” numbering is available, eg with the exercise numbers enumerated within a chapter and preceded by the chapter number.
At the current time, Jupyter Book admonitions do not support the nesting of code cells within an admonition block. This inability to embed executable code cells in the exercise admonition block rather limits its utility if you need to create an activity with some executable code as part of the setup. (That said, you might also argue that learners should manually copy and paste, or rekey, any code provided as part of an exercise into a code cell themselves.)
A solution to this is to use a new gated syntax. Currently, this only works with sphinx in the production of Jupyter Book content. It is not (yet?) an official part of the MyST syntax and it is not supported by JupyterLab-MyST.
The gated exercise admonition requires two admonitions to be used, one at the start of the gated area and one at the end:
```{exercise-start}
:label: example1
```
Add markdown, code cells, etc., as required here...
```{exercise-end}
```
Here’s how it looks on being with in the rendered Jupyter Book:
Expanding the block shows the “nested” markdown and code cells, along with code cell output:
As with other referenceable types, exercises can be referenced and the reference text will be automatically generated. For example, we can generate references using {ref}
or {numref}
roles. Here’s some example source MyST markdown:
A simple reference to the exercise using a `{ref}` role, {ref}`ex1g`, or a more elaborate one using a `{numref}` role, {numref}`My custom {number} title and {name} <ex1g>`
This renders as follows:
As well as defining exercises, we can also define (linked) solutions using a {solution}
directive. This directive requires an exercise’s :label:
identifier as an argument, rather than an optional title . Non-executable code can be emnedded in a {solution}
directive block in the normal way (the {solution}
directive needs to be fenced by more backticks than any code fence blocks it contains, and may be collapsed by default by setting the :class:
to dropdown
, in the normal way.
An exercise solution can also be defined using gated directives, specifically {solution-start} EXERCISE_LABEL_ID
and {solution-end}
:
```{solution-start} exercise-test
:label: solution-gated-test
:class: dropdown
```
Example Solutions
Example code cell without tags and no space after fence:
```{code-cell} python3
# Code cell defined using:
# {code-cell} python3
print("another hello")
```
```{solution-end}
```
For the solution, the :label:
unique identifier is optional.
The title to the solution block is derived from the title of the exercise it relates to:
The style of the solution block is rather simpler than that of the exercise block. However, an optional :class:
element defined in the {solution-start}
admonition (and which can be set to dropdown
to collapse the solution by default), can be passed and thenceforth used to style the solution cell(s).
The gated admonitions are very powerful, and essentially allow you to wrap a sequence of markdown and code cell blocks within a referenceable div
element in the output book HTML.
At the current time, there is no mechanism for rendering the gated admonitions in JupyterLab or RetroLab. In addition, the juptyerlab-myst
extension does not support the {exercise}
directive.
Something that would be really useful in the short term would be a jupyterlab-myst-exercise
JuptyerLab extension that demonstrated a minimal solution to extending jupyterlab-myst
to support the exercise
and solution
directives, but not the gated directives. Not only would this improve compatability of exercise rendering across Jupyter Book and JupyterLab/RetroLab, it would also demonstrate how to extend juptyerlab-myst
.
Something else I’m looking forward to is a separation of the “gated directive” support into a more abstract form (for example, a gated directive class that was then extended to provide the gated exercise and gated solution directives). A MyST enhancement proposal may be on the way regarding this, and is perhaps more likely now that there is an official MyST-specification available [repo].
Elsewhere, there is extension support for collapsible headings in JupyterLab/RetroLab, (assuming it hasn’t rotted?!), so that could perhaps also be cribbed as a way of providing grouped styling and collapsible display between hidden styled exercise or solution start and end blocks (though personally, I’d prefer exercise-start
and exercise-end
etc. tagged cells to identify gated fences. These could either apply to the first/last cell in the exercise (or solution for gated solutions) or could be otherwise empty markdown cells containing just the appropriate tags).