Skip to content

Commit 1412854

Browse files
committed
Improve noxfile for dev install and management
Moving toward simplicity of use, the `noxfile.txt` now has a session called `dev`. This session creates, if needed, the `.venv` and installs all development requirements. Additionally, the `update` and `upgrade` sessions have been renamed to `update-deps` and `upgrade-deps` respectively. They now use the new, awesome pattern of a single constraint file for dependencies. Finally, added the `--no-annotations` to the `pip-compile` commands and what a difference that makes! I have yet to understand when and why `pip-compile` will expand `requirements/requirements.txt` to `/home/preocts/project-name/requirements/requirements.txt`. In practice, the results are different depending on the OS being used as well. Since it all lead to noisy diffs without much value, removing it seems ideal!
1 parent 9b12424 commit 1412854

2 files changed

Lines changed: 83 additions & 146 deletions

File tree

noxfile.py

Lines changed: 72 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,11 @@
1111
MODULE_NAME = "module_name"
1212
TESTS_PATH = "tests"
1313
COVERAGE_FAIL_UNDER = 50
14-
DEFAULT_PYTHON_VERSION = "3.12"
14+
DEFAULT_PYTHON = "3.12"
1515
PYTHON_MATRIX = ["3.9", "3.10", "3.11", "3.12", "3.13"]
1616
VENV_BACKEND = "venv"
1717
VENV_PATH = ".venv"
18-
REQUIREMENT_IN_FILES = [
19-
pathlib.Path("requirements/requirements.in"),
20-
]
18+
REQUIREMENTS_PATH = "./requirements"
2119

2220
# What we allowed to clean (delete)
2321
CLEANABLE_TARGETS = [
@@ -43,6 +41,38 @@
4341
]
4442

4543

44+
@nox.session(python=False)
45+
def dev(session: nox.Session) -> None:
46+
"""Setup a development environment by creating the venv and installs dependencies."""
47+
# Use the active environement if it exists, otherwise create a new one
48+
venv_path = os.environ.get("VIRTUAL_ENV", VENV_PATH)
49+
50+
if sys.platform == "win32":
51+
py_command = "py"
52+
venv_path = f"{venv_path}/Scripts"
53+
activate_command = f"{venv_path}/activate"
54+
else:
55+
py_command = f"python{DEFAULT_PYTHON}"
56+
venv_path = f"{venv_path}/bin"
57+
activate_command = f"source {venv_path}/activate"
58+
59+
if not os.path.exists(VENV_PATH):
60+
session.run(py_command, "-m", "venv", VENV_PATH, "--upgrade-deps")
61+
62+
python = f"{venv_path}/python"
63+
requirement_files = get_requirement_files()
64+
65+
session.run(python, "-m", "pip", "install", "-e", ".")
66+
for requirement_file in requirement_files:
67+
session.run(python, "-m", "pip", "install", "-r", requirement_file)
68+
69+
session.run(python, "-m", "pip", "install", "pre-commit")
70+
session.run(f"{venv_path}/pre-commit", "install")
71+
72+
if not os.environ.get("VIRTUAL_ENV"):
73+
session.log(f"\n\nRun '{activate_command}' to enter the virtual environment.\n")
74+
75+
4676
@nox.session(python=PYTHON_MATRIX, venv_backend=VENV_BACKEND)
4777
def version_coverage(session: nox.Session) -> None:
4878
"""Run unit tests with coverage saved to partial file."""
@@ -52,7 +82,7 @@ def version_coverage(session: nox.Session) -> None:
5282
session.run("coverage", "run", "-p", "-m", "pytest", TESTS_PATH)
5383

5484

55-
@nox.session(python=DEFAULT_PYTHON_VERSION, venv_backend=VENV_BACKEND)
85+
@nox.session(python=DEFAULT_PYTHON, venv_backend=VENV_BACKEND)
5686
def coverage_combine(session: nox.Session) -> None:
5787
"""Combine all coverage partial files and generate JSON report."""
5888
print_standard_logs(session)
@@ -65,7 +95,7 @@ def coverage_combine(session: nox.Session) -> None:
6595
session.run("python", "-m", "coverage", "json")
6696

6797

68-
@nox.session(python=DEFAULT_PYTHON_VERSION, venv_backend=VENV_BACKEND)
98+
@nox.session(python=DEFAULT_PYTHON, venv_backend=VENV_BACKEND)
6999
def mypy(session: nox.Session) -> None:
70100
"""Run mypy against package and all required dependencies."""
71101
print_standard_logs(session)
@@ -83,7 +113,7 @@ def coverage(session: nox.Session) -> None:
83113
session.run("coverage", "report", "-m")
84114

85115

