[SC-16155] Fix async chart uploads by pre-serializing figure images#505
Conversation
…io conflict (ZD-626) Plotly's to_image() method (via kaleido) fails when called from within an async context, causing "RuntimeError: Timeout context manager should be used inside a task" errors in Jupyter notebooks with nested asyncio. This fix adds a pre_serialize() method to Figure that caches PNG bytes before entering the async context in TestResult.log(), ensuring kaleido is never invoked from within run_async(). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…tstanding-error-logs
johnwalz97
left a comment
There was a problem hiding this comment.
lgtm... was also not able to reproduce but it seems like this could be a fix
| """Creates a `requests`-compatible files object to be sent to the API.""" | ||
| if is_matplotlib_figure(self.figure): | ||
| buffer = BytesIO() | ||
| self.figure.savefig(buffer, bbox_inches="tight") |
There was a problem hiding this comment.
just fyi the bbox_inches param isn't maintained in the new implementation
|
|
||
| if is_matplotlib_figure(self.figure): | ||
| buffer = BytesIO() | ||
| self.figure.savefig(buffer, format="png") |
There was a problem hiding this comment.
we might want the bbox_inches (see below) but not sure
There was a problem hiding this comment.
Great catch @johnwalz97! I am inclined to not introduced too many more changes on this PR and perhaps leave it as-is for now. Previously bbox_inches="tight"only affected uploads, but with the new shared PNG path it would now affect all Matplotlib rendering, not just upload serialization. If we decide we want that back, we can always reintroduce it later?
…tstanding-error-logs
PR SummaryThis PR introduces caching of PNG bytes for figures to ensure async-safe serialization, particularly addressing conflicts with Plotly's Kaleido library and asyncio event loops. A new private attribute (_cached_png_bytes) and helper method (_get_png_bytes) have been added to cache or generate PNG representations for figures, which can be of matplotlib, Plotly, or pre-rendered PNG image types. Key changes include:
These changes enhance performance by avoiding repeated image generation and improve robustness in async environments by mitigating library conflicts. Test Suggestions
|
Pull Request Description
What and why?
Tests that generate charts or figures fail to upload results to ValidMind with:
and the customer confirmed that after the
Kaleido 1.2.0fix the process no longer hangs indefinitely but still fails to complete chart generation and upload the results.This change pre-serializes figure PNGs before
TestResult.log()enters the async upload flow, avoiding the Plotly/Kaleido and asyncio conflict in the customer-reported error path. It also centralizes figure PNG serialization behind a cached path so uploads, HTML rendering, and base64 generation all reuse the same image bytes consistently.How to test
I was not able to reproduce this locally. I tested with
Python 3.9and also tried reproducing the issue outside ValidMind by serializing figures directly through theFigureobject, but could not trigger the failure, so this may depend on the customer’s environment.What needs special review?
Check whether it makes sense to take a defensive approach here, based on the customer-provided evidence, which includes clear stack traces showing the exact error path this fix addresses.
The fix is in principle low risk because it does not change behavior on systems where serialization already works; it only guarantees that
to_image()is not called from within anasynccontext where it may conflict.Dependencies, breaking changes, and deployment notes
Release notes
Checklist