Repository navigation
FIX: Keep figures inside exercise and solution boxes from floating in LaTeX - #95
Conversation
… LaTeX
Sphinx's LaTeX writer increments no_latex_floats when it opens an
admonition, so visit_figure writes \begin{figure}[H] rather than a
float. The exercise and solution visitors open the same sphinxadmonition
box but never touched the counter, so a captioned figure inside them was
written as \begin{figure}[htbp] and LaTeX stopped with "Not in outer par
mode".
Open and close the box through a helper pair that also maintains the
counter, used by all three LaTeX visit/depart pairs (exercise_node,
exercise_enumerable_node and solution_node; gated blocks are transformed
into these nodes).
Add a latexfigure test root with a figure in each kind of box, plus one
after them that must still float, so the counter is shown to be restored.
Fixes #94
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Note
Copilot was unable to complete this review at the selected tier due to model policy or plan restrictions.
🟢 Approval recommended
The change is narrowly focused on correcting LaTeX figure handling in exercise/solution boxes and is backed by a new dedicated LaTeX regression test that inspects generated output to confirm [H] is used inside boxed environments and normal float behavior is restored afterward.
No open issues 🎉
What changed in this PR
This pull request fixes LaTeX/PDF builds failing with LaTeX Error: Not in outer par mode when captioned figures appear inside exercise / solution boxes (including gated *-start / *-end blocks). It aligns the extension’s LaTeX output with Sphinx’s admonition behavior by ensuring figures inside these boxed environments are emitted as non-floating [H] figures, while figures outside remain normal floats.
Changes:
- Add shared LaTeX admonition enter/exit helpers and update
no_latex_floatsbookkeeping so in-box figures use[H]. - Add a dedicated LaTeX test root plus a regression test asserting
[H]inside boxes and[htbp]after boxes (verifying the counter is restored). - Document the fix under
[Unreleased]in the changelog.
| File | Summary |
|---|---|
tests/test_latex.py |
Adds a regression test that inspects generated LaTeX to verify figure placement is [H] inside exercise/solution boxes and [htbp] outside. |
tests/books/test-latexfigure/index.rst |
New test content covering numbered/unnumbered exercises, solutions, and gated blocks containing captioned figures, plus a figure after the boxes. |
tests/books/test-latexfigure/conf.py |
Minimal Sphinx configuration for the new LaTeX regression test root. |
sphinx_exercise/nodes.py |
Centralizes LaTeX admonition open/close logic and increments/decrements no_latex_floats so figures inside exercise/solution boxes render as [H]. |
CHANGELOG.md |
Adds an [Unreleased] entry describing the LaTeX figure-in-admonition fix. |
This pull request is too complex or risky for automated approval.
Review effort: Balanced
Add a notebook to the latexfigure test root with a code-cell whose
mystnb figure metadata puts a captioned figure inside a gated solution,
which is how Jupyter Book projects usually hit the float bug. Its
outputs are stored and nb_execution_mode is "off", so no kernel runs.
Match figures by their :name: label instead of their caption. The
caption regex stopped at the first closing brace, so a caption with
markup such as \sphinxhyphen{} was misread, and a figure without a
caption was paired with the next one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes #94
A captioned figure inside an
exerciseorsolutionbox, including gatedexercise-start/solution-startblocks, breaks the LaTeX/PDF build withLaTeX Error: Not in outer par mode. Sphinx's LaTeX writer incrementsno_latex_floatswhenever it opens one of its own admonitions, andvisit_figurethen writes\begin{figure}[H]instead of a float. sphinx-exercise opens the samesphinxadmonitionbox but never touched the counter, so figures inside it were written as\begin{figure}[htbp], which LaTeX rejects inside the box.Changes
sphinx_exercise/nodes.py: the three LaTeX visit/depart pairs (exercise_node,exercise_enumerable_node,solution_node) now open and close the box through a smallvisit_latex_admonition/depart_latex_admonitionhelper pair that also increments and decrementsself.no_latex_floats. Gated blocks are transformed into these nodes, so they are covered too. HTML output is unchanged.tests/books/test-latexfigure/andtests/test_latex.py::test_latex_figure_in_admonition: a figure in a numbered exercise, an unnumbered exercise, a solution, a gated exercise and a gated solution, plus a notebook whosecode-cellhasmystnb: figure:metadata inside a gated solution, which is how Jupyter Book projects usually hit this. Each is asserted to be[H]inside its box, and a figure after the boxes must still be[htbp], which shows the counter is restored on depart. The notebook stores its outputs and the root setsnb_execution_mode = "off", so no kernel runs. Figures are matched by their:name:label rather than by caption text, so captions with LaTeX markup are handled. The existing file_regression fixtures are untouched.CHANGELOG.md: entry under[Unreleased].Testing
main(every in-box figure comes out as[htbp]) with Sphinx 6.2.1, 7.4.7, 8.1.3, 8.2.3 and 9.1.1, and passes with this change on all of them. CI does not test Sphinx 9, but the package allows it at runtime, and it usesno_latex_floatsthe same way.latexmk -pdfon Sphinx 7.4.7: the new test root, including the notebook figure, stops with "Not in outer par mode" onmainand compiles with this change.See QuantEcon/meta#297
🤖 Generated with Claude Code