Skip to content

Commit 5b44e48

Browse files
henryiiiCopilot
andauthored
feat: add noxfile checks (#659)
* feat: add noxfile checks Signed-off-by: Henry Schreiner <[email protected]> * Apply suggestions from code review Co-authored-by: Copilot <[email protected]> * fix: some smaller fixes Signed-off-by: Henry Schreiner <[email protected]> * refactor: rename --------- Signed-off-by: Henry Schreiner <[email protected]> Signed-off-by: Henry Schreiner <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent 18d60e9 commit 5b44e48

File tree

9 files changed

+480
-17
lines changed

9 files changed

+480
-17
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,13 @@ repos:
4646
rev: "v1.18.2"
4747
hooks:
4848
- id: mypy
49-
files: "(src|tests)"
49+
files: "(src|tests|noxfile.py)"
5050
args: []
5151
additional_dependencies:
5252
- click
5353
- markdown-it-py
5454
- pytest
55+
- nox
5556
- orjson
5657
- repo-review>=0.10.6
5758
- rich

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,14 @@ for family, grp in itertools.groupby(collected.checks.items(), key=lambda x: x[1
358358
- [`MY105`](https://learn.scientific-python.org/development/guides/style#MY105): MyPy enables redundant-expr
359359
- [`MY106`](https://learn.scientific-python.org/development/guides/style#MY106): MyPy enables truthy-bool
360360

361+
### Nox
362+
- [`NOX101`](https://learn.scientific-python.org/development/guides/tasks#NOX101): Sets minimum nox version
363+
- [`NOX102`](https://learn.scientific-python.org/development/guides/tasks#NOX102): Sets venv backend
364+
- [`NOX103`](https://learn.scientific-python.org/development/guides/tasks#NOX103): Set default per session instead of session list
365+
- [`NOX201`](https://learn.scientific-python.org/development/guides/tasks#NOX201): Set a script block with dependencies in your noxfile
366+
- [`NOX202`](https://learn.scientific-python.org/development/guides/tasks#NOX202): Has a shebang line
367+
- [`NOX203`](https://learn.scientific-python.org/development/guides/tasks#NOX203): Provide a main block to run nox
368+
361369
### Pre-commit
362370
- [`PC100`](https://learn.scientific-python.org/development/guides/style#PC100): Has pre-commit-hooks
363371
- [`PC110`](https://learn.scientific-python.org/development/guides/style#PC110): Uses black or ruff-format

docs/pages/guides/tasks.md

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,9 @@ This supports setting up a quick server as well, run like this:
260260
$ nox -s docs -- --serve
261261
```
262262

263+
Notice that we set `default=False` so that docs are not built every time nox is
264+
run without arguments. {% rr NOX103 %}
265+
263266
#### Build (pure Python)
264267

265268
For pure Python packages, this could be useful:
@@ -304,7 +307,8 @@ def build(session: nox.Session) -> None:
304307

305308
The [uv](https://github.com/astral-sh/uv) project is a Rust reimplementation of
306309
pip, pip-tools, and venv that is very, very fast. You can tell nox to use `uv`
307-
if it is on your system by adding the following to your `noxfile.py`:
310+
if it is on your system by adding the following to your `noxfile.py`
311+
{% rr NOX102 %}:
308312

309313
```python
310314
nox.needs_version = ">=2024.3.2"
@@ -322,7 +326,39 @@ Check your jobs with `uv`; most things do not need to change. The main
322326
difference is `uv` doesn't install `pip` unless you ask it to. If you want to
323327
interact with uv, nox might be getting uv from it's environment instead of the
324328
system environment, so you can install `uv` if `shutil.which("uv")` returns
325-
`None`.
329+
`None`. You should also set a minimum version of nox. {% rr NOX101 %}
330+
331+
### Running without nox or requiring dependencies
332+
333+
Nox also allows you to use the script block, both for running with runners (like
334+
`uv run` and `pipx run`), and for specifying dependencies to install or a
335+
minimum nox version; nox will read this block and run itself from a venv with
336+
those requirements installed if they are not met.
337+
338+
```python
339+
#!/usr/bin/env -S uv run --script
340+
341+
# /// script
342+
# dependencies = ["nox>=2025.10.16"]
343+
# ///
344+
345+
import nox
346+
347+
nox.needs_version = ">=2025.10.16"
348+
349+
if __name__ == "__main__":
350+
nox.main()
351+
```
352+
353+
The script block then specifies that nox is required {% rr NOX201 %}. If you
354+
want other dependencies here, those will also be installed before the file is
355+
run. Older versions of nox still need the `nox.needs_version` line to keep nice
356+
error messages.
357+
358+
The first line is a shebang line {% rr NOX202 %}, which allows this file to be
359+
run directly if it is made executable. You can put any runner here; uv is shown.
360+
You also need a main block {% rr NOX203 %} to allow nox to be run when this file
361+
is executed directly.
326362

327363
### Examples
328364

noxfile.py

100644100755
Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
#!/usr/bin/env -S uv run --script
2+
3+
# /// script
4+
# dependencies = ["nox>=2025.2.9"]
5+
# ///
6+
17
"""
28
Nox runner for cookie & sp-repo-review.
39
@@ -7,7 +13,8 @@
713
from __future__ import annotations
814

915
import difflib
10-
import email.message
16+
import email.parser
17+
import email.policy
1118
import functools
1219
import json
1320
import os
@@ -26,7 +33,6 @@
2633
import nox
2734

2835
nox.needs_version = ">=2025.2.9"
29-
nox.options.sessions = ["rr_lint", "rr_tests", "rr_pylint", "readme"]
3036
nox.options.default_venv_backend = "uv|virtualenv"
3137

3238

@@ -58,7 +64,7 @@ def get_expected_version(backend: str, vcs: bool) -> str:
5864
return "0.2.3" if vcs and backend not in {"maturin", "mesonpy", "uv"} else "0.1.0"
5965

6066

61-
def make_copier(session: nox.Session, backend: str, vcs: bool) -> None:
67+
def make_copier(session: nox.Session, backend: str, vcs: bool) -> Path:
6268
package_dir = Path(f"copy-{backend}")
6369
if package_dir.exists():
6470
rmtree_ro(package_dir)
@@ -85,7 +91,7 @@ def make_copier(session: nox.Session, backend: str, vcs: bool) -> None:
8591
return package_dir
8692

8793

88-
def make_cookie(session: nox.Session, backend: str, vcs: bool) -> None:
94+
def make_cookie(session: nox.Session, backend: str, vcs: bool) -> Path:
8995
package_dir = Path(f"cookie-{backend}")
9096
if package_dir.exists():
9197
rmtree_ro(package_dir)
@@ -106,7 +112,7 @@ def make_cookie(session: nox.Session, backend: str, vcs: bool) -> None:
106112
return package_dir
107113

108114

109-
def make_cruft(session: nox.Session, backend: str, vcs: bool) -> None:
115+
def make_cruft(session: nox.Session, backend: str, vcs: bool) -> Path:
110116
package_dir = Path(f"cruft-{backend}")
111117
if package_dir.exists():
112118
rmtree_ro(package_dir)
@@ -156,7 +162,7 @@ def init_git(session: nox.Session, package_dir: Path) -> None:
156162
IGNORE_FILES = {"__pycache__", ".git", ".copier-answers.yml", ".cruft.json"}
157163

158164

159-
def valid_path(path: Path):
165+
def valid_path(path: Path) -> bool:
160166
return path.is_file() and not IGNORE_FILES & set(path.parts)
161167

162168

@@ -233,7 +239,9 @@ def tests(session: nox.Session, backend: str, vcs: bool) -> None:
233239
"-c",
234240
f'import importlib.metadata as m; print(m.version("{name}"))',
235241
silent=True,
236-
).strip()
242+
)
243+
assert version
244+
version = version.strip()
237245
expected_version = get_expected_version(backend, vcs)
238246
assert version == expected_version, f"{version=} != {expected_version=}"
239247

@@ -287,7 +295,9 @@ def dist(session: nox.Session, backend: str, vcs: bool) -> None:
287295
(metadata_path,) = (
288296
n for n in names if n.endswith("PKG-INFO") and "egg-info" not in n
289297
)
290-
with tf.extractfile(metadata_path) as mfile:
298+
efile = tf.extractfile(metadata_path)
299+
assert efile
300+
with efile as mfile:
291301
info = mfile.read().decode("utf-8")
292302
if "License-Expression: BSD-3-Clause" not in info:
293303
msg = "License expression not found in METADATA"
@@ -302,7 +312,11 @@ def dist(session: nox.Session, backend: str, vcs: bool) -> None:
302312
metadata_path = next(iter(n for n in names if n.endswith("METADATA")))
303313
with zf.open(metadata_path) as mfile:
304314
txt = mfile.read()
305-
license_fields = email.message.EmailMessage(txt).get_all("License", [])
315+
license_fields = (
316+
email.parser.BytesParser(policy=email.policy.default)
317+
.parsebytes(txt)
318+
.get_all("License", [])
319+
)
306320
if license_fields:
307321
msg = f"Should not have anything in the License slot, got {license_fields}"
308322
session.error(msg)
@@ -403,14 +417,16 @@ def pc_bump(session: nox.Session) -> None:
403417

404418
for proj, (old_version, space) in old_versions.items():
405419
if proj not in versions:
406-
versions[proj] = session.run(
420+
versions_proj = session.run(
407421
"lastversion",
408422
"--at=github",
409423
"--format=tag",
410424
"--exclude=~alpha|beta|rc",
411425
proj,
412426
silent=True,
413-
).strip()
427+
)
428+
assert versions_proj
429+
versions[proj] = versions_proj.strip()
414430
new_version = versions[proj]
415431

416432
after = PC_REPL_LINE.format(proj, new_version, space, '"')
@@ -449,7 +465,7 @@ def get_latest_version_tag(repo: str, old_version: str) -> dict[str, Any] | None
449465
and x["name"].startswith("v") == old_version.startswith("v")
450466
]
451467
if tags:
452-
return tags[0]
468+
return tags[0] # type: ignore[no-any-return]
453469
return None
454470

455471

@@ -548,3 +564,7 @@ def rr_build(session: nox.Session) -> None:
548564

549565
session.install("build")
550566
session.run("python", "-m", "build")
567+
568+
569+
if __name__ == "__main__":
570+
nox.main()

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ mypy = "sp_repo_review.checks.mypy:repo_review_checks"
6666
github = "sp_repo_review.checks.github:repo_review_checks"
6767
readthedocs = "sp_repo_review.checks.readthedocs:repo_review_checks"
6868
setupcfg = "sp_repo_review.checks.setupcfg:repo_review_checks"
69+
noxfile = "sp_repo_review.checks.noxfile:repo_review_checks"
6970

7071
[project.entry-points."repo_review.fixtures"]
7172
workflows = "sp_repo_review.checks.github:workflows"
@@ -74,6 +75,7 @@ precommit = "sp_repo_review.checks.precommit:precommit"
7475
readthedocs = "sp_repo_review.checks.readthedocs:readthedocs"
7576
ruff = "sp_repo_review.checks.ruff:ruff"
7677
setupcfg = "sp_repo_review.checks.setupcfg:setupcfg"
78+
noxfile = "sp_repo_review.checks.noxfile:noxfile"
7779

7880
[project.entry-points."repo_review.families"]
7981
scikit-hep = "sp_repo_review.families:get_families"

0 commit comments

Comments
 (0)