Skip to content

Commit 0a6471c

Browse files
chrisjsewellclaude
andauthored
🔧 Modernize packaging, tooling and CI (#21)
All CI jobs were failing: Python 3.7-3.9 are no longer available on ubuntu-latest runners, and the tests that did run failed against modern Sphinx/docutils. - Packaging: require Python >=3.11 and sphinx>=8,<10; update classifiers, project URLs (repo moved to sphinx-extensions2) and flit sdist excludes - Drop the docutils<0.18 findall/traverse compatibility shim - Lint: replace black/isort/flake8/pyupgrade with ruff-check + ruff-format - CI: add a pre-commit job; test matrix of py3.11-3.14 x sphinx 8/9 (plus Windows); add a PyPI trusted-publishing job on version tags - tox: py{311-314}-sphinx{8,9} and docs-{update,clean} environments - Read the Docs: ubuntu-24.04/py3.12 and the now-required configuration key - Tests: make the .tex output filename version-agnostic (Sphinx >=7.2 renamed the default project); serialize the is_subfigure attribute as a string so doctree fixtures are stable across docutils 0.21/0.22 All 15 tests pass against both sphinx 8.2/docutils 0.21 and sphinx 9.0/docutils 0.22, and the docs build warning-free on Sphinx 9. Claude-Session: https://claude.ai/code/session_01CQYXtJrMEhZWpJVvBcx7ff Co-authored-by: Claude <noreply@anthropic.com>
1 parent f53b108 commit 0a6471c

14 files changed

Lines changed: 125 additions & 87 deletions

File tree

.github/workflows/ci.yml

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,73 @@ on:
99

1010
jobs:
1111

12+
pre-commit:
13+
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- uses: actions/checkout@v6
18+
- name: Set up Python 3.11
19+
uses: actions/setup-python@v6
20+
with:
21+
python-version: "3.11"
22+
- uses: pre-commit/action@v3.0.1
23+
1224
tests:
1325

1426
strategy:
1527
fail-fast: false
1628
matrix:
17-
python-version: ["3.7", "3.8", "3.9", "3.10"]
29+
python-version: ["3.11", "3.12", "3.13", "3.14"]
30+
sphinx: [">=8,<9", ">=9,<10"]
1831
os: [ubuntu-latest]
1932
include:
2033
- os: windows-latest
21-
python-version: "3.8"
34+
python-version: "3.11"
35+
sphinx: ">=8,<9"
36+
- os: windows-latest
37+
python-version: "3.14"
38+
sphinx: ">=9,<10"
2239

2340
runs-on: ${{ matrix.os }}
2441

42+
name: "pytest: py${{ matrix.python-version }}, sphinx${{ matrix.sphinx }}, on ${{ matrix.os }}"
43+
2544
steps:
26-
- uses: actions/checkout@v2
45+
- uses: actions/checkout@v6
2746
- name: Set up Python ${{ matrix.python-version }}
28-
uses: actions/setup-python@v1
47+
uses: actions/setup-python@v6
2948
with:
3049
python-version: ${{ matrix.python-version }}
50+
allow-prereleases: true
3151
- name: Install dependencies
3252
run: |
3353
python -m pip install --upgrade pip
34-
pip install -e .[testing]
54+
pip install -e ".[testing]" "sphinx${{ matrix.sphinx }}"
3555
- name: Run pytest
36-
run: pytest --cov=src --cov-report=xml --cov-report=term-missing
37-
- name: Upload coverage to Codecov
38-
uses: codecov/codecov-action@v3
56+
run: pytest --cov=sphinx_subfigure --cov-report=term-missing
57+
58+
publish:
59+
60+
name: Publish to PyPI
61+
needs: [pre-commit, tests]
62+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') && github.repository == 'sphinx-extensions2/sphinx-subfigure'
63+
runs-on: ubuntu-latest
64+
environment:
65+
name: pypi
66+
url: https://pypi.org/project/sphinx-subfigure/
67+
permissions:
68+
id-token: write
69+
70+
steps:
71+
- uses: actions/checkout@v6
72+
- name: Set up Python 3.12
73+
uses: actions/setup-python@v6
74+
with:
75+
python-version: "3.12"
76+
- name: Build package
77+
run: |
78+
python -m pip install build
79+
python -m build
80+
- name: Publish to PyPI
81+
uses: pypa/gh-action-pypi-publish@release/v1

.pre-commit-config.yaml

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,15 @@
22
# See https://pre-commit.com/hooks.html for more hooks
33
repos:
44
- repo: https://github.com/pre-commit/pre-commit-hooks
5-
rev: v4.5.0
5+
rev: v6.0.0
66
hooks:
77
- id: trailing-whitespace
88
- id: end-of-file-fixer
99
- id: check-yaml
1010

11-
- repo: https://github.com/asottile/pyupgrade
12-
rev: v3.15.0
11+
- repo: https://github.com/astral-sh/ruff-pre-commit
12+
rev: v0.15.12
1313
hooks:
14-
- id: pyupgrade
15-
args: [--py37-plus]
16-
17-
- repo: https://github.com/PyCQA/isort
18-
rev: 5.13.2
19-
hooks:
20-
- id: isort
21-
22-
- repo: https://github.com/psf/black
23-
rev: 23.12.1
24-
hooks:
25-
- id: black
26-
27-
- repo: https://github.com/PyCQA/flake8
28-
rev: 7.0.0
29-
hooks:
30-
- id: flake8
31-
additional_dependencies:
32-
- flake8-comprehensions
33-
- flake8-bugbear
14+
- id: ruff-check
15+
args: [--fix]
16+
- id: ruff-format

.readthedocs.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
version: 2
22

33
build:
4-
os: ubuntu-22.04
4+
os: ubuntu-24.04
55
tools:
6-
python: "3.8"
6+
python: "3.12"
77

88
python:
99
install:
@@ -14,6 +14,7 @@ python:
1414

1515
sphinx:
1616
builder: html
17+
configuration: docs/conf.py
1718
fail_on_warning: true
1819

1920
# Also build PDF & ePub

AGENTS.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ src/sphinx_subfigure/
2727
├── __init__.py # __version__ and setup() re-export
2828
├── main.py # SubfigureDirective: content parsing, layout generation + validation
2929
├── tr_html.py # HTML post-transform, grid/item nodes, responsive CSS injection
30-
├── tr_latex.py # LaTeX post-transform, subfigure environment nodes
31-
└── _compat.py # docutils <0.18 findall/traverse compatibility
30+
└── tr_latex.py # LaTeX post-transform, subfigure environment nodes
3231
3332
tests/
3433
├── test_simple.py # all tests (pytest-param-files + sphinx-pytest)
@@ -116,8 +115,11 @@ Tests can also be run via `tox` (e.g. `tox -e py311`).
116115
### Documentation
117116

118117
```bash
119-
# Build docs (also: BUILDER=linkcheck tox -e docs)
120-
tox -e docs
118+
# Build docs from clean (also: BUILDER=linkcheck tox -e docs-clean)
119+
tox -e docs-clean
120+
121+
# Incremental docs build
122+
tox -e docs-update
121123

122124
# Or manually
123125
pip install -e ".[docs]"
@@ -165,8 +167,8 @@ pre-commit run --all-files
165167
annotation syntax (`list[str]`, `X | None`)
166168
- Complete type annotations on all function signatures
167169
- Sphinx-style docstrings; short module docstrings on every module
168-
- Style is enforced by pre-commit (see `.pre-commit-config.yaml`); run
169-
`pre-commit run --all-files` before committing
170+
- Style is enforced by pre-commit (see `.pre-commit-config.yaml`), using `ruff-check`
171+
(with `--fix`) and `ruff-format`; run `pre-commit run --all-files` before committing
170172
- User-facing directive errors should be raised via `self.error(...)` inside the
171173
directive, with messages prefixed to identify the failing option
172174
(e.g. `Invalid subfigure layout-sm (...)`)

pyproject.toml

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[build-system]
2-
requires = ["flit_core >=3.2,<4"]
2+
requires = ["flit_core >=3.4,<4"]
33
build-backend = "flit_core.buildapi"
44

55
[project]
@@ -12,31 +12,43 @@ classifiers = [
1212
"Development Status :: 4 - Beta",
1313
"License :: OSI Approved :: MIT License",
1414
"Programming Language :: Python :: 3",
15+
"Programming Language :: Python :: 3 :: Only",
16+
"Programming Language :: Python :: 3.11",
17+
"Programming Language :: Python :: 3.12",
18+
"Programming Language :: Python :: 3.13",
19+
"Programming Language :: Python :: 3.14",
1520
"Topic :: Software Development :: Libraries :: Python Modules",
1621
"Topic :: Text Processing :: Markup",
1722
"Framework :: Sphinx :: Extension",
1823
]
1924
keywords = ["sphinx", "subfigure"]
20-
requires-python = ">=3.7"
21-
dependencies = ["sphinx"]
25+
requires-python = ">=3.11"
26+
dependencies = ["sphinx>=8,<10"]
2227

23-
[project."optional-dependencies"]
28+
[project.optional-dependencies]
2429
testing = [
30+
"beautifulsoup4",
31+
"defusedxml",
2532
"pytest",
2633
"pytest-cov",
27-
"pytest-regressions",
28-
"pytest-param-files",
29-
"sphinx-pytest>=0.0.4",
30-
"beautifulsoup4",
34+
"pytest-param-files~=0.6.0",
35+
"sphinx-pytest~=0.3.0",
3136
]
3237
docs = [
3338
"myst-parser",
3439
"furo"
3540
]
3641

3742
[project.urls]
38-
Homepage = "https://github.com/chrisjsewell/sphinx-subfigure"
43+
Homepage = "https://github.com/sphinx-extensions2/sphinx-subfigure"
44+
Documentation = "https://sphinx-subfigure.readthedocs.io"
45+
46+
[tool.flit.sdist]
47+
exclude = [
48+
"docs/",
49+
"tests/",
50+
]
3951

40-
[tool.isort]
41-
profile = "black"
42-
force_sort_within_sections = true
52+
[tool.ruff.lint]
53+
extend-select = ["B", "C4", "FA", "FURB", "I", "ICN", "ISC", "N", "PERF", "PGH", "PIE", "RUF", "SIM", "UP"]
54+
extend-ignore = ["ISC001", "RUF005", "RUF012"]

src/sphinx_subfigure/_compat.py

Lines changed: 0 additions & 3 deletions
This file was deleted.

src/sphinx_subfigure/main.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ class SubfigureDirective(SphinxDirective):
4444
def run(self) -> list[nodes.Element]:
4545
"""Run the directive."""
4646
self.assert_has_content()
47+
# note the attribute value is a string, not a bool,
48+
# so that its doctree serialization is stable across docutils versions
4749
figure_node = nodes.figure(
48-
is_subfigure=True,
50+
is_subfigure="true",
4951
classes=["sphinx-subfigure"] + self.options.get("class", []),
5052
grid_classes=self.options.get("class-grid", []),
5153
area_classes=self.options.get("class-area", []),
@@ -126,7 +128,7 @@ def _validate_layout(
126128

127129
prefix = f"Invalid subfigure {ltype}"
128130

129-
area_indices: dict[str, list[tuple[int, int]]] = {}
131+
area_indices: dict[str, set[tuple[int, int]]] = {}
130132

131133
# check all rows have the same number of columns, and retrieve indices of each area
132134
if not layout:

src/sphinx_subfigure/tr_html.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
from sphinx.transforms.post_transforms import SphinxPostTransform
99
from sphinx.writers.html import HTMLTranslator
1010

11-
from ._compat import findall
12-
1311

1412
def setup_html(app: Sphinx):
1513
"""Setup the extension for HTML building."""
@@ -112,9 +110,8 @@ class SubfigureHtmlTransform(SphinxPostTransform):
112110
def run(self) -> None:
113111
"""Run the transform."""
114112

115-
# docutils <0.18 (traverse) >=0.18 (findall) compatibility
116-
for fig_node in findall(
117-
self.document, lambda n: "is_subfigure" in getattr(n, "attributes", {})
113+
for fig_node in self.document.findall(
114+
lambda n: "is_subfigure" in getattr(n, "attributes", {})
118115
):
119116
# initiate figure children
120117
children = []
@@ -127,9 +124,9 @@ def run(self) -> None:
127124
layout_class = f"ss-layout-{size}-" + "_".join(
128125
["".join(a.replace(".", "d") for a in row) for row in layout]
129126
)
130-
self.document["subfig_layouts"].setdefault(size, {})[
131-
layout_class
132-
] = layout
127+
self.document["subfig_layouts"].setdefault(size, {})[layout_class] = (
128+
layout
129+
)
133130
classes.append(layout_class)
134131

135132
# add grid

src/sphinx_subfigure/tr_latex.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
from sphinx.transforms.post_transforms import SphinxPostTransform
88
from sphinx.writers.latex import LaTeXTranslator
99

10-
from ._compat import findall
11-
1210

1311
def setup_latex(app: Sphinx):
1412
"""Setup the extension for LaTeX building."""
@@ -50,9 +48,8 @@ class SubfigureLaTexTransform(SphinxPostTransform):
5048
def run(self) -> None:
5149
"""Run the transform."""
5250

53-
# docutils <0.18 (traverse) >=0.18 (findall) compatibility
54-
for fig_node in findall(
55-
self.document, lambda n: "is_subfigure" in getattr(n, "attributes", {})
51+
for fig_node in self.document.findall(
52+
lambda n: "is_subfigure" in getattr(n, "attributes", {})
5653
):
5754
layout = fig_node["layout"]["default"]
5855
if not layout:

tests/fixtures/posttransform_html.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ simple
99
:alt: hallo
1010
.
1111
<document source="<src>/index.rst" subfig_layouts="{'default': {'ss-layout-default-AB': [['A', 'B']]}}">
12-
<figure area_classes="" classes="sphinx-subfigure" grid_classes="" is_subfigure="True" layout="{'default': [['A', 'B']]}">
12+
<figure area_classes="" classes="sphinx-subfigure" grid_classes="" is_subfigure="true" layout="{'default': [['A', 'B']]}">
1313
<SubfigureGridHtml classes="sphinx-subfigure-grid ss-layout-default-AB">
1414
<SubfigureGridItemHtml area="A" classes="sphinx-subfigure-area">
1515
<image alt="hallo" candidates="{'*': 'image.png'}" subfigure_area="A" uri="image.png">

0 commit comments

Comments
 (0)