86-
@nox.session(python=DEFAULT_PYTHON_VERSION, venv_backend=VENV_BACKEND)
116+
@nox.session(python=DEFAULT_PYTHON, venv_backend=VENV_BACKEND)
87117
def build(session: nox.Session) -> None:
88118
"""Build distribution files."""
89119
print_standard_logs(session)
@@ -92,49 +122,41 @@ def build(session: nox.Session) -> None:
92122
session.run("python", "-m", "build")
93123

94124

95-
@nox.session(python=False, venv_backend=VENV_BACKEND)
96-
def install(session: nox.Session) -> None:
97-
"""Setup a development environment. Uses active venv if available, builds one if not."""
98-
# Use the active environement if it exists, otherwise create a new one
99-
venv_path = os.environ.get("VIRTUAL_ENV", VENV_PATH)
100-
101-
if sys.platform == "win32":
102-
py_command = "py"
103-
venv_path = f"{venv_path}/Scripts"
104-
activate_command = f"{venv_path}/activate"
105-
else:
106-
py_command = f"python{DEFAULT_PYTHON_VERSION}"
107-
venv_path = f"{venv_path}/bin"
108-
activate_command = f"source {venv_path}/activate"
109-
110-
if not os.path.exists(VENV_PATH):
111-
session.run(py_command, "-m", "venv", VENV_PATH, "--upgrade-deps")
112-
113-
session.run(f"{venv_path}/python", "-m", "pip", "install", "-e", ".[dev,test]")
114-
session.run(f"{venv_path}/pre-commit", "install")
115-
116-
if not venv_path:
117-
session.log(f"\n\nRun '{activate_command}' to enter the virtual environment.\n")
118-
119-
120-
@nox.session(python=DEFAULT_PYTHON_VERSION, venv_backend=VENV_BACKEND)
121-
def update(session: nox.Session) -> None:
122-
"""Process requirement*.in files, updating only additions/removals."""
125+
@nox.session(python=DEFAULT_PYTHON, venv_backend=VENV_BACKEND, name="update-deps")
126+
def update_deps(session: nox.Session) -> None:
127+
"""Process requirement*.txt files, updating only additions/removals."""
123128
print_standard_logs(session)
124129

125-
session.install("pip-tools")
126-
for filename in REQUIREMENT_IN_FILES:
127-
session.run("pip-compile", "--no-emit-index-url", str(filename))
128-
130+
requirement_files = get_requirement_files()
129131

130-
@nox.session(python=DEFAULT_PYTHON_VERSION, venv_backend=VENV_BACKEND)
131-
def upgrade(session: nox.Session) -> None:
132-
"""Process requirement*.in files and upgrade all libraries as possible."""
132+
session.install("pip-tools")
133+
session.run(
134+
"pip-compile",
135+
"--no-annotate",
136+
"--no-emit-index-url",
137+
"--output-file",
138+
f"{REQUIREMENTS_PATH}/constraints.txt",
139+
*requirement_files,
140+
)
141+
142+
143+
@nox.session(python=DEFAULT_PYTHON, venv_backend=VENV_BACKEND, name="upgrade-deps")
144+
def upgrade_deps(session: nox.Session) -> None:
145+
"""Process requirement*.txt files and upgrade all libraries as possible."""
133146
print_standard_logs(session)
134147

148+
requirement_files = get_requirement_files()
149+
135150
session.install("pip-tools")
136-
for filename in REQUIREMENT_IN_FILES:
137-
session.run("pip-compile", "--no-emit-index-url", "--upgrade", str(filename))
151+
session.run(
152+
"pip-compile",
153+
"--no-annotate",
154+
"--no-emit-index-url",
155+
"--upgrade",
156+
"--output-file",
157+
f"{REQUIREMENTS_PATH}/constraints.txt",
158+
*requirement_files,
159+
)
138160

139161

140162
@nox.session(python=False, venv_backend=VENV_BACKEND)
@@ -157,3 +179,9 @@ def print_standard_logs(session: nox.Session) -> None:
157179
version = session.run("python", "--version", silent=True)
158180
session.log(f"Running from: {session.bin}")
159181
session.log(f"Running with: {version}")
182+
183+
184+
def get_requirement_files() -> list[pathlib.Path]:
185+
"""Get a list of requirement files matching "requirements*.txt"."""
186+
glob = pathlib.Path(REQUIREMENTS_PATH).glob("requirements*.txt")
187+
return [path for path in glob]

requirements/constraints.txt

Lines changed: 11 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -2,121 +2,30 @@
22
# This file is autogenerated by pip-compile with Python 3.12
33
# by the following command:
44
#
5-
# pip-compile --output-file=requirements/constraints.txt requirements/requirements-dev.txt requirements/requirements-test.txt requirements/requirements.txt
5+
# pip-compile --no-annotate --no-emit-index-url --output-file=./requirements/constraints.txt requirements/requirements-dev.txt requirements/requirements-test.txt requirements/requirements.txt
66
#
7-
argcomplete==3.6.2
8-
# via
9-
# -c /home/preocts/python-src-template/requirements/constraints.txt
10-
# nox
11-
attrs==25.3.0
12-
# via
13-
# -c /home/preocts/python-src-template/requirements/constraints.txt
14-
# nox
7+
black==25.1.0
158
certifi==2025.4.26
16-
# via
17-
# -c /home/preocts/python-src-template/requirements/constraints.txt
18-
# requests
19-
cfgv==3.4.0
20-
# via
21-
# -c /home/preocts/python-src-template/requirements/constraints.txt
22-
# pre-commit
239
charset-normalizer==3.4.2
24-
# via
25-
# -c /home/preocts/python-src-template/requirements/constraints.txt
26-
# requests
27-
colorlog==6.9.0
28-
# via
29-
# -c /home/preocts/python-src-template/requirements/constraints.txt
30-
# nox
10+
click==8.2.1
3111
coverage==7.8.2
32-
# via
33-
# -c /home/preocts/python-src-template/requirements/constraints.txt
34-
# -r requirements/requirements-test.txt
35-
dependency-groups==1.3.1
36-
# via
37-
# -c /home/preocts/python-src-template/requirements/constraints.txt
38-
# nox
39-
distlib==0.3.9
40-
# via
41-
# -c /home/preocts/python-src-template/requirements/constraints.txt
42-
# virtualenv
43-
filelock==3.18.0
44-
# via
45-
# -c /home/preocts/python-src-template/requirements/constraints.txt
46-
# virtualenv
47-
identify==2.6.12
48-
# via
49-
# -c /home/preocts/python-src-template/requirements/constraints.txt
50-
# pre-commit
12+
flake8==7.2.0
13+
flake8-builtins==2.5.0
14+
flake8-pep585==0.1.7
5115
idna==3.10
52-
# via
53-
# -c /home/preocts/python-src-template/requirements/constraints.txt
54-
# requests
5516
iniconfig==2.1.0
56-
# via
57-
# -c /home/preocts/python-src-template/requirements/constraints.txt
58-
# pytest
17+
isort==6.0.1
18+
mccabe==0.7.0
5919
mypy==1.15.0
60-
# via
61-
# -c /home/preocts/python-src-template/requirements/constraints.txt
62-
# -r requirements/requirements-dev.txt
6320
mypy-extensions==1.1.0
64-
# via
65-
# -c /home/preocts/python-src-template/requirements/constraints.txt
66-
# mypy
67-
nodeenv==1.9.1
68-
# via
69-
# -c /home/preocts/python-src-template/requirements/constraints.txt
70-
# pre-commit
71-
nox==2025.5.1
72-
# via
73-
# -c /home/preocts/python-src-template/requirements/constraints.txt
74-
# -r requirements/requirements-test.txt
7521
packaging==25.0
76-
# via
77-
# -c /home/preocts/python-src-template/requirements/constraints.txt
78-
# dependency-groups
79-
# nox
80-
# pytest
22+
pathspec==0.12.1
8123
platformdirs==4.3.8
82-
# via
83-
# -c /home/preocts/python-src-template/requirements/constraints.txt
84-
# virtualenv
8524
pluggy==1.6.0
86-
# via
87-
# -c /home/preocts/python-src-template/requirements/constraints.txt
88-
# pytest
89-
pre-commit==4.2.0
90-
# via
91-
# -c /home/preocts/python-src-template/requirements/constraints.txt
92-
# -r requirements/requirements-dev.txt
25+
pycodestyle==2.13.0
26+
pyflakes==3.3.2
9327
pytest==8.3.5
94-
# via
95-
# -c /home/preocts/python-src-template/requirements/constraints.txt
96-
# -r requirements/requirements-test.txt
97-
# pytest-randomly
9828
pytest-randomly==3.16.0
99-
# via
100-
# -c /home/preocts/python-src-template/requirements/constraints.txt
101-
# -r requirements/requirements-test.txt
102-
pyyaml==6.0.2
103-
# via
104-
# -c /home/preocts/python-src-template/requirements/constraints.txt
105-
# pre-commit
10629
requests==2.32.3
107-
# via
108-
# -c /home/preocts/python-src-template/requirements/constraints.txt
109-
# -r requirements/requirements.txt
11030
typing-extensions==4.13.2
111-
# via
112-
# -c /home/preocts/python-src-template/requirements/constraints.txt
113-
# mypy
11431
urllib3==2.4.0
115-
# via
116-
# -c /home/preocts/python-src-template/requirements/constraints.txt
117-
# requests
118-
virtualenv==20.31.2
119-
# via
120-
# -c /home/preocts/python-src-template/requirements/constraints.txt
121-
# nox
122-
# pre-commit

0 commit comments

Comments
 (0)