You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Currently when importing any of the entropy calculator, _core is imported, which runs matplotlib.use("Agg") automatically, suppressing plots to show as per intended by this package.
However, in experimental environments (like working with notebooks during testing for example), users may want other upstream scripts to output plots automatically: e.g. running df['feature'].plot() to visually inspect the trend. With matplotlib.use("Agg"), pandas' Series.plot() doesn't work naively.
Perhaps should restrict the matplotlib.use("Agg") option to within the utils.plot module itself if users want a clean CI pipeline/Docker integration.
Thanks for this, matplotlib.use("Agg") runs at import time in _core, and since every measure imports _core, just doing from entroscope import shannon quietly switches your whole backend and breaks things like df.plot() in a notebook. A library shouldn't be mutating global matplotlib state on import like that. Plan is to drop the forced backend entirely and let matplotlib resolve it normally (it already falls back to a non-interactive backend when there's no display), and move the headless guarantee into the test/CI environment instead. Working on it now.
import matplotlib
matplotlib.use("module://matplotlib_inline.backend_inline") # what a notebook uses
import matplotlib; print("before:", matplotlib.get_backend())
from entroscope import shannon # importing ANY measure pulls in _core
print("after: ", matplotlib.get_backend())
# before: module://matplotlib_inline.backend_inline
# after: agg <-- _core forced it, df.plot() now broken
Root cause was exactly what you said: _core.py ran matplotlib.use("Agg") at import time, and every measure imports _core, so just importing shannon flipped the whole backend.
The fix: I pulled the forced backend out of _core entirely and let matplotlib resolve its own (it already falls back to a non-interactive backend when there's no display, so headless still works on its own).
# entroscope/_core.py (after)
import matplotlib.pyplot as plt # importing pyplot does NOT force a backend
I went slightly further than restricting it to utils.plot like you suggested, since even there it would still flip the backend globally whenever you called a plot function. Instead, nothing in the library touches the global backend now. For folks who want a guaranteed non-interactive backend in Docker/CI, the standard env var does it:
export MPLBACKEND=Agg # or in a Dockerfile: ENV MPLBACKEND=Agg
Added a regression test that imports entroscope in a subprocess under a non-Agg backend and asserts it stays put, so this can't silently come back. Fix is on a branch now, will ship in the next release. Thanks again for the clean report.
Activity
Par-python commentedon Jun 3, 2026
Thanks for this, matplotlib.use("Agg") runs at import time in _core, and since every measure imports _core, just doing from entroscope import shannon quietly switches your whole backend and breaks things like df.plot() in a notebook. A library shouldn't be mutating global matplotlib state on import like that. Plan is to drop the forced backend entirely and let matplotlib resolve it normally (it already falls back to a non-interactive backend when there's no display), and move the headless guarantee into the test/CI environment instead. Working on it now.
fix: stop forcing matplotlib Agg backend on import (#2)
test: regression guard that import does not force a backend (#2)
docs: document MPLBACKEND=Agg for headless users; set it in CI (#2)
Par-python commentedon Jun 3, 2026
reproduced the bug:
Root cause was exactly what you said: _core.py ran matplotlib.use("Agg") at import time, and every measure imports _core, so just importing shannon flipped the whole backend.
The fix: I pulled the forced backend out of _core entirely and let matplotlib resolve its own (it already falls back to a non-interactive backend when there's no display, so headless still works on its own).
I went slightly further than restricting it to utils.plot like you suggested, since even there it would still flip the backend globally whenever you called a plot function. Instead, nothing in the library touches the global backend now. For folks who want a guaranteed non-interactive backend in Docker/CI, the standard env var does it:
export MPLBACKEND=Agg # or in a Dockerfile: ENV MPLBACKEND=AggAdded a regression test that imports entroscope in a subprocess under a non-Agg backend and asserts it stays put, so this can't silently come back. Fix is on a branch now, will ship in the next release. Thanks again for the clean report.
Merge pull request #4 from Par-python/2-enforcing-matplotlibuseagg