Skip to content

Commit bae2701

Browse files
committed
nox setup to run tests on python/pydantic matrix simliar to github action
1 parent 6b56235 commit bae2701

File tree

2 files changed

+172
-0
lines changed

2 files changed

+172
-0
lines changed

docs/contributing.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,62 @@ $ bash scripts/test.sh
119119

120120
This command generates a directory `./htmlcov/`, if you open the file `./htmlcov/index.html` in your browser, you can explore interactively the regions of code that are covered by the tests, and notice if there is any region missing.
121121

122+
### Local testing with Nox
123+
124+
You can use `nox` to run the tests under multiple python and pydantic versions similar to the way the
125+
github actions run tests for pull-requests. To setup your local environment, install `nox` and `nox-poetry`.
126+
Example install using `pipx`:
127+
128+
```console
129+
$ pip install pipx
130+
$ pipx install nox
131+
$ pipx inject nox nox-poetry
132+
```
133+
If you are using `pyenv` to manage your pythons, enable all versions that you want to run the
134+
tests on:
135+
136+
```console
137+
$ pyenv global 3.7 3.8 3.9 3.10 3.11 3.12
138+
```
139+
Run the tests:
140+
```console
141+
$ nox -rs tests
142+
```
143+
144+
This will find all python versions and run the test suite with each python versions / pydantic combination.
145+
146+
### Other nox Sessions
147+
148+
#### Lint
149+
150+
Run mypy and other linting tests for all python / pydantic version combinations using the `lint` session:
151+
152+
```console
153+
$ nox -r --session=lint
154+
```
155+
156+
#### The pre-commit session
157+
158+
`pre-commit` is a multi-language linter framework and a Git hook manager.
159+
160+
Run pre-commit from Nox using the pre-commit session:
161+
162+
```console
163+
$ nox --session=pre-commit
164+
```
165+
166+
This session always runs with the current stable release of Python.
167+
168+
Use the separator -- to pass additional options to pre-commit. For example,
169+
the following command installs the pre-commit hooks, so they run automatically
170+
on every commit you make:
171+
172+
```console
173+
$ nox --session=pre-commit -- install
174+
```
175+
176+
177+
122178
## Thanks
123179

124180
Thanks for contributing! ☕

noxfile.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
from pathlib import Path
2+
from textwrap import dedent
3+
4+
from nox import parametrize
5+
from nox_poetry import Session, session
6+
7+
pydantic1 = ">=1.10.0,<2.0.0"
8+
pydantic2 = ">=2.0.2,<3.0.0"
9+
pydantic_versions = {
10+
pydantic1: "pyd1",
11+
pydantic2: "pyd2",
12+
}
13+
14+
15+
@session(python=["3.12", "3.11", "3.10", "3.9", "3.8", "3.7"])
16+
@parametrize("pydantic", list(pydantic_versions.keys()), ids=pydantic_versions.values())
17+
def tests(session: Session, pydantic: str) -> None:
18+
"""Run pytest with coverage."""
19+
session.run_always("poetry", "install", external=True)
20+
session.install(".")
21+
session.run_always(
22+
"pip", "install", "--upgrade", f"pydantic{pydantic}", external=True
23+
)
24+
my_env = {
25+
# Need to set PYTHONPATH because some tests depend on docs_src
26+
"PYTHONPATH": session.invoked_from,
27+
"COVERAGE_FILE": f"coverage/.coverage.py{session.python}-{pydantic_versions[pydantic]}",
28+
"CONTEXT": f"py{session.python}-{pydantic_versions[pydantic]}",
29+
}
30+
session.log(f"Using env: {my_env}")
31+
session.run("bash", "scripts/test.sh", env=my_env, external=True)
32+
session.notify("coverage")
33+
34+
35+
@session(python=["3.8"])
36+
def coverage(session: Session) -> None:
37+
"""Gather coverage from test runs."""
38+
session.install("coverage[toml]", ".")
39+
session.run("coverage", "combine", "coverage")
40+
session.run("coverage", "report")
41+
session.run("coverage", "html", "--show-contexts", "--title", "Coverage")
42+
43+
44+
@session
45+
@parametrize("python", ["3.12", "3.11", "3.10", "3.9", "3.8", "3.7"])
46+
@parametrize("pydantic", list(pydantic_versions.keys()), ids=pydantic_versions.values())
47+
def lint(session: Session, python: str, pydantic: str) -> None:
48+
"""Run lint checks."""
49+
if python != "3.7" and pydantic == pydantic2:
50+
session.run_always("poetry", "install", external=True)
51+
session.install(".")
52+
session.run_always(
53+
"pip", "install", "--upgrade", f"pydantic{pydantic}", external=True
54+
)
55+
session.run("bash", "scripts/lint.sh", external=True)
56+
57+
58+
def activate_virtualenv_in_precommit_hooks(session: Session) -> None:
59+
"""Activate virtualenv in hooks installed by pre-commit.
60+
61+
This function patches git hooks installed by pre-commit to activate the
62+
session's virtual environment. This allows pre-commit to locate hooks in
63+
that environment when invoked from git.
64+
65+
Args:
66+
session: The Session object.
67+
"""
68+
if session.bin is None:
69+
return
70+
71+
virtualenv = session.env.get("VIRTUAL_ENV")
72+
if virtualenv is None:
73+
return
74+
75+
hookdir = Path(".git") / "hooks"
76+
if not hookdir.is_dir():
77+
return
78+
79+
for hook in hookdir.iterdir():
80+
if hook.name.endswith(".sample") or not hook.is_file():
81+
continue
82+
83+
text = hook.read_text()
84+
bindir = repr(session.bin)[1:-1] # strip quotes
85+
if not (
86+
Path("A") == Path("a") and bindir.lower() in text.lower() or bindir in text
87+
):
88+
continue
89+
90+
lines = text.splitlines()
91+
if not (lines[0].startswith("#!") and "python" in lines[0].lower()):
92+
continue
93+
94+
header = dedent(
95+
f"""\
96+
import os
97+
os.environ["VIRTUAL_ENV"] = {virtualenv!r}
98+
os.environ["PATH"] = os.pathsep.join((
99+
{session.bin!r},
100+
os.environ.get("PATH", ""),
101+
))
102+
"""
103+
)
104+
105+
lines.insert(1, header)
106+
hook.write_text("\n".join(lines))
107+
108+
109+
@session(name="pre-commit", python="3.12")
110+
def precommit(session: Session) -> None:
111+
"""Lint using pre-commit."""
112+
args = session.posargs or ["run", "--all-files", "--show-diff-on-failure"]
113+
session.install("ruff", "pre-commit", "pre-commit-hooks")
114+
session.run("pre-commit", *args)
115+
if args and args[0] == "install":
116+
activate_virtualenv_in_precommit_hooks(session)

0 commit comments

Comments
 (0)