Skip to content

Pin kaleido<1.0 in both containers to fix ChromeNotFoundError - #85

Merged
mmcky merged 1 commit into
mainfrom
fix/kaleido-pin-v0
Jun 22, 2026
Merged

mmcky merged 1 commit into
mainfrom
fix/kaleido-pin-v0

Conversation

@mmcky

@mmcky mmcky commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Problem

The container tests (test-containers-lectures.yml) fail at notebook execution with ChromeNotFoundError from kaleido on Plotly static-image export — BCG_complete_mkts.md, BCG_incomplete_mkts.md, knowing_forecasts_of_others.md. This is the same "unpinned dependency bumped a major" class as #28.

Root cause

kaleido was pip-installed unpinned and resolved to v1.x (currently 1.3.0). kaleido v1 dropped the self-contained chromium and requires a separately provisioned Chrome. Both Dockerfiles ran kaleido.get_chrome_sync() at build time — but that downloads Chrome under /root/.cache (build runs as root), while GitHub Actions container jobs run with HOME=/github/home, so at test runtime kaleido looks in the wrong place and can't find Chrome.

Fix

  • Pin kaleido<1.0 in both containers/quantecon/environment.yml and containers/quantecon-build/environment.yml.
  • Drop the now-invalid kaleido.get_chrome_sync() build step from both Dockerfiles (v0 has no such API; leaving it would fail the build).

kaleido 0.2.x bundles its own chromium inside the package (location-independent, so the HOME difference is irrelevant) and still exports images on the current plotly. The existing apt chromium runtime libs (libnss3, libgbm1, libasound2t64, …) are retained for the bundled browser.

Verification

Installed plotly==6.7.0 (the version the lean image resolves) + kaleido==0.2.1 on Python 3.13 and exported a PNG:

plotly 6.7.0 | kaleido 0.2.1
to_image(engine=kaleido) OK -> 23957 bytes

Only DeprecationWarnings are emitted (Plotly notes kaleido v0 support is deprecated) — no error. The full image's plotly 6.3.0 is older and on the same deprecation (warn, not error) path.

Caveat / follow-up

This is a pragmatic stopgap consistent with long-standing QuantEcon practice: kaleido v0 is deprecated by Plotly, so a future Plotly release could eventually drop it. Migrating to kaleido v1 with a system-provisioned Chrome (install google-chrome/chromium to a fixed path and point kaleido at it via env, independent of HOME) is the durable path and is left as a follow-up.

Notes

🤖 Generated with Claude Code

`kaleido` was pip-installed unpinned and resolved to v1.x, which dropped the
self-contained chromium and requires a separately provisioned Chrome. The
Dockerfiles ran `kaleido.get_chrome_sync()` at build time (as root, into
/root/.cache), but GitHub Actions container jobs run with HOME=/github/home, so
at test runtime kaleido looked in the wrong place and Plotly static-image export
failed with ChromeNotFoundError (BCG_complete_mkts.md, BCG_incomplete_mkts.md,
knowing_forecasts_of_others.md) — the same "unpinned dep bumped a major" class
as #28.

Pin `kaleido<1.0` in both images and drop the now-invalid get_chrome_sync build
step (v0 has no such API). kaleido 0.2.x bundles its own chromium
(location-independent, so unaffected by the HOME difference) and still exports
PNGs on the current plotly — verified plotly 6.7.0 + kaleido 0.2.1 produces a
PNG (deprecation warning only, no error). The existing apt chromium runtime libs
are retained for the bundled browser.

Migrating to kaleido v1 with a system-provisioned Chrome is left as a future
follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 22, 2026 01:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR stabilizes Plotly static-image export in the quantecon and quantecon-build container images by preventing kaleido from upgrading to v1.x (which requires an externally provisioned Chrome) and by removing the now-invalid build-time Chrome download step.

Changes:

  • Pin kaleido<1.0 in both container environment.yml files to keep using kaleido v0.x (bundled chromium).
  • Remove the kaleido.get_chrome_sync() build step from both container Dockerfiles.
  • Document the fix and rationale in CHANGELOG.md under Unreleased → Fixed.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
containers/quantecon/environment.yml Pin kaleido<1.0 in pip deps with rationale for CI runtime HOME behavior.
containers/quantecon/Dockerfile Remove build-time kaleido.get_chrome_sync() invocation.
containers/quantecon-build/environment.yml Pin kaleido<1.0 in pip deps with matching rationale.
containers/quantecon-build/Dockerfile Remove build-time kaleido.get_chrome_sync() invocation.
CHANGELOG.md Add Unreleased changelog entry describing the kaleido pin + removed Chrome download step.

@mmcky
mmcky merged commit b49824a into main Jun 22, 2026
1 check passed
@mmcky
mmcky deleted the fix/kaleido-pin-v0 branch June 22, 2026 01:34
mmcky added a commit that referenced this pull request Aug 5, 2026
The container smoke tests could not fail. The fixture was inert for three
independent reasons, not the one #108 names:

  1. `execute_notebooks: "off"` in _config.yml
  2. its one code block was a plain ```python fence, not a {code-cell}
  3. no jupytext front matter, so the file was never a notebook at all

An image whose numpy, matplotlib or kaleido was completely broken therefore
still produced a green container test. Fixing only (1) and (2) would still not
be enough: myst-nb reports a failed cell as a *warning*, so a raising cell
yields a published book and exit 0 unless `-W` or `raise_on_error` is set.

The fixture now executes real cells that ASSERT on results — a stack that
imports cleanly but computes wrong answers is exactly what a smoke test should
catch, and printing alone would not. Coverage: numpy/scipy eigenvalues against
an exact closed form, a pandas groupby, a real matplotlib PNG render, and a
plotly static export through kaleido. That last one is the #85 path and the
reason both images pin kaleido<1.0.

test-container.yml now runs both images as real `container:` jobs instead of
`docker run`. Not cosmetic: a container job forces HOME=/github/home while
docker run leaves HOME=/root, and that is precisely why #85 passed these tests
while failing test-containers-lectures.yml, which does use a container job. The
job asserts HOME explicitly so a silent revert is caught, and the lean image
gains pdflatex coverage it never had.

smoke-test.sh --self-test stages a deliberate exception and fails if the build
does NOT go red — because the defect being fixed here is silence, and a fixture
that cannot fail reads as coverage. It attributes the red on captured stdout
rather than reports/*.err.log: verified locally that with raise_on_error myst-nb
aborts before Sphinx writes that file, unlike build-fail-guard's path.

Verified locally (minus the plotly cell — kaleido is not in my env): the cells
genuinely execute, with real values in the built HTML; the book builds clean
under -W with zero warnings; a staged raising cell exits 1; the sentinel appears
in captured output; and reports/ is indeed absent. The plotly/kaleido cell and
the container-job HOME assertion can only be verified in CI — workflow_dispatch
on this branch, since workflow_run only fires for workflows already on the
default branch.

Image size is now read from the manifest, since a container job cannot run
docker. That is COMPRESSED layer bytes — a different, smaller number than the
old `docker images` figure, so the two series are not comparable.

Refs #108

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mmcky added a commit that referenced this pull request Aug 5, 2026
The container smoke tests could not fail. The fixture was inert for three
independent reasons, not the one #108 names:

  1. `execute_notebooks: "off"` in _config.yml
  2. its one code block was a plain ```python fence, not a {code-cell}
  3. no jupytext front matter, so the file was never a notebook at all

An image whose numpy, matplotlib or kaleido was completely broken therefore
still produced a green container test. Fixing only (1) and (2) would still not
be enough: myst-nb reports a failed cell as a *warning*, so a raising cell
yields a published book and exit 0 unless `-W` or `raise_on_error` is set.

The fixture now executes real cells that ASSERT on results — a stack that
imports cleanly but computes wrong answers is exactly what a smoke test should
catch, and printing alone would not. Coverage: numpy/scipy eigenvalues against
an exact closed form, a pandas groupby, a real matplotlib PNG render, and a
plotly static export through kaleido. That last one is the #85 path and the
reason both images pin kaleido<1.0.

test-container.yml now runs both images as real `container:` jobs instead of
`docker run`. Not cosmetic: a container job forces HOME=/github/home while
docker run leaves HOME=/root, and that is precisely why #85 passed these tests
while failing test-containers-lectures.yml, which does use a container job. The
job asserts HOME explicitly so a silent revert is caught, and the lean image
gains pdflatex coverage it never had.

smoke-test.sh --self-test stages a deliberate exception and fails if the build
does NOT go red — because the defect being fixed here is silence, and a fixture
that cannot fail reads as coverage. It attributes the red on captured stdout
rather than reports/*.err.log: verified locally that with raise_on_error myst-nb
aborts before Sphinx writes that file, unlike build-fail-guard's path.

Verified locally (minus the plotly cell — kaleido is not in my env): the cells
genuinely execute, with real values in the built HTML; the book builds clean
under -W with zero warnings; a staged raising cell exits 1; the sentinel appears
in captured output; and reports/ is indeed absent. The plotly/kaleido cell and
the container-job HOME assertion can only be verified in CI — workflow_dispatch
on this branch, since workflow_run only fires for workflows already on the
default branch.

Image size is now read from the manifest, since a container job cannot run
docker. That is COMPRESSED layer bytes — a different, smaller number than the
old `docker images` figure, so the two series are not comparable.

Refs #108

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mmcky added a commit that referenced this pull request Aug 5, 2026
* test(containers): make the container fixture able to fail (#108)

The container smoke tests could not fail. The fixture was inert for three
independent reasons, not the one #108 names:

  1. `execute_notebooks: "off"` in _config.yml
  2. its one code block was a plain ```python fence, not a {code-cell}
  3. no jupytext front matter, so the file was never a notebook at all

An image whose numpy, matplotlib or kaleido was completely broken therefore
still produced a green container test. Fixing only (1) and (2) would still not
be enough: myst-nb reports a failed cell as a *warning*, so a raising cell
yields a published book and exit 0 unless `-W` or `raise_on_error` is set.

The fixture now executes real cells that ASSERT on results — a stack that
imports cleanly but computes wrong answers is exactly what a smoke test should
catch, and printing alone would not. Coverage: numpy/scipy eigenvalues against
an exact closed form, a pandas groupby, a real matplotlib PNG render, and a
plotly static export through kaleido. That last one is the #85 path and the
reason both images pin kaleido<1.0.

test-container.yml now runs both images as real `container:` jobs instead of
`docker run`. Not cosmetic: a container job forces HOME=/github/home while
docker run leaves HOME=/root, and that is precisely why #85 passed these tests
while failing test-containers-lectures.yml, which does use a container job. The
job asserts HOME explicitly so a silent revert is caught, and the lean image
gains pdflatex coverage it never had.

smoke-test.sh --self-test stages a deliberate exception and fails if the build
does NOT go red — because the defect being fixed here is silence, and a fixture
that cannot fail reads as coverage. It attributes the red on captured stdout
rather than reports/*.err.log: verified locally that with raise_on_error myst-nb
aborts before Sphinx writes that file, unlike build-fail-guard's path.

Verified locally (minus the plotly cell — kaleido is not in my env): the cells
genuinely execute, with real values in the built HTML; the book builds clean
under -W with zero warnings; a staged raising cell exits 1; the sentinel appears
in captured output; and reports/ is indeed absent. The plotly/kaleido cell and
the container-job HOME assertion can only be verified in CI — workflow_dispatch
on this branch, since workflow_run only fires for workflows already on the
default branch.

Image size is now read from the manifest, since a container job cannot run
docker. That is COMPRESSED layer bytes — a different, smaller number than the
old `docker images` figure, so the two series are not comparable.

Refs #108

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(containers): address Copilot review on the smoke fixture

Three valid points, one of which was my own mess.

TESTING.md: my earlier edit replaced a trailing clause and left the sentence it
was attached to dangling — an em dash running straight into a capitalised new
sentence, then a second em dash. Split into two sentences.

run-local-tests.sh: the prereq block ran a bare `python -c`, unlike the three
`command -v` checks directly above it. Many macOS setups have only python3, so
the script would report a missing science stack when the real problem is no
python at all — a misleading diagnostic in the one place whose whole job is
diagnosing. Now resolves an interpreter (python3 first) and says which it found.

intro.md: the matplotlib cell used np.linspace while importing only matplotlib.
It works, because cells share a kernel, but it meant a numpy breakage would
surface as a matplotlib-cell failure. Attribution is the point of this PR, so
the cell now imports numpy itself.

Re-verified locally after the change (minus the plotly cell, kaleido is not in
my env): the book still builds clean under -W and the cells still execute, with
numpy and matplotlib both reporting real values in the built HTML.

Refs #108

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants