Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions test/test_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,3 +229,29 @@ def test_to_raster_pixel_ratio(gridpath, r1, r2):
f = r2 / r1
d = np.array(raster2.shape) - f * np.array(raster1.shape)
assert (d >= 0).all() and (d <= f - 1).all()


def test_matplotlib_backend_does_not_clobber_mpl_state(gridpath):
"""Regression test for #1537.

``plot(backend="matplotlib")`` calls ``hv.extension("matplotlib")`` which
switches the active Matplotlib backend and breaks subsequent native
Matplotlib/xarray ``.plot()`` calls. UXarray should restore the original
Matplotlib backend so plain Matplotlib plotting still works afterwards.
"""
mesh_path = gridpath("mpas", "QU", "oQU480.231010.nc")
uxds = ux.open_dataset(mesh_path, mesh_path)

backend_before = matplotlib.get_backend()

# UXarray plot using the matplotlib backend (the trigger in #1537)
uxds["bottomDepth"].plot(backend="matplotlib")

# The active matplotlib backend must be restored.
assert matplotlib.get_backend() == backend_before

# A subsequent plain xarray/matplotlib plot must still work without error.
fig, ax = plt.subplots()
xr.DataArray(np.random.rand(5, 5), dims=["y", "x"], name="plain").plot(ax=ax)
assert len(ax.collections) + len(ax.images) > 0
plt.close(fig)
Comment thread
rajeeja marked this conversation as resolved.
Outdated
18 changes: 17 additions & 1 deletion uxarray/plot/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,24 @@ def assign(self, backend: str):
# only call hv.extension if it needs to be changed
hv.extension(backend)

if backend == "matplotlib":
Comment thread
rajeeja marked this conversation as resolved.
# ``hv.extension("matplotlib")`` switches the active Matplotlib
# backend (e.g. to ``agg`` in Jupyter), which clobbers the
# IPython inline display hook. This silently breaks any
# subsequent native ``matplotlib``/``xarray`` ``.plot()`` calls,
# which render nothing. HoloViews objects still display because
# they render through ``hv.Store.current_backend`` rather than
# the active Matplotlib backend, so restoring the original
# backend here is safe and fixes downstream plotting. See
# https://github.com/UXARRAY/uxarray/issues/1537
self.reset_mpl_backend()

def reset_mpl_backend(self):
"""Resets the default backend for the ``matplotlib`` module."""
"""Restores the original ``matplotlib`` backend (and its IPython inline
display hook) that was active before a HoloViews backend switch."""
if self.matplotlib_backend is None:
return

import matplotlib as mpl

mpl.use(self.matplotlib_backend)
Comment thread
rajeeja marked this conversation as resolved.
Expand Down
Loading