-
Notifications
You must be signed in to change notification settings - Fork 88
/
Copy pathnoxfile.py
154 lines (119 loc) · 4.35 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
"""Configure nox as the task runner.
Nox provides the following tasks:
- "init-project": install the pre-commit hooks
- "doctests": run the xdoctests in the source files
- "fix-branch-references": adjusts links with git branch references in
various files (e.g., Mardown or notebooks)
"""
import contextlib
import glob
import os
import re
import shutil
import subprocess
import tempfile
import nox
REPOSITORY = "webartifex/intro-to-python"
SRC_LOCATIONS = (
"02_functions/sample_module.py",
"11_classes/sample_package",
)
# Use a unified .cache/ folder for all develop tools.
nox.options.envdir = ".cache/nox"
# All tools except git and poetry are project dependencies.
# Avoid accidental successes if the environment is not set up properly.
nox.options.error_on_external_run = True
@nox.session(name="init-project", venv_backend="none")
def init_project(session):
"""Install the pre-commit hooks."""
for type_ in (
"pre-commit",
"pre-merge-commit",
):
session.run("poetry", "run", "pre-commit", "install", f"--hook-type={type_}")
@nox.session(venv_backend="none")
def doctests(session):
"""Run the xdoctests in the source files."""
for location in SRC_LOCATIONS:
session.run("poetry", "run", "xdoctest", "--silent", location)
@nox.session(name="fix-branch-references", venv_backend="none")
def fix_branch_references(_session):
"""Change git branch references.
Intended to be run as a pre-commit hook.
Many files in the project (e.g., README.md) contain links to resources on
github.com, nbviewer.jupyter.org, or mybinder.org that contain git branch
labels.
This task rewrites branch labels into either "main" or "develop".
"""
# Glob patterns that expand into the files whose links are re-written.
paths = ["*.md", "**/*.ipynb"]
branch = (
subprocess.check_output(
("git", "rev-parse", "--abbrev-ref", "HEAD"),
)
.decode()
.strip()
)
# If the current branch is only temporary and will be merged into "main", ...
if branch.startswith("release-") or branch.startswith("hotfix-"):
branch = "main"
# If the branch is not "main", we assume it is a feature branch.
elif branch != "main":
branch = "develop"
rewrites = [
{
"name": "github",
"pattern": re.compile(
rf"((((http)|(https))://github\.com/{REPOSITORY}/((blob)|(tree))/)([\w-]+)/)"
),
"replacement": rf"\2{branch}/",
},
{
"name": "nbviewer",
"pattern": re.compile(
rf"((((http)|(https))://nbviewer\.jupyter\.org/github/{REPOSITORY}/((blob)|(tree))/)([\w-]+)/)",
),
"replacement": rf"\2{branch}/",
},
{
"name": "mybinder",
"pattern": re.compile(
rf"((((http)|(https))://mybinder\.org/v2/gh/{REPOSITORY}/)([\w-]+)\?)",
),
"replacement": rf"\2{branch}?",
},
]
for expanded in _expand(*paths):
with _line_by_line_replace(expanded) as (old_file, new_file):
for line in old_file:
for rewrite in rewrites:
line = re.sub(rewrite["pattern"], rewrite["replacement"], line)
new_file.write(line)
def _expand(*patterns):
"""Expand glob patterns into paths.
Args:
*patterns: the patterns to be expanded
Yields:
path: a single expanded path
"""
for pattern in patterns:
yield from glob.glob(pattern.strip())
@contextlib.contextmanager
def _line_by_line_replace(path):
"""Replace/change the lines in a file one by one.
This generator function yields two file handles, one to the current file
(i.e., `old_file`) and one to its replacement (i.e., `new_file`).
Usage: loop over the lines in `old_file` and write the files to be kept
to `new_file`. Files not written to `new_file` are removed!
Args:
path: the file whose lines are to be replaced
Yields:
old_file, new_file: handles to a file and its replacement
"""
file_handle, new_file_path = tempfile.mkstemp()
with os.fdopen(file_handle, "w") as new_file:
with open(path) as old_file:
yield old_file, new_file
shutil.copymode(path, new_file_path)
os.remove(path)
shutil.move(new_file_path, path)