Skip to content

Commit 9a721e3

Browse files
zlatko-minevclaude
andcommitted
paperplot — publication-correct matplotlib figures
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0 parents  commit 9a721e3

60 files changed

Lines changed: 5524 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/dependabot.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
version: 2
2+
updates:
3+
# Keep GitHub Actions current; pair with SHA-pinning for supply-chain hardening.
4+
- package-ecosystem: github-actions
5+
directory: "/"
6+
schedule:
7+
interval: weekly
8+
commit-message:
9+
prefix: "ci"
10+
11+
# Python dependency bumps for the package + extras.
12+
- package-ecosystem: pip
13+
directory: "/"
14+
schedule:
15+
interval: weekly
16+
commit-message:
17+
prefix: "deps"

.github/workflows/ci.yml

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
# Least privilege: tests/builds only need to read the repo (caps the token even
9+
# if a matrix dependency or action is compromised).
10+
permissions:
11+
contents: read
12+
13+
concurrency:
14+
group: ci-${{ github.ref }}
15+
cancel-in-progress: true
16+
17+
jobs:
18+
test:
19+
runs-on: ${{ matrix.os }}
20+
strategy:
21+
fail-fast: false
22+
matrix:
23+
os: [ubuntu-latest]
24+
python-version: ["3.10", "3.11", "3.12", "3.13"]
25+
# Cross-platform smoke on the current default Python (keeps the grid small).
26+
include:
27+
- { os: macos-latest, python-version: "3.12" }
28+
- { os: windows-latest, python-version: "3.12" }
29+
name: test (${{ matrix.os }}, py${{ matrix.python-version }})
30+
env:
31+
MPLBACKEND: Agg
32+
PYTHONUTF8: "1" # UTF-8 mode so Unicode output works on Windows consoles
33+
steps:
34+
- uses: actions/checkout@v4
35+
36+
- name: Set up Python ${{ matrix.python-version }}
37+
uses: actions/setup-python@v5
38+
with:
39+
python-version: ${{ matrix.python-version }}
40+
cache: pip
41+
42+
- name: Install (with dev + optional extras)
43+
run: |
44+
python -m pip install --upgrade pip
45+
pip install -e ".[dev,notebook,seaborn]"
46+
47+
- name: Run tests
48+
run: pytest -q
49+
50+
- name: Render examples headless (smoke test)
51+
run: python examples/run_all.py
52+
53+
build:
54+
runs-on: ubuntu-latest
55+
steps:
56+
- uses: actions/checkout@v4
57+
- uses: actions/setup-python@v5
58+
with:
59+
python-version: "3.12"
60+
cache: pip
61+
- name: Build sdist + wheel
62+
run: |
63+
python -m pip install --upgrade pip build twine
64+
python -m build
65+
twine check dist/*

.github/workflows/docs.yml

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
name: Docs
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
workflow_dispatch:
8+
9+
# Default to read-only; the elevated Pages/OIDC scopes are granted only to the
10+
# deploy job below, so fork-PR builds run with a read-only token.
11+
permissions:
12+
contents: read
13+
14+
# PR builds get a fast, per-ref cancellable group; the deploy job uses the global
15+
# `pages` group (set on the job) so concurrent deploys serialize.
16+
concurrency:
17+
group: docs-${{ github.ref }}
18+
cancel-in-progress: true
19+
20+
jobs:
21+
build:
22+
runs-on: ubuntu-latest
23+
permissions:
24+
contents: read
25+
steps:
26+
- uses: actions/checkout@v4
27+
28+
- uses: actions/setup-python@v5
29+
with:
30+
python-version: "3.12"
31+
cache: pip
32+
33+
- name: Install package + docs deps
34+
run: |
35+
python -m pip install --upgrade pip
36+
pip install -e ".[docs,notebook]"
37+
38+
- name: Generate gallery (runs the showcase headless)
39+
env:
40+
MPLBACKEND: Agg
41+
run: python docs/generate_gallery.py
42+
43+
- name: Build site
44+
run: mkdocs build --strict
45+
46+
- name: Upload Pages artifact
47+
uses: actions/upload-pages-artifact@v3
48+
with:
49+
path: site
50+
51+
# Deploy only from main; PRs build (above) to catch breakage without publishing.
52+
deploy:
53+
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
54+
needs: build
55+
runs-on: ubuntu-latest
56+
# Elevated scopes live here only — never on PR builds.
57+
permissions:
58+
pages: write
59+
id-token: write
60+
concurrency:
61+
group: pages
62+
cancel-in-progress: false
63+
environment:
64+
name: github-pages
65+
url: ${{ steps.deployment.outputs.page_url }}
66+
steps:
67+
- name: Deploy to GitHub Pages
68+
id: deployment
69+
uses: actions/deploy-pages@v4

.gitignore

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Python
2+
__pycache__/
3+
*.py[cod]
4+
*.egg-info/
5+
.eggs/
6+
build/
7+
dist/
8+
.pytest_cache/
9+
.ipynb_checkpoints/
10+
11+
# Generated example outputs
12+
examples/out/
13+
14+
# MkDocs build output (docs/assets/gallery/ IS tracked — committed gallery images)
15+
site/
16+
.cache/
17+
18+
# OS / editor
19+
.DS_Store
20+
.vscode/
21+
.idea/
22+
*.code-workspace
23+
24+
# Claude Code personal settings
25+
.claude/settings.local.json

CLAUDE.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# paperplot — notes for agents
2+
3+
Publication-correct matplotlib figures, sized/styled/preflighted per journal
4+
(APS + Nature + IEEE, plus a `talk` presentation target). Small library; keep it small.
5+
6+
## Commands
7+
- Tests: `pytest` (Agg backend; fixtures snapshot/restore rcParams — keep that pattern).
8+
- Examples: `python examples/showcase.py` or `python examples/run_all.py`
9+
→ writes PDFs to `examples/out/` and PNG proofs to `examples/out/render/` (gitignored).
10+
- Dev install: `pip install -e ".[dev]"`. Python ≥ 3.10.
11+
12+
## Hard rules (don't break these)
13+
- **matplotlib-only core.** `numpy` + `matplotlib` are the only runtime deps.
14+
`seaborn` and `IPython` are OPTIONAL extras — import them lazily *inside* the
15+
function that needs them, behind a try/guard. Never add a top-level `import
16+
seaborn`/`IPython`. (`plots.hist_outline` has the one opt-in seaborn path.)
17+
- **Journals are data, not code.** Add/edit journal specs in
18+
`paperplot/data/journals.toml`, loaded via `registry.py` — not hardcoded.
19+
- **`__init__.py` is the public API contract.** Anything users call must be
20+
re-exported there and listed in `__all__`. Keep imports + `__all__` in sync.
21+
- **`plots.py` is a deliberately small set** of opinionated composites
22+
(hist_outline/hist_filled, data_fit_band, swatches/show_palettes). It is NOT a
23+
general plotting library — no dataframes, faceting, or grammar-of-graphics.
24+
See the "Scope amendment" note in `paperplot_DESIGN.md`.
25+
- **preflight warns, never blocks.** `save()`/`preflight()` always produce output
26+
and surface a `Report`; no raising/strict mode.
27+
- **`.mplstyle` export is an on-ramp, not the product.** `mplstyle.py` renders the
28+
*look* (rcParams) for `plt.style.use`; it cannot size/embed/preflight, so never
29+
route core sizing through it. `register_mplstyles()` mutates matplotlib's global
30+
style library — keep it opt-in; NEVER auto-register on import (silent first import).
31+
Hex colors in the file text are written without `#` (it's a comment char there).
32+
33+
## Conventions
34+
- Default cycle is **Okabe-Ito** (colorblind-safe). Separately, the fill/stroke
35+
convention: `pp.fills()` (muted) for areas, `pp.strokes()` (bright) for lines —
36+
these are NOT colorblind-safe; don't make them the categorical default.
37+
- Sequential/diverging colormaps go through `pp.cmap(...)`, never the categorical
38+
cycle (palettes.py enforces this with a warning).
39+
40+
## Source of truth
41+
`paperplot_DESIGN.md` records the design decisions and trade-offs — read it before
42+
changing architecture (registry/versioning, rcParams mapping, journal model).

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 The paperplot authors
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

0 commit comments

Comments
 (0)