-
Notifications
You must be signed in to change notification settings - Fork 16
/
noxfile.py
175 lines (135 loc) · 5.65 KB
/
noxfile.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
from concurrent.futures import ThreadPoolExecutor
from functools import wraps
import os
from pathlib import Path
import shutil
import sys
import nox
# Environment defaults: we want strict behavior in CI, and good local defaults
# so `nox` works reasonably and fast.
if os.environ.get('GITHUB_ACTIONS', False):
nox.options.error_on_missing_interpreters = True
nox.options.error_on_external_run = True
else:
# local development defaults
nox.options.sessions = ['test', 'flake8']
nox.options.reuse_existing_virtualenvs = True
# The versions we test against, and the lowest version of Python we support.
# We should always update with pip-compile using the lowest version of Python
# so that we get all backport packages we'll need for that.
SUPPORTED_PYTHONS = ('3.9', '3.10', '3.11', '3.12')
MIN_SUPPORTED_PYTHON = '3.9'
# Helper session for integrating well with pip-sync.
def sync_session(*envs, install_marbles=False, **kwargs):
'''Nox session decorator that uses pip-sync to manage dependencies.'''
def decorator(f):
@nox.session(**kwargs)
@wraps(f)
def wrapper(session: nox.Session, *args, **kwargs):
session.install('pip-tools')
concrete_reqs = [f'requirements/{env}.txt' for env in envs]
if install_marbles and sys.platform == 'win32':
concrete_reqs.append('requirements/windows.txt')
session.run_always('pip-sync', *concrete_reqs)
if install_marbles:
session.install('-e', 'marbles/core', '-e', 'marbles/mixins',
'--no-deps')
return f(session, *args, **kwargs)
return wrapper
return decorator
# Most useful testing code below: tests, coverage, linting, docs.
@sync_session('base', python=SUPPORTED_PYTHONS, install_marbles=True)
def test(session: nox.Session):
'''Run tests, without coverage.'''
for subpackage in ('marbles/core', 'marbles/mixins'):
with session.chdir(subpackage):
session.run('python', '-m', 'unittest', 'discover', 'tests')
@sync_session('coverage', python=SUPPORTED_PYTHONS, install_marbles=True,
tags=['actions-test'])
def coverage(session: nox.Session):
'''Run tests, with coverage.'''
session.run('coverage', 'erase')
with session.chdir('marbles/core'):
session.run('coverage', 'run', '-m', 'unittest', 'discover', 'tests')
examples_dir = Path('marbles/core/example_packages')
for f in examples_dir.glob('**/*.coverage*'):
shutil.move(str(f), 'marbles/core')
with session.chdir('marbles/mixins'):
session.run('coverage', 'run', '-m', 'unittest', 'discover', 'tests')
session.run('coverage', 'combine', 'marbles/core',
'marbles/mixins/.coverage', '.')
session.run('coverage', 'report')
session.run('coverage', 'html')
session.run('coverage', 'xml')
@nox.session
def serve_coverage(session: nox.Session):
'''Serve coverage data.'''
with session.chdir('build/coverage/html'):
session.run('python', '-m', 'http.server', *session.posargs)
@sync_session('lint', tags=['actions-check'])
def flake8(session: nox.Session):
'''Lint with flake8.'''
session.run('flake8')
@sync_session('docs', install_marbles=True, tags=['actions-check'])
def docs(session: nox.Session):
'''Build docs with Sphinx.'''
session.run('sphinx-build', '-b', 'html', '-WEa',
'-d', 'build/sphinx/doctrees',
'docs', 'build/sphinx/html')
@nox.session
def serve_docs(session: nox.Session):
'''Serve built docs.'''
with session.chdir('build/sphinx/html'):
session.run('python', '-m', 'http.server', *session.posargs)
@sync_session('package', tags=['actions-check'])
def package(session: nox.Session):
'''Build source and wheel distributions.'''
for d in ('dist', 'marbles/core/dist', 'marbles/mixins/dist'):
shutil.rmtree(d, ignore_errors=True)
for d in ('.', 'marbles/core', 'marbles/mixins'):
session.run('python', '-m', 'build', d)
# Administrative sessions: uploading to PyPI, updating dependencies, increasing
# the version strings.
@nox.session
def upload(session: nox.Session):
'''Upload distributions to PyPI.'''
session.install('twine')
session.run(
'twine', 'upload',
'dist/*', 'marbles/core/dist/*', 'marbles/mixins/dist/*',
)
@nox.session(python=MIN_SUPPORTED_PYTHON)
def pip_compile(session: nox.Session):
'''Refresh concrete dependencies using pip-compile.
You can upgrade our pinned dependencies by running ``nox -s pip_compile --
-U``.
'''
session.install('pip-tools')
args = ['--generate-hashes']
for subpackage in ('marbles/core', 'marbles/mixins'):
with session.chdir(subpackage):
session.run('pip-compile', *args, *session.posargs)
reqs_dir = Path('requirements')
infiles = [
req for req in reqs_dir.glob('*.in')
if req.stem != 'windows' or sys.platform == 'win32'
]
with ThreadPoolExecutor() as executor:
executor.map(
lambda infile: session.run(
'pip-compile', *args, *session.posargs, str(infile)
),
infiles
)
# Remove after https://github.com/jazzband/pip-tools/pull/1650
for f in reqs_dir.glob('*.txt'):
content = f.read_text()
content = content.replace(f'{Path.cwd()}/', '')
f.write_text(content)
@nox.session
def bumpversion(session: nox.Session):
'''Update the version number: pass "major", "minor", or "patch".
Use with positional arguments, e.g. ``nox -s bumpversion -- minor``.
'''
session.install('bump2version')
session.run('bumpversion', *session.posargs)