Make matplotlib an optional [plot] extra (#2494)#2495
Merged
Conversation
matplotlib was in install_requires but no compute path imports it; every use is a lazy import inside the .xrs.plot accessor helpers. Move it to a plot extra so a plain install skips matplotlib and its transitive chain (pillow, fonttools, kiwisolver, contourpy, cycler, pyparsing). Add a _require_matplotlib() guard at the two plot entry points so a missing install raises a clear ImportError pointing at pip install xarray-spatial[plot]. Add matplotlib to the tests extra so the existing plot tests keep running in CI. Update README, install docs, and CHANGELOG.
brendancol
commented
May 27, 2026
brendancol
left a comment
Contributor
Author
There was a problem hiding this comment.
PR Review: Make matplotlib an optional [plot] extra (#2494)
Scope is packaging + two guard calls, so the geospatial-specific checks
(backends, dask, CUDA, numerics) don't apply here. I focused on whether the
dependency move is complete and the guard behaves.
Blockers
None.
Suggestions
_require_matplotlib()(accessor.py:24) checksimport matplotlibbut the
call sites then doimport matplotlib.pyplot as plt. matplotlib can import
while pyplot fails (no usable backend, broken install). That's an uncommon
case and the error would still be an ImportError, just not the friendly one.
Checkingimport matplotlib.pyplotin the guard would close the gap. Not
blocking.
Nits
test_compute_imports_without_matplotlibcallsimportlib.reloadon
xrspatial.focal,zonal, anddasymetric. The reload re-executes those
modules in place, so other tests in the same session keep the reloaded
objects. It passes today, but reloading numba/ngjit modules for an
import-side-effect check is heavier than needed. Importing the modules in a
subprocess with matplotlib blocked, or just asserting the import doesn't
raise, would isolate the check better.
What looks good
- The core finding holds: no compute path imports matplotlib. The only runtime
uses are the two.xrs.plotaccessor methods; the matches inzonal.pyand
bump.pyare inside.. plot::docstring examples, not executed code. - matplotlib added to the
testsextra, so the existing plot/palette tests keep
running in CI rather than silently skipping. - pandas correctly left as required, with the reason recorded.
_listed_colormap_from_attrskept as-is; it already returns None on
ImportError and sits on the read path, so guarding it would have broken
non-plotting GeoTIFF reads.- New tests assert both the friendly-error path and that core modules import
with matplotlib blocked.
Checklist
- No compute path imports matplotlib (verified across the package)
- Friendly ImportError points at the
plotextra - tests extra keeps plot tests running in CI
- Docs (README, installation.rst) and CHANGELOG updated
- pandas correctly identified as non-removable
- Guard covers
matplotlib.pyplot, not justmatplotlib(see suggestion)
- _require_matplotlib now imports matplotlib.pyplot so an installed-but- unusable matplotlib (no backend) also gets the friendly error. - Replace the importlib.reload-based import test with a subprocess that imports the compute modules against a clean cache with matplotlib blocked.
brendancol
commented
May 27, 2026
brendancol
left a comment
Contributor
Author
There was a problem hiding this comment.
Follow-up review (after 1e1157d)
Both findings from the first pass are resolved.
- Suggestion (guard pyplot):
_require_matplotlib()now does
import matplotlib.pyplot, so an installed-but-unusable matplotlib also
produces the friendly error. Resolved. - Nit (reload-based test):
test_compute_imports_without_matplotlibnow runs
in a subprocess against a clean module cache instead of reloading numba
modules in-session. Resolved.
No new issues. pytest xrspatial/tests/test_accessor.py is 29 passed and
flake8 is clean on the changed files. No remaining items beyond CI.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2494
matplotlib was listed in
install_requires, but nothing in the package importsit at module top level. Every use is a lazy import inside the
.xrs.plotaccessor helpers. So a plain
pip install xarray-spatialwas dragging inmatplotlib plus its whole chain (pillow, fonttools, kiwisolver, contourpy,
cycler, pyparsing) for users who only run the compute functions.
matplotlibout ofinstall_requiresinto a newplotextra._require_matplotlib()guard at the two plot entry points inaccessor.py. Without matplotlib,.xrs.plot()now raises a clearImportError pointing at
pip install xarray-spatial[plot]instead of a bareModuleNotFoundError.matplotlibto thetestsextra so the existing plot tests keep runningin CI.
pandas is intentionally left alone: xarray requires it and several core modules
import it at top level, so it can't move to an extra.
This is a compute-only change, not backend-specific. No spatial operation
imports matplotlib on any backend.
Test plan
pytest xrspatial/tests/test_accessor.py(29 passed) — includes new teststhat block matplotlib in
sys.modulesand assert core modules still importand the plot helpers raise the friendly error.
pytest xrspatial/geotiff/tests/test_features.py -k "plot or palette or colormap"(8 passed)import xrspatialand the focal/zonal/dasymetricmodules import with matplotlib blocked.