Skip to content

fix: make the sphinx-gallery scraper capture every shown figure - #5701

Open
larsoner wants to merge 9 commits into
plotly:mainfrom
larsoner:sgthumb
Open

fix: make the sphinx-gallery scraper capture every shown figure#5701
larsoner wants to merge 9 commits into
plotly:mainfrom
larsoner:sgthumb

Conversation

@larsoner

Copy link
Copy Markdown

Link to issue

Closes #4722
Closes #4959
Closes sphinx-gallery/sphinx-gallery#1238
Closes sphinx-gallery/sphinx-gallery#1632

Description of change

Fix sphinx-gallery thumbnail generation!

Demo

index

Testing strategy

Added some unit tests to make sure it works. It uses SG (added to dev_optional so CIs install it) to make sure the contract holds.

Additional information (optional)

I am a SG maintainer. I used Claude Opus 5 to draft the changes here but I iterated with it a lot and have reviewed the final diff.

Guidelines

@sr-murthy

sr-murthy commented Aug 12, 2026

Copy link
Copy Markdown

Tried it (replaced plotly dep in project TOML with a ref to your fork branch of Plotly), cleared out the docs build folder, and ran Sphinx build again (make -C docs html), but still get the same placeholder thumbs (in the docs auto_examples/images/thumb folder - the full size figures are correctly drawn in the HTMLs.

I can see that the Plotly version in my env. is your branch because the UV log has the following line:

...
DEBUG Requirement already installed: plotly==6.9.0 (from git+https://github.com/larsoner/plotly.py@5ef36f39d12315835d211c39ae5ff2357c06a70b)
...

Conf changes for Plotly PNG rendering are as described here. Folder structure for the examples folder containing the Python files, is also as recommended, with auto_examples adjacent to examples. Here is the gallery conf. in docs/conf.py:

# Sphinx gallery conf.
sphinx_gallery_conf = {
    "examples_dirs": "sources/plot-gallery/examples",  # `docs`-relative path to Python scripts
    "gallery_dirs": "sources/plot-gallery/auto_examples",  # `docs`-relative path to gallery outputs
    "image_scrapers": ("matplotlib", "plotly.io._sg_scraper.plotly_sg_scraper",),
}

Wouldn't it be simpler for Sphinx gallery to support a post-build hook (a custom function maybe) that created the thumbnails by resizing and exporting the full sized figures into PNGs in the appropriate location?

larsoner and others added 2 commits August 12, 2026 09:40
Probe Kaleido/browser availability once per build; when unavailable, emit
a single sphinx warning (suppressible via suppress_warnings =
["plotly.sg_scraper"]), embed shown figures inline in the rst instead of
via files (sphinx-gallery requires an image file for every image path
consumed), and let examples fall back to placeholder thumbnails.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
larsoner and others added 3 commits August 12, 2026 10:36
- The _repr_html_ fallback (hit when the default renderer is not a
  mimetype renderer, e.g. sphinx_gallery_png) now sizes like the html
  renderers (default_height=525) instead of height:100%, which collapses
  or overflows in containers with no set height.
- The scraper now embeds shown figures inline in the rst, wrapped in the
  same output_subarea div sphinx-gallery wraps captured HTML reprs in, so
  themes can style both kinds of embed with one hook; it no longer writes
  .html files next to the images.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A figure whose inline script draws while the page is still being parsed
can be sized to a container whose width changes by the time loading
finishes (e.g. pydata-sphinx-theme's secondary sidebar comes after the
article in the DOM), leaving it clipped until a window resize. Append a
per-figure script to the _repr_html_ fallback and the sphinx-gallery
scraper embeds that calls Plotly.Plots.resize once on window load.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Move the dark-theme styling out of sphinx-gallery: sphinx-gallery should
not carry plotly-specific CSS, so ship a scoped style with each embed
(repr fallback and scraper) instead. The white padded card only shows on
dark pages (data-theme toggles or OS preference); on light pages it is
invisible.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@larsoner

Copy link
Copy Markdown
Author

Wouldn't it be simpler for Sphinx gallery to support a post-build hook (a custom function maybe) that created the thumbnails by resizing the full sized PNGs into the appropriate location?

I don't think we should rework / discuss the SG API contracts here -- it'll be a much bigger discussion if needed, and require an understanding of design decisions and code evolution over the years where we settled on the existing design.

Instead I'd like to see if we can get things working properly for you using this branch. Can you make clean and try again with the latest version of the PR? I pushed some fixe -- the previous code required some SG changes and fig.show() to be used etc. but this version should work, even with the simpler:

    "image_scrapers": ("matplotlib", "plotly"),

I confirmed this works in sphinx-gallery/sphinx-gallery#1635 (see the updated example render) so hopefully it works for you now!

larsoner and others added 2 commits August 12, 2026 11:45
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Keep only the minimal sizing fix in basedatatypes (_repr_html_ fallback
sizes like the html renderers). The dark-page card and the after-load
resize now come from a single idempotent fix-up block the scraper
appends to any code block that displayed a figure: repr-captured embeds
and the scraper's own embeds both sit in an output_subarea div, so one
:has() rule styles both, and one guarded listener resizes every figure.
No more per-figure uuid/script plumbing outside scraper code.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sr-murthy

sr-murthy commented Aug 12, 2026

Copy link
Copy Markdown

Thx, I'll try it out, and let you know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants