Skip to content

Commit fa605cf

Browse files
feat(dev): optimize pdm installation in nox (#1344)
Co-authored-by: vi <[email protected]>
1 parent d7050a7 commit fa605cf

File tree

1 file changed

+65
-34
lines changed

1 file changed

+65
-34
lines changed

noxfile.py

Lines changed: 65 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,32 +10,16 @@
1010

1111
from __future__ import annotations
1212

13-
import os
1413
import pathlib
15-
from itertools import chain
16-
from typing import TYPE_CHECKING, Callable, Dict, List, Tuple, TypeVar
14+
from typing import Any, Dict, List, Sequence, Tuple
1715

1816
import nox
1917

20-
if TYPE_CHECKING:
21-
from typing_extensions import Concatenate, ParamSpec
22-
23-
P = ParamSpec("P")
24-
T = TypeVar("T")
25-
26-
NoxSessionFunc = Callable[Concatenate[nox.Session, P], T]
27-
2818
nox.needs_version = ">=2025.5.1"
2919

30-
# see https://pdm-project.org/latest/usage/advanced/#use-nox-as-the-runner
31-
os.environ.update(
32-
{
33-
"PDM_IGNORE_SAVED_PYTHON": "1",
34-
},
35-
)
3620

3721
nox.options.error_on_external_run = True
38-
nox.options.reuse_existing_virtualenvs = True
22+
nox.options.reuse_venv = "yes"
3923
nox.options.sessions = [
4024
"lint",
4125
"check-manifest",
@@ -50,14 +34,53 @@
5034
reset_coverage = True
5135

5236

37+
def install_deps(
38+
session: nox.Session,
39+
*,
40+
extras: Sequence[str] = (),
41+
groups: Sequence[str] = (),
42+
project: bool = True,
43+
) -> None:
44+
"""Helper to install dependencies from a group."""
45+
if not project and extras:
46+
raise TypeError("Cannot install extras without also installing the project")
47+
48+
command: List[str] = [
49+
"pdm",
50+
"sync",
51+
"--fail-fast",
52+
"--clean-unselected",
53+
]
54+
55+
# see https://pdm-project.org/latest/usage/advanced/#use-nox-as-the-runner
56+
env: Dict[str, Any] = {
57+
"PDM_IGNORE_SAVED_PYTHON": "1",
58+
}
59+
60+
command.extend([f"-G={g}" for g in (*extras, *groups)])
61+
62+
if not groups:
63+
# if no dev groups requested, make sure we don't install any
64+
command.append("--prod")
65+
66+
if not project:
67+
command.append("--no-self")
68+
69+
session.run_install(
70+
*command,
71+
env=env,
72+
external=True,
73+
)
74+
75+
5376
@nox.session(python="3.8")
5477
def docs(session: nox.Session) -> None:
5578
"""Build and generate the documentation.
5679
5780
If running locally, will build automatic reloading docs.
5881
If running in CI, will build a production version of the documentation.
5982
"""
60-
session.run_always("pdm", "install", "--prod", "-G", "docs", external=True)
83+
install_deps(session, extras=["docs"])
6184
with session.chdir("docs"):
6285
args = ["-b", "html", "-n", ".", "_build/html", *session.posargs]
6386
if session.interactive:
@@ -86,23 +109,22 @@ def docs(session: nox.Session) -> None:
86109
@nox.session
87110
def lint(session: nox.Session) -> None:
88111
"""Check all files for linting errors"""
89-
session.run_always("pdm", "install", "-G", "tools", external=True)
112+
install_deps(session, project=False, groups=["tools"])
90113

91114
session.run("pre-commit", "run", "--all-files", *session.posargs)
92115

93116

94117
@nox.session(name="check-manifest")
95118
def check_manifest(session: nox.Session) -> None:
96119
"""Run check-manifest."""
97-
# --no-self is provided here because check-manifest builds disnake. There's no reason to build twice, so we don't.
98-
session.run_always("pdm", "install", "--no-self", "-dG", "tools", external=True)
120+
install_deps(session, project=False, groups=["tools"])
99121
session.run("check-manifest", "-v")
100122

101123

102-
@nox.session()
124+
@nox.session
103125
def slotscheck(session: nox.Session) -> None:
104126
"""Run slotscheck."""
105-
session.run_always("pdm", "install", "-dG", "tools", external=True)
127+
install_deps(session, project=True, groups=["tools"])
106128
session.run("python", "-m", "slotscheck", "--verbose", "-m", "disnake")
107129

108130

@@ -113,7 +135,7 @@ def autotyping(session: nox.Session) -> None:
113135
Because of the nature of changes that autotyping makes, and the goal design of examples,
114136
this runs on each folder in the repository with specific settings.
115137
"""
116-
session.run_always("pdm", "install", "-dG", "codemod", external=True)
138+
install_deps(session, project=True, groups=["codemod"])
117139

118140
base_command = ["python", "-m", "libcst.tool", "codemod", "autotyping.AutotypeCommand"]
119141
if not session.interactive:
@@ -172,7 +194,7 @@ def autotyping(session: nox.Session) -> None:
172194
@nox.session(name="codemod", python="3.8")
173195
def codemod(session: nox.Session) -> None:
174196
"""Run libcst codemods."""
175-
session.run_always("pdm", "install", "-dG", "codemod", external=True)
197+
install_deps(session, project=True, groups=["codemod"])
176198

177199
base_command = ["python", "-m", "libcst.tool"]
178200
base_command_codemod = [*base_command, "codemod"]
@@ -196,10 +218,21 @@ def codemod(session: nox.Session) -> None:
196218
session.notify("autotyping", posargs=[])
197219

198220

199-
@nox.session()
221+
@nox.session
200222
def pyright(session: nox.Session) -> None:
201223
"""Run pyright."""
202-
session.run_always("pdm", "install", "-d", "-Gspeed", "-Gdocs", "-Gvoice", external=True)
224+
install_deps(
225+
session,
226+
project=True,
227+
extras=["speed", "voice"],
228+
groups=[
229+
"test", # tests/
230+
"nox", # noxfile.py
231+
"docs", # docs/
232+
"codemod", # scripts/
233+
"typing", # pyright
234+
],
235+
)
203236
env = {
204237
"PYRIGHT_PYTHON_IGNORE_WARNINGS": "1",
205238
}
@@ -221,10 +254,7 @@ def pyright(session: nox.Session) -> None:
221254
)
222255
def test(session: nox.Session, extras: List[str]) -> None:
223256
"""Run tests."""
224-
# shell splitting is not done by nox
225-
extras = list(chain(*(["-G", extra] for extra in extras)))
226-
227-
session.run_always("pdm", "install", "-dG", "test", "-dG", "typing", *extras, external=True)
257+
install_deps(session, project=True, extras=extras, groups=["test"])
228258

229259
pytest_args = ["--cov", "--cov-context=test"]
230260
global reset_coverage # noqa: PLW0603
@@ -243,10 +273,11 @@ def test(session: nox.Session, extras: List[str]) -> None:
243273
)
244274

245275

246-
@nox.session()
276+
@nox.session
247277
def coverage(session: nox.Session) -> None:
248278
"""Display coverage information from the tests."""
249-
session.run_always("pdm", "install", "-dG", "test", external=True)
279+
install_deps(session, project=False, groups=["test"])
280+
250281
posargs = session.posargs
251282
# special-case serve
252283
if "serve" in posargs:

0 commit comments

Comments
 (0)