Skip to content
This repository was archived by the owner on Apr 13, 2023. It is now read-only.

Commit 1ade09f

Browse files
author
Thiago C. D'Ávila
authored
Merge pull request #102 from staticdev/nox-docs
Nox refactor
2 parents a934be7 + 290df33 commit 1ade09f

File tree

2 files changed

+85
-65
lines changed

2 files changed

+85
-65
lines changed

.github/workflows/tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
- { python-version: 3.8, os: windows-latest, session: "tests" }
2424
- { python-version: 3.8, os: macos-latest, session: "tests" }
2525
# - { python-version: 3.8, os: ubuntu-latest, session: "typeguard" }
26-
- { python-version: 3.8, os: ubuntu-latest, session: "docs" }
26+
- { python-version: 3.8, os: ubuntu-latest, session: "docs-build" }
2727

2828
env:
2929
NOXSESSION: ${{ matrix.session }}
@@ -81,7 +81,7 @@ jobs:
8181
nox --force-color --python=${{ matrix.python-version }}
8282
8383
- name: Upload documentation
84-
if: matrix.session == 'docs'
84+
if: matrix.session == 'docs-build'
8585
uses: actions/upload-artifact@v2
8686
with:
8787
name: docs

noxfile.py

Lines changed: 83 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
"""Nox sessions."""
2-
import contextlib
2+
import hashlib
33
import shutil
44
import sys
5-
import tempfile
65
from pathlib import Path
76
from textwrap import dedent
8-
from typing import cast
9-
from typing import Iterator
107

118
import nox
129
from nox.sessions import Session
@@ -15,7 +12,14 @@
1512
package = "git_portfolio"
1613
# python_versions = ["3.9", "3.8", "3.7"]
1714
python_versions = ["3.8", "3.7"]
18-
nox.options.sessions = "pre-commit", "safety", "mypy", "tests", "typeguard"
15+
nox.options.sessions = (
16+
"pre-commit",
17+
"safety",
18+
"mypy",
19+
"tests",
20+
"typeguard",
21+
"docs-build",
22+
)
1923

2024

2125
class Poetry:
@@ -29,46 +33,62 @@ def __init__(self, session: Session) -> None:
2933
"""Constructor."""
3034
self.session = session
3135

32-
@contextlib.contextmanager
33-
def export(self, *args: str) -> Iterator[Path]:
36+
def export(self, path: Path, *, dev: bool) -> None:
3437
"""Export the lock file to requirements format.
3538
3639
Args:
37-
args: Command-line arguments for ``poetry export``.
38-
39-
Yields:
40-
The path to the requirements file.
41-
"""
42-
with tempfile.TemporaryDirectory() as directory:
43-
requirements = Path(directory) / "requirements.txt"
44-
self.session.run(
45-
"poetry",
46-
"export",
47-
*args,
48-
"--format=requirements.txt",
49-
f"--output={requirements}",
50-
external=True,
51-
)
52-
yield requirements
53-
54-
def version(self) -> str:
55-
"""Retrieve the package version.
56-
57-
Returns:
58-
The package version.
40+
path: The destination path.
41+
dev: If True, include development dependencies.
5942
"""
60-
output = self.session.run(
61-
"poetry", "version", external=True, silent=True, stderr=None
43+
options = ["--dev"] if dev else []
44+
self.session.run(
45+
"poetry",
46+
"export",
47+
"--format=requirements.txt",
48+
f"--output={path}",
49+
*options,
50+
external=True,
6251
)
63-
return cast(str, output).split()[1]
6452

65-
def build(self, *args: str) -> None:
53+
def build(self, *args: str) -> str:
6654
"""Build the package.
6755
6856
Args:
6957
args: Command-line arguments for ``poetry build``.
58+
59+
Returns:
60+
The basename of the wheel built by Poetry.
7061
"""
71-
self.session.run("poetry", "build", *args, external=True)
62+
output = self.session.run(
63+
"poetry", "build", *args, external=True, silent=True, stderr=None
64+
)
65+
assert isinstance(output, str) # noqa: S101
66+
return output.split()[-1]
67+
68+
69+
def export_requirements(session: Session, *, dev: bool) -> Path:
70+
"""Export the lock file to requirements format.
71+
72+
Args:
73+
session: The Session object.
74+
dev: If True, include development dependencies.
75+
76+
Returns:
77+
The path to the requirements file.
78+
"""
79+
tmpdir = Path(session.create_tmp())
80+
name = "dev-requirements.txt" if dev else "requirements.txt"
81+
path = tmpdir / name
82+
hashfile = tmpdir / f"{name}.hash"
83+
84+
lockdata = Path("poetry.lock").read_bytes()
85+
digest = hashlib.blake2b(lockdata).hexdigest()
86+
87+
if not hashfile.is_file() or hashfile.read_text() != digest:
88+
Poetry(session).export(path, dev=dev)
89+
hashfile.write_text(digest)
90+
91+
return path
7292

7393

7494
def install_package(session: Session) -> None:
@@ -84,16 +104,11 @@ def install_package(session: Session) -> None:
84104
session: The Session object.
85105
"""
86106
poetry = Poetry(session)
107+
wheel = poetry.build("--format=wheel")
108+
requirements = export_requirements(session, dev=False)
87109

88-
with poetry.export() as requirements:
89-
session.install(f"--requirement={requirements}")
90-
91-
poetry.build("--format=wheel")
92-
93-
version = poetry.version()
94-
session.install(
95-
"--no-deps", "--force-reinstall", f"dist/{package}-{version}-py3-none-any.whl"
96-
)
110+
session.install(f"--requirement={requirements}")
111+
session.install("--no-deps", "--force-reinstall", f"dist/{wheel}")
97112

98113

99114
def install(session: Session, *args: str) -> None:
@@ -107,9 +122,8 @@ def install(session: Session, *args: str) -> None:
107122
session: The Session object.
108123
args: Command-line arguments for ``pip install``.
109124
"""
110-
poetry = Poetry(session)
111-
with poetry.export("--dev") as requirements:
112-
session.install(f"--constraint={requirements}", *args)
125+
requirements = export_requirements(session, dev=True)
126+
session.install(f"--constraint={requirements}", *args)
113127

114128

115129
def activate_virtualenv_in_precommit_hooks(session: Session) -> None:
@@ -189,10 +203,9 @@ def precommit(session: Session) -> None:
189203
@nox.session(python="3.8")
190204
def safety(session: Session) -> None:
191205
"""Scan dependencies for insecure packages."""
192-
poetry = Poetry(session)
193-
with poetry.export("--dev", "--without-hashes") as requirements:
194-
install(session, "safety")
195-
session.run("safety", "check", f"--file={requirements}", "--bare")
206+
install(session, "safety")
207+
requirements = export_requirements(session, dev=True)
208+
session.run("safety", "check", f"--file={requirements}", "--bare")
196209

197210

198211
@nox.session(python=python_versions)
@@ -210,7 +223,7 @@ def mypy(session: Session) -> None:
210223
def tests(session: Session) -> None:
211224
"""Run the test suite."""
212225
install_package(session)
213-
install(session, "coverage[toml]", "pytest", "pytest_mock")
226+
install(session, "coverage[toml]", "pygments", "pytest", "pytest_mock")
214227
try:
215228
session.run("coverage", "run", "--parallel", "-m", "pytest", *session.posargs)
216229
finally:
@@ -236,7 +249,7 @@ def coverage(session: Session) -> None:
236249
def typeguard(session: Session) -> None:
237250
"""Runtime type checking using Typeguard."""
238251
install_package(session)
239-
install(session, "pytest", "typeguard", "pytest_mock")
252+
install(session, "pygments", "pytest", "pytest_mock", "typeguard")
240253
session.run("pytest", f"--typeguard-packages={package}", *session.posargs)
241254

242255

@@ -249,22 +262,29 @@ def xdoctest(session: Session) -> None:
249262
session.run("python", "-m", "xdoctest", package, *args)
250263

251264

252-
@nox.session(python="3.8")
253-
def docs(session: Session) -> None:
265+
@nox.session(name="docs-build", python="3.8")
266+
def docs_build(session: Session) -> None:
254267
"""Build the documentation."""
255268
args = session.posargs or ["docs", "docs/_build"]
269+
install_package(session)
270+
install(session, "sphinx")
271+
272+
build_dir = Path("docs", "_build")
273+
if build_dir.exists():
274+
shutil.rmtree(build_dir)
256275

257-
if session.interactive and not session.posargs:
258-
args.insert(0, "--open-browser")
276+
session.run("sphinx-build", *args)
259277

260-
builddir = Path("docs", "_build")
261-
if builddir.exists():
262-
shutil.rmtree(builddir)
263278

279+
@nox.session(python="3.8")
280+
def docs(session: Session) -> None:
281+
"""Build and serve the documentation with live reloading on file changes."""
282+
args = session.posargs or ["--open-browser", "docs", "docs/_build"]
264283
install_package(session)
265284
install(session, "sphinx", "sphinx-autobuild")
266285

267-
if session.interactive:
268-
session.run("sphinx-autobuild", *args)
269-
else:
270-
session.run("sphinx-build", *args)
286+
build_dir = Path("docs", "_build")
287+
if build_dir.exists():
288+
shutil.rmtree(build_dir)
289+
290+
session.run("sphinx-autobuild", *args)

0 commit comments

Comments
 (0)