You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
jax_intro renders its Chinese figure title as tofu — live to readers for three months #77
jax_intro.md:544 sets a Chinese figure title — ax.set_title("PRNG 密钥拆分树", fontsize=13, pad=10) — and the lecture carries no matplotlib font configuration. matplotlib falls back to DejaVu Sans, which has no CJK coverage, so the title renders as tofu: blank rectangles in an otherwise valid PNG.
This one is live. The font block was stripped in 4120371 (2026-04-12), the title was re-translated to Chinese two days later, and the block was never restored. This edition has published three times since, so readers have been seeing a boxed-out figure title for roughly three months. It is the only instance in the estate that has actually reached readers — the intermediate and intro editions have the same defect but have not published since it appeared.
Fix
Restore the block above first plotting use:
FONTPATH="_fonts/SourceHanSerifSC-SemiBold.otf"mpl.font_manager.fontManager.addfont(FONTPATH)
plt.rcParams['font.family'] = ['Source Han Serif SC']
Check the path against this repo before committing — the estate currently has two conventions for a byte-identical asset (lectures/fonts/ in 136 lectures across the other editions, lectures/_fonts/ in 17 here and in the engine's output), and standardising on one is a separate item in the program review.
Why CI did not catch it
The missing-glyph warning is a UserWarning raised inside the Jupyter kernel subprocess. Sphinx's -W only escalates Sphinx-logger warnings, so it never sees it; myst-nb renders it into the published page as stderr output instead. No CI log in the estate has ever contained findfont or Glyph … missing.
Separately and more seriously for this repo: the -n -W --keep-going on the sphinx-tojupyter step is inert — sphinx_tojupyter/builders/jupyter.py:297 calls exit(self.execution_status_code), initialised to 0 and never set, so that step swallows all execution errors. That also bounds what #76 achieves.
A runtime scan of the published site finds two more broken pages, and they are a different defect from this one.
bin/check-tofu in project-translation reads the missing-glyph warnings where they actually land — myst-nb renders the kernel's UserWarning into the published HTML as stderr output. Pointing it at https://quantecon.github.io/lecture-python-programming.zh-cn:
Both new pages already carry the font block, so they are not the same defect.
matplotlib.md — the block is set and then clobbered
The font block sits at line 83. The lecture then demonstrates style sheets: plt.style.use(style) at line 307, plt.style.use('default') at 395 and 451, and plt.rcParams.update(parameters) at 422. style.use() resets rcParams to the style's own values, which wipes font.family — hence glyphs missing from Liberation Sans, a style sheet's font rather than the fallback default.
Fix options: re-apply font.family after each style.use(), wrap the demonstrations in plt.rc_context(), or leave the demonstration figures' labels in English since the lecture is about styling rather than about the data.
pandas_panel.md — not yet explained
Its block was added 2026-03-20 (93dfab0), before both publish-2026may14 and publish-2026jun19, so the block was live at publish and the labels still lost their glyphs. That needs a targeted look; I have not diagnosed it. Worth checking for a later rcParams reset, an execution failure that skipped the block's cell, or a figure drawn before the block runs.
Method note
Source-level analysis found only jax_intro in this repo; the runtime scan found three. The two approaches are complementary — static analysis catches unpublished defects, runtime catches what readers actually see plus anything built dynamically. Both are worth keeping.
matplotlib and pandas_panel were the two extra pages found by the runtime scan in the comment above, and they were a different defect from the original one here — not a missing font block, but plt.style.use() and sns.set_theme() resetting font.family after the i18n cell had set it.
Verification
The publish workflow completed successfully against 0153f28 on 2026-08-07, so the fix is live rather than only on main. Fetching the three published pages and grepping for the missing-glyph warnings where they actually land — myst-nb renders the kernel's UserWarning into the page as stderr output, which is what bin/check-tofu keys on — returns clean on all three:
jax_intro.html — HTTP 200, no findfont / missing from font occurrences
matplotlib.html — HTTP 200, clean
pandas_panel.html — HTTP 200, clean
Scope note: this checks the three pages named in this issue, not the full estate. A repo-wide sweep would be worth running separately if the goal is to confirm no fourth page is affected.
The two structural gaps this issue documented are unchanged and are not closed by this: Sphinx's -W still cannot see kernel-side UserWarnings, and the sphinx-tojupyter step still swallows execution errors via the exit(self.execution_status_code) path. Those want the CI tofu detector proposed in the linked report rather than being tracked here.
Activity
mmcky commentedon Jul 24, 2026
A runtime scan of the published site finds two more broken pages, and they are a different defect from this one.
bin/check-tofuinproject-translationreads the missing-glyph warnings where they actually land — myst-nb renders the kernel'sUserWarninginto the published HTML as stderr output. Pointing it at https://quantecon.github.io/lecture-python-programming.zh-cn:jax_intromatplotlibpandas_panelBoth new pages already carry the font block, so they are not the same defect.
matplotlib.md— the block is set and then clobberedThe font block sits at line 83. The lecture then demonstrates style sheets:
plt.style.use(style)at line 307,plt.style.use('default')at 395 and 451, andplt.rcParams.update(parameters)at 422.style.use()resets rcParams to the style's own values, which wipesfont.family— hence glyphs missing from Liberation Sans, a style sheet's font rather than the fallback default.Fix options: re-apply
font.familyafter eachstyle.use(), wrap the demonstrations inplt.rc_context(), or leave the demonstration figures' labels in English since the lecture is about styling rather than about the data.pandas_panel.md— not yet explainedIts block was added 2026-03-20 (
93dfab0), before bothpublish-2026may14andpublish-2026jun19, so the block was live at publish and the labels still lost their glyphs. That needs a targeted look; I have not diagnosed it. Worth checking for a later rcParams reset, an execution failure that skipped the block's cell, or a figure drawn before the block runs.Method note
Source-level analysis found only
jax_introin this repo; the runtime scan found three. The two approaches are complementary — static analysis catches unpublished defects, runtime catches what readers actually see plus anything built dynamically. Both are worth keeping.Fix the live tofu in jax_intro, without trading it for subscript tofu (…
mmcky commentedon Aug 10, 2026
Verified fixed on the live site — closing.
What landed
jax_intromatplotlibfont.familyinsidedraw_graphsafter eachplt.style.use(style)0153f28)pandas_panelsns.set_theme(rc=...)0153f28)matplotlibandpandas_panelwere the two extra pages found by the runtime scan in the comment above, and they were a different defect from the original one here — not a missing font block, butplt.style.use()andsns.set_theme()resettingfont.familyafter the i18n cell had set it.Verification
The publish workflow completed successfully against
0153f28on 2026-08-07, so the fix is live rather than only onmain. Fetching the three published pages and grepping for the missing-glyph warnings where they actually land — myst-nb renders the kernel'sUserWarninginto the page as stderr output, which is whatbin/check-tofukeys on — returns clean on all three:jax_intro.html— HTTP 200, nofindfont/missing from fontoccurrencesmatplotlib.html— HTTP 200, cleanpandas_panel.html— HTTP 200, cleanScope note: this checks the three pages named in this issue, not the full estate. A repo-wide sweep would be worth running separately if the goal is to confirm no fourth page is affected.
The two structural gaps this issue documented are unchanged and are not closed by this: Sphinx's
-Wstill cannot see kernel-sideUserWarnings, and thesphinx-tojupyterstep still swallows execution errors via theexit(self.execution_status_code)path. Those want the CI tofu detector proposed in the linked report rather than being tracked here.