This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
py-maidr (maidr) is a Python library that makes matplotlib/seaborn visualizations accessible to blind and low-vision users. It monkey-patches plotting functions at import time via wrapt, so users just import maidr and their existing code automatically generates interactive HTML with sonification, braille, and tactile support. It is the Python binding for the maidr JavaScript library.
| Task | Command |
|---|---|
| Install (all extras + dev) | uv sync --locked --all-extras --dev |
| Run all tests | uv run pytest -vvv |
| Run single test file | uv run pytest tests/core/test_figure_manager.py -vvv |
| Run single test by name | uv run pytest tests/core/test_figure_manager.py::test_get_axes_from_none -vvv |
| Lint (check) | ruff check --diff |
| Lint (auto-fix) | ruff check --fix |
| Check lockfile | uv lock --check |
The maidr/patch/ modules use wrapt to intercept matplotlib/seaborn plot calls (e.g., Axes.bar, seaborn.barplot) at import time. Each patch:
- Calls the original function
- Extracts axes/figure from the result
- Registers the plot with
FigureManager
ContextManager (using contextvars.ContextVar) prevents infinite recursion when patched functions call other patched functions internally.
maidr/api.py— Public API:render(),show(),save_html(),stacked(),close()maidr/core/figure_manager.py— Thread-safe singleton mapping matplotlibFigureobjects toMaidrinstancesmaidr/core/maidr.py—Maidrclass: holds a Figure + list ofMaidrPlotobjects, handles SVG extraction (vialxml), MAIDR JSON schema generation, and HTML renderingmaidr/core/plot/— Factory pattern:MaidrPlotFactorydispatches to concreteMaidrPlotsubclasses (BarPlot, BoxPlot, HeatPlot, etc.) based onPlotTypeenum. Each subclass implements_extract_plot_data()maidr/patch/— One module per plot type (barplot.py, boxplot.py, etc.) plushighlight.py(injects maidr attributes into SVG elements) andclear.py(cleanup onplt.clf/plt.cla)maidr/util/mixin/— Reusable extraction logic:ContainerExtractorMixin,LevelExtractorMixin,LineExtractorMixin,CollectionExtractorMixin,FormatExtractorMixinmaidr/widget/shiny.py— Shiny framework integration
Defined in maidr/core/enum/plot_type.py: BAR, BOX, COUNT, DODGED, HEAT, HIST, LINE, SCATTER, STACKED, SMOOTH, CANDLESTICK.
Every emitted schema's axes object follows the canonical per-axis form:
{
"x": {"label": "...", "min": ..., "max": ..., "tickStep": ..., "format": {...}},
"y": {"label": "...", ...},
"z": {"label": "..."}, # only when applicable (heatmap colorbar, hue/legend)
}- Keys of
axesare a subset of{x, y, z}. No other keys are allowed. - Each value is an
AxisConfigdict.labelis a string;min/max/tickStepare numbers;formatis a dict. format,min,max,tickStep,fill, andlevelmust never appear as siblings ofx/y/z.- Use
MaidrPlot._axis_config(...)/PlotlyPlot._axis_config(...)helpers to build anAxisConfigso only non-Nonefields are emitted. tests/core/test_axes_schema.pyenforces this contract across real emitters.
- Linter/Formatter: Ruff (line-length 88), configured in pyproject.toml
- PEP 8 style; NumPy-style docstrings for all functions and classes
- Type annotations on all functions
- Commits: Conventional commits enforced in CI (allowed tags: feat, fix, docs, perf, refactor, style, test, build, chore, ci). Semantic release uses
featfor minor,fix/perffor patch.
Tests live in tests/ using pytest + pytest-mock. Test fixtures in tests/fixture/ use a factory pattern (MatplotlibFactory, SeabornFactory) to create test plots. Tests are parametrized across library/plot-type combinations.