Skip to content
Taliesin User Guide

4 Executable content

Executable cells against a warm kernel: what a language label decides, how to set up Python, and how an output becomes a block.

This is the heart of “code-block-enhanced HTML.” A fenced cell tagged with a language runs against a warm Jupyter kernel; its outputs become their own blocks tied to the cell’s id. Everything on this page is produced from its own .tmd source.

4.1 Code cells

import numpy as np
np.arange(5).sum()
np.int64(10)

On save, only cells whose source changed (plus downstream cells) re-execute, and the kernel never cold-starts. {python} cells run against a warm kernel: point the server at a Python with ipykernel via TALIESIN_PYTHON (one-time setup). A cell in any other language, or with no kernel available, renders as highlighted source rather than failing. The first cell of a session pays the interpreter’s own start-up (booting Python and importing what the cell reaches for); every edit after that reuses the kernel that boot produced.

Output streams into the preview as the cell produces it, beside a running-elapsed badge, so a training loop’s epoch lines appear as they are printed rather than all at once at the end. Progress bars that redraw with a carriage return (tqdm and friends) render as one updating line, not one line per frame.

The kernel is resilient: if it crashes mid-session the server respawns it on the next run, and a wedged cell is interrupted instead of locking up the kernel. The cap is on silence, not on runtime: a cell that produces no output at all for TALIESIN_CELL_SILENCE seconds (default 600) is sent SIGINT, and the budget resets on every line the cell prints, so a long job that reports progress runs to completion however long it takes. The dev menu’s Restart kernel action drops the kernel, re-runs every cell against a fresh one, and reloads so define values re-bind. See Troubleshooting for the symptoms these answer.

4.1.1 echo, include, and cache: three different switches

These three boolean options look similar but control different stages, and it helps to keep them straight:

OptionDoes the cell run?Source shown?Output shown?Cached in _freeze/?
(defaults)yesyesyesyes
#| echo: falseyesnoyesyes
#| include: falseyesnonoyes
#| cache: falseyesyesyesnever

echo: false hides the source listing only, for when the reader should see a plot or a table but not the code that made it.

include: false goes further and hides both source and output, but the cell still executes (kernel state is kept), so a later cell still sees its variables. This is the option for a setup cell whose result you never want to see but whose state you need downstream. To skip execution entirely, run the whole document with --no-exec / TALIESIN_NO_EXEC.

cache: false is orthogonal to both: the cell runs and shows normally, but its output is never frozen, and everything downstream of it re-runs and is likewise never cached (a downstream output follows from this cell’s value, which the cache key cannot see: only its code).

#| include: false
#| cache: false
import pandas as pd
df = pd.read_csv("data.csv")
The cache key sees your code, not your data

The _freeze/ key is built from cell code, every upstream cell’s code, and the interpreter’s identity, which means it is blind to everything a cell reads that is not code: a data file, an environment variable, a URL it fetches, the clock, an installed library you upgraded in place.

Edit data.csv without touching a line of Python and the numbers on the page will not change, because as far as the cache is concerned nothing happened. Mark any cell with an out-of-band input #| cache: false, as above.

To force one fresh run without editing anything, use the dev-menu Restart kernel or run with TALIESIN_NO_CACHE.

cache (and only cache) also exists as a document-wide default under the execute: front-matter key, and a per-cell #| option always overrides it. See the cell-options reference for the full option table and the Configuration reference for execute:.

4.1.2 Captions: figures, listings, and tables

#| fig-cap: captions a cell’s output and numbers it as a figure; #| lst-cap: and #| tbl-cap: do the same for a code listing and for a table the cell publishes. Pair any of them with a matching #| label: fig-x / lst-x / tbl-x and the output is cross-referenceable as @fig-x, @lst-x or @tbl-x from anywhere in the book. The cell-options reference has the routing rules.

4.1.3 Publishing a table, rather than printing one

#| tbl-cap: captions whatever the cell publishes, and the shortest way to write a table publishes something you did not mean. The trap is quiet: the page builds, the lint is clean, and the defect is visible only in the rendered output.

A bare df on its own line emits pandas’ own repr: a table tagged border="1" and class="dataframe", a row-index column, and a <style scoped> block, which no browser scopes, so it leaks to the whole page. Publish the markup yourself instead: to_html(index=False, border=0) on a DataFrame, or any other HTML string handed to display(HTML(…)), emits plain markup that the page’s own table styling reaches. Keep the index when its labels carry meaning (a describe() table, where they name the statistic) and drop it when it is a bare row number. Table 4.1 below is a hand-built HTML string published exactly that way.

import numpy as np
from IPython.display import HTML, display

rows = ""
for seed in (0, 1, 2):
    x = np.random.default_rng(seed).normal(size=1000)
    rows += f"<tr><td>{seed}</td><td>{x.mean():+.3f}</td><td>{x.std():.3f}</td></tr>"
display(HTML(f"<table><tr><th>seed</th><th>mean</th><th>sd</th></tr>{rows}</table>"))
Table 4.1: Mean and standard deviation of 1000 normal draws, by seed
seedmeansd
0-0.0480.977
1-0.0540.986
2-0.0221.013

4.1.4 Folded code

#| code-fold: true collapses a listing behind a summary (label it with code-summary), so long setup code stays out of the way until clicked, as Listing 4.1 does:

Listing 4.1: A setup cell, folded behind its summary.
Show the setup
import numpy as np
rng = np.random.default_rng(0)
draws = rng.normal(size=10_000)
(round(float(draws.mean()), 3),
 round(float(draws.std()), 3))
(0.006, 0.998)

4.1.5 Writing a figure to disk

Inline {python} figures are themed for the web (Figure 4.1 below has a transparent background and neutral grey axes that read on light and dark), but that theming never touches global rcParams, so a plain fig.savefig("x.pdf") in a cell already writes a clean, print-ready figure (black on white):

import matplotlib.pyplot as plt, numpy as np, os
fig, ax = plt.subplots()
ax.plot(np.arange(40), np.exp(-0.1 * np.arange(40)))
ax.set_xlabel("iteration"); ax.set_ylabel("loss")
os.makedirs("figures", exist_ok=True)
fig.savefig("figures/loss.pdf")
plt.show()
Figure 4.1: Loss curve

A cell runs with its document’s directory as the working directory, so a relative path lands beside the source rather than wherever you launched Taliesin.

4.2 Live, browser-side content

{js} cells run in your browser rather than against the kernel. See Interactive and explorable documents.