Playing with Hybrid Cell Exercise Blocks in Jupyter Book via sphinx-exercise

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).

Author: Tony Hirst

I'm a Senior Lecturer at The Open University, with an interest in #opendata policy and practice, as well as general web tinkering...

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: