Skip to content

Commit 60770f4

Browse files
authored
feat: use ruff to format docs code instead of blacken-docs (#833)
* feat: use ruff to format docs code instead of blacken-docs Ruff 0.16 formats Python code blocks in Markdown, so a single tool covers both code and docs. Drop the blacken-docs hook, bump ruff-pre-commit to v0.16.0, and add markdown to the ruff-format hook's types_or. PC111 now passes with either blacken-docs or a ruff-format hook that lists markdown in types_or. Assisted-by: ClaudeCode:claude-opus-4.8 * docs: use tabs for the two doc-formatter options Assisted-by: ClaudeCode:claude-opus-4.8 * refactor: accept ruff-format defaults in PC111 Ruff's pre-commit hook will format Markdown by default at some point. Pass PC111 unless the ruff-format hook filters Markdown out, instead of requiring a literal `markdown` entry. Assisted-by: ClaudeCode:claude-opus-5 * refactor: use pattern matching in PC111 Assisted-by: ClaudeCode:claude-opus-5 * chore: add pyproject to list too Signed-off-by: Henry Schreiner <henryfs@princeton.edu> --------- Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
1 parent c8fa0e0 commit 60770f4

8 files changed

Lines changed: 121 additions & 27 deletions

File tree

‎.pre-commit-config.yaml‎

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,13 @@ repos:
2222
- id: requirements-txt-fixer
2323
- id: trailing-whitespace
2424

25-
- repo: https://github.com/adamchainz/blacken-docs
26-
rev: "1.20.0"
27-
hooks:
28-
- id: blacken-docs
29-
additional_dependencies: [black==24.*]
30-
3125
- repo: https://github.com/astral-sh/ruff-pre-commit
32-
rev: "v0.15.22"
26+
rev: "v0.16.0"
3327
hooks:
3428
- id: ruff-check
3529
args: ["--fix"]
3630
- id: ruff-format
31+
types_or: [python, pyi, jupyter, markdown, pyproject]
3732

3833
- repo: https://github.com/pre-commit/pygrep-hooks
3934
rev: "v1.10.0"

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ Will not show up if using lefthook instead of pre-commit/prek.
388388

389389
- [`PC100`](https://learn.scientific-python.org/development/guides/style#PC100): Has pre-commit-hooks
390390
- [`PC110`](https://learn.scientific-python.org/development/guides/style#PC110): Uses black or ruff-format
391-
- [`PC111`](https://learn.scientific-python.org/development/guides/style#PC111): Uses blacken-docs
391+
- [`PC111`](https://learn.scientific-python.org/development/guides/style#PC111): Formats code in docs (ruff-format or blacken-docs)
392392
- [`PC140`](https://learn.scientific-python.org/development/guides/style#PC140): Uses a type checker
393393
- [`PC160`](https://learn.scientific-python.org/development/guides/style#PC160): Uses a spell checker
394394
- [`PC170`](https://learn.scientific-python.org/development/guides/style#PC170): Uses PyGrep hooks (only needed if rST present)

‎docs/guides/style.md‎

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ Here is the snippet to add the formatter to your `.pre-commit-config.yml`
148148

149149
```yaml
150150
- repo: https://github.com/astral-sh/ruff-pre-commit
151-
rev: "v0.15.22"
151+
rev: "v0.16.0"
152152
hooks:
153153
# id: ruff-check would go here if using both
154154
- id: ruff-format
@@ -205,11 +205,31 @@ option! Most of the time, you can find a way to make the Blacked code look
205205
better by rewriting your code; factor out long unreadable portions into a
206206
variable, avoid writing matrices as 1D lists, etc.
207207

208-
:::{dropdown} Documentation / README snippets support
209-
{rr}`PC111` If you want Black used in your documentation, you can use
210-
blacken-docs. This can even catch syntax errors in code snippets! It supports
211-
markdown and restructured text. Note that because black is in
212-
`additional_dependencies`, you'll have to keep it up to date manually.
208+
::::::{dropdown} Documentation / README snippets support
209+
{rr}`PC111` A formatter can keep the Python snippets in your docs tidy, and
210+
even catch syntax errors in them.
211+
212+
:::::{tab-set}
213+
::::{tab-item} Ruff-format
214+
Ruff (0.16+) formats Python code blocks in Markdown, so a single tool covers
215+
your code and your docs. Add `markdown` to the `ruff-format` hook's `types_or`:
216+
217+
```yaml
218+
- repo: https://github.com/astral-sh/ruff-pre-commit
219+
rev: "v0.16.0"
220+
hooks:
221+
- id: ruff-format
222+
types_or: [python, pyi, jupyter, markdown, pyproject]
223+
```
224+
225+
The hook only sees the file types you list, so `markdown` is needed until it is
226+
part of the hook's default.
227+
228+
::::
229+
::::{tab-item} blacken-docs
230+
Use blacken-docs if you use Black, or want reStructuredText snippets formatted
231+
too. Note that because black is in `additional_dependencies`, you'll have to
232+
keep it up to date manually.
213233

214234
```yaml
215235
- repo: https://github.com/adamchainz/blacken-docs
@@ -219,7 +239,9 @@ markdown and restructured text. Note that because black is in
219239
additional_dependencies: [black==24.*]
220240
```
221241

222-
:::
242+
::::
243+
:::::
244+
::::::
223245

224246
## Ruff
225247

@@ -234,7 +256,7 @@ pre-commit hook.
234256

235257
```yaml
236258
- repo: https://github.com/astral-sh/ruff-pre-commit
237-
rev: "v0.15.22"
259+
rev: "v0.16.0"
238260
hooks:
239261
- id: ruff-check
240262
args: ["--fix", "--show-fixes"]

‎docs/principles/design.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ def get_image(
213213
*,
214214
normalize: bool = True,
215215
beginning: int = 0,
216-
end: int | None = None
216+
end: int | None = None,
217217
) -> np.ndarray: ...
218218
```
219219

‎pyproject.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ ignore = [
180180
"A002", # Okay for arguments to shadow builtins
181181
"A004", # Okay for imports to shadow builtins (like print)
182182
"COM812", # Trailing commas teach the formatter
183+
"CPY001", # No copyright notices
183184
"D", # Docstrings
184185
"E501", # Line too long
185186
"FBT", # Boolean args fine

‎src/sp_repo_review/checks/precommit.py‎

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ def precommit(root: Traversable) -> dict[str, Any]:
2424
return {}
2525

2626

27+
def _formats_markdown(hook: dict[str, Any]) -> bool:
28+
"A `ruff-format` hook that Markdown files can reach."
29+
match hook:
30+
case {"id": "ruff-format", "types_or": types}:
31+
return "markdown" in types
32+
# No `types_or` means Ruff's own default, which will include
33+
# Markdown in a future release.
34+
case {"id": "ruff-format"}:
35+
return True
36+
case _:
37+
return False
38+
39+
2740
class PreCommit:
2841
family = "pre-commit"
2942
requires = {"PY006"}
@@ -45,7 +58,7 @@ def describe(self) -> str:
4558
return "one of " + ", ".join(msgs)
4659

4760
@classmethod
48-
def check(cls, precommit: dict[str, Any]) -> bool | None | str:
61+
def check(cls, precommit: dict[str, Any]) -> bool | str | None:
4962
"Must have {self.describe} in `.pre-commit-config.yaml`"
5063
assert cls.repos, f"{cls.__name__} must have a repo, invalid class definition"
5164
for repo_item in precommit.get("repos", {}):
@@ -89,14 +102,40 @@ class PC110(PreCommit):
89102

90103

91104
class PC111(PreCommit):
92-
"Uses blacken-docs"
105+
"Formats code in docs (ruff-format or blacken-docs)"
93106

94107
requires = {"PY006", "PC110"}
95-
repos = {"https://github.com/adamchainz/blacken-docs"}
108+
repos = {
109+
"https://github.com/astral-sh/ruff-pre-commit",
110+
"https://github.com/adamchainz/blacken-docs",
111+
}
96112
renamed = {
97113
"https://github.com/asottile/blacken-docs": "https://github.com/adamchainz/blacken-docs"
98114
}
99115

116+
@classmethod
117+
def check(cls, precommit: dict[str, Any]) -> bool | str | None:
118+
"""
119+
Add `blacken-docs`, or (Ruff 0.16+) let Markdown files reach the
120+
`ruff-format` hook in `.pre-commit-config.yaml`. Until the hook formats
121+
Markdown by default, that means `types_or: [python, pyi, jupyter,
122+
markdown, pyproject]`.
123+
"""
124+
for repo_item in precommit.get("repos", {}):
125+
match repo_item.get("repo", "").lower(), repo_item.get("hooks", []):
126+
case "https://github.com/adamchainz/blacken-docs", _:
127+
return True
128+
case "https://github.com/astral-sh/ruff-pre-commit", hooks if any(
129+
_formats_markdown(hook) for hook in hooks
130+
):
131+
return True
132+
case repo, _ if repo in cls.renamed:
133+
return (
134+
f"Use `{cls.renamed[repo]}` instead of `{repo}` in "
135+
"`.pre-commit-config.yaml`"
136+
)
137+
return False
138+
100139

101140
class PC190(PreCommit):
102141
"Uses a linter (Ruff/Flake8)"

‎tests/test_precommit.py‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,48 @@ def test_pc111():
6666
assert compute_check("PC111", precommit=precommit).result
6767

6868

69+
def test_pc111_ruff_markdown():
70+
precommit = yaml.safe_load("""
71+
repos:
72+
- repo: https://github.com/astral-sh/ruff-pre-commit
73+
hooks:
74+
- id: ruff-format
75+
types_or: [python, pyi, jupyter, markdown, pyproject]
76+
""")
77+
assert compute_check("PC111", precommit=precommit).result
78+
79+
80+
def test_pc111_ruff_default_types():
81+
precommit = yaml.safe_load("""
82+
repos:
83+
- repo: https://github.com/astral-sh/ruff-pre-commit
84+
hooks:
85+
- id: ruff-format
86+
""")
87+
assert compute_check("PC111", precommit=precommit).result
88+
89+
90+
def test_pc111_ruff_markdown_excluded():
91+
precommit = yaml.safe_load("""
92+
repos:
93+
- repo: https://github.com/astral-sh/ruff-pre-commit
94+
hooks:
95+
- id: ruff-format
96+
types_or: [python, pyi, jupyter]
97+
""")
98+
assert not compute_check("PC111", precommit=precommit).result
99+
100+
101+
def test_pc111_ruff_check_only():
102+
precommit = yaml.safe_load("""
103+
repos:
104+
- repo: https://github.com/astral-sh/ruff-pre-commit
105+
hooks:
106+
- id: ruff-check
107+
""")
108+
assert not compute_check("PC111", precommit=precommit).result
109+
110+
69111
def test_pc111_rename():
70112
precommit = yaml.safe_load("""
71113
repos:

‎{{cookiecutter.project_name}}/.pre-commit-config.yaml‎

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,6 @@ ci:
66
exclude: ^.cruft.json|.copier-answers.yml$
77

88
repos:
9-
- repo: https://github.com/adamchainz/blacken-docs
10-
rev: "1.20.0"
11-
hooks:
12-
- id: blacken-docs
13-
additional_dependencies: [black==26.*]
14-
159
- repo: https://github.com/pre-commit/pre-commit-hooks
1610
rev: "v6.0.0"
1711
hooks:
@@ -43,11 +37,12 @@ repos:
4337
args: [--prose-wrap=always]
4438

4539
- repo: https://github.com/astral-sh/ruff-pre-commit
46-
rev: "v0.15.22"
40+
rev: "v0.16.0"
4741
hooks:
4842
- id: ruff-check
4943
args: ["--fix"]
5044
- id: ruff-format
45+
types_or: [python, pyi, jupyter, markdown, pyproject]
5146

5247
{%- if cookiecutter.backend in ["pybind11", "skbuild", "mesonpy"] %}
5348

0 commit comments

Comments
 (0)