[pyplot] Make gallery behavior gates fail closed under callback errors and Matplotlib version skew
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Quiet
- Tech stack
- python
- Domain
- data-visualization, testing-qa
Research direction
Start by reviewing the gallery contract from PR #413 and the supplied event_handling/resample.py and widgets/polygon_selector_simple.py examples. Run the existing behavior gate and inspect how callback, widget, animation, version-adapter, and provenance evidence are evaluated. Done means all acceptance conditions pass and the 71-example standard behavior gate remains green.
Written by the indexing model from the issue text.
Description
Problem
Reaching the end of a script is not proof that an interactive Matplotlib example works. A callback can raise after the initial draw, a selector can fail to mutate its artists, or a small patch-version API change can make a reference example fail before XY is evaluated.
The gallery contract in PR #413 now drives required interactions deterministically, records behavioral evidence, fails on callback exceptions or unchanged required state, and confines version adaptation to an exact source/engine/version match.
The supplied 3.11.1 resample.py source needs one audited Matplotlib-3.11.0 reference-only adapter for the FillBetweenPolyCollection.set_data signature. No adapter is applied to XY, other files, or other versions.
Comparisons
Resample callback
| Matplotlib | xy.pyplot |
|---|---|
![]() |
![]() |
Polygon selector
| Matplotlib | xy.pyplot |
|---|---|
![]() |
![]() |
Complete upstream Matplotlib examples
event_handling/resample.py
"""
===============
Resampling Data
===============
Downsampling lowers the sample rate or sample size of a signal. In
this tutorial, the signal is downsampled when the plot is adjusted
through dragging and zooming.
.. note::
This example exercises the interactive capabilities of Matplotlib, and this
will not appear in the static documentation. Please run this code on your
machine to see the interactivity.
You can copy and paste individual parts, or download the entire example
using the link at the bottom of the page.
"""
import matplotlib.pyplot as plt
import numpy as np
# A class that will downsample the data and recompute when zoomed.
class DataDisplayDownsampler:
def __init__(self, xdata, y1data, y2data):
self.origY1Data = y1data
self.origY2Data = y2data
self.origXData = xdata
self.max_points = 50
self.delta = xdata[-1] - xdata[0]
def plot(self, ax):
x, y1, y2 = self._downsample(self.origXData.min(), self.origXData.max())
(self.line,) = ax.plot(x, y1, 'o-')
self.poly_collection = ax.fill_between(x, y1, y2, step="pre", color="r")
def _downsample(self, xstart, xend):
# get the points in the view range
mask = (self.origXData > xstart) & (self.origXData < xend)
# dilate the mask by one to catch the points just outside
# of the view range to not truncate the line
mask = np.convolve([1, 1, 1], mask, mode='same').astype(bool)
# sort out how many points to drop
ratio = max(np.sum(mask) // self.max_points, 1)
# mask data
xdata = self.origXData[mask]
y1data = self.origY1Data[mask]
y2data = self.origY2Data[mask]
# downsample data
xdata = xdata[::ratio]
y1data = y1data[::ratio]
y2data = y2data[::ratio]
print(f"using {len(y1data)} of {np.sum(mask)} visible points")
return xdata, y1data, y2data
def update(self, ax):
# Update the artists
lims = ax.viewLim
if abs(lims.width - self.delta) > 1e-8:
self.delta = lims.width
xstart, xend = lims.intervalx
x, y1, y2 = self._downsample(xstart, xend)
self.line.set_data(x, y1)
self.poly_collection.set_data(x, y1, y2, step="pre")
ax.figure.canvas.draw_idle()
# Create a signal
xdata = np.linspace(16, 365, (365-16)*4)
y1data = np.sin(2*np.pi*xdata/153) + np.cos(2*np.pi*xdata/127)
y2data = y1data + .2
d = DataDisplayDownsampler(xdata, y1data, y2data)
fig, ax = plt.subplots()
# Hook up the line
d.plot(ax)
ax.set_autoscale_on(False) # Otherwise, infinite loop
# Connect for changing the view limits
ax.callbacks.connect('xlim_changed', d.update)
ax.set_xlim(16, 365)
plt.show()
# %%
# .. tags:: interactivity: zoom, interactivity: event-handling
widgets/polygon_selector_simple.py
"""
================
Polygon Selector
================
Shows how to create a polygon programmatically or interactively
"""
import matplotlib.pyplot as plt
from matplotlib.widgets import PolygonSelector
# %%
#
# To create the polygon programmatically
fig, ax = plt.subplots()
fig.show()
selector = PolygonSelector(ax, lambda *args: None)
# Add three vertices
selector.verts = [(0.1, 0.4), (0.5, 0.9), (0.3, 0.2)]
# %%
#
# To create the polygon interactively
fig2, ax2 = plt.subplots()
fig2.show()
selector2 = PolygonSelector(ax2, lambda *args: None)
print("Click on the figure to create a polygon.")
print("Press the 'esc' key to start a new polygon.")
print("Try holding the 'shift' key to move all of the vertices.")
print("Try holding the 'ctrl' key to move a single vertex.")
# %%
# .. tags::
#
# component: axes,
# styling: position,
# plot-type: line,
# level: intermediate,
# domain: cartography,
# domain: geometry,
# domain: statistics,
#
# .. admonition:: References
#
# The use of the following functions, methods, classes and modules is shown
# in this example:
#
# - `matplotlib.widgets.PolygonSelector`
Acceptance
- Callback exceptions fail the example even if initial rendering succeeded.
- Required widget, event, and animation interactions produce changed semantic or device-space evidence.
- Animation tests drive initial, middle, and final frames plus timer/callback behavior.
- Version adapters are allowlisted by exact source hash, engine, and Matplotlib version and are reported in provenance.
- Acceptance rejects unapproved adapters, fallbacks, and waivers.
- The 71-example standard behavior gate remains fully green.
- Dominant language
- Python
- Stars
- 1.8k
- Forks
- 76
- Avg merge
- 1h 7m
- Merged PRs (30d)
- 8
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from reflex-dev/xy
-
needs investigate performance
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
reflex-dev/xy#169 ·
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
reflex-dev/xy#516 · 1 comment ·
-
Difficulty 3/5 1-2 days Newbie friendliness 74/100
reflex-dev/xy#512 ·
-
Difficulty 3/5 1-2 days Newbie friendliness 68/100
reflex-dev/xy#511 ·
-
Difficulty 3/5 1-2 days Newbie friendliness 62/100
reflex-dev/xy#510 ·
Similar issues
-
area/auth bug comp/agent P3 platform/discord type/security
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
NousResearch/hermes-agent#117848 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
bancolombia/sentinel#23 ·
-
test md OpenCI
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
-
integration:quickjs org:external priority:backlog topic:code-interpreter topic:middleware type:feature
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
langchain-ai/deepagents#6450 ·
-
bug client
Difficulty 2/5 1-3 hours Newbie friendliness 88/100



