|
1 | 1 | """Nox sessions."""
|
| 2 | +import contextlib |
| 3 | +import shutil |
2 | 4 | import tempfile
|
3 |
| -from typing import Any |
| 5 | +from pathlib import Path |
| 6 | +from typing import cast |
| 7 | +from typing import Iterator |
4 | 8 |
|
5 | 9 | import nox
|
6 | 10 | from nox.sessions import Session
|
7 | 11 |
|
| 12 | +package = "django_sorting_bootstrap" |
| 13 | +python_versions = ["3.8", "3.7"] |
8 | 14 | nox.options.sessions = "lint", "safety", "mypy"
|
9 | 15 | locations = "src", "noxfile.py"
|
10 | 16 |
|
11 | 17 |
|
12 |
| -def install_with_constraints(session: Session, *args: str, **kwargs: Any) -> None: |
13 |
| - """Install packages constrained by Poetry's lock file. |
14 |
| - This function is a wrapper for nox.sessions.Session.install. It |
15 |
| - invokes pip to install packages inside of the session's virtualenv. |
16 |
| - Additionally, pip is passed a constraints file generated from |
17 |
| - Poetry's lock file, to ensure that the packages are pinned to the |
18 |
| - versions specified in poetry.lock. This allows you to manage the |
19 |
| - packages as Poetry development dependencies. |
20 |
| - Arguments: |
| 18 | +class Poetry: |
| 19 | + """Helper class for invoking Poetry inside a Nox session. |
| 20 | +
|
| 21 | + Attributes: |
21 | 22 | session: The Session object.
|
22 |
| - args: Command-line arguments for pip. |
23 |
| - kwargs: Additional keyword arguments for Session.install. |
24 | 23 | """
|
25 |
| - with tempfile.NamedTemporaryFile() as requirements: |
26 |
| - session.run( |
27 |
| - "poetry", |
28 |
| - "export", |
29 |
| - "--dev", |
30 |
| - "--format=requirements.txt", |
31 |
| - f"--output={requirements.name}", |
32 |
| - external=True, |
| 24 | + |
| 25 | + def __init__(self, session: Session) -> None: |
| 26 | + """Constructor.""" |
| 27 | + self.session = session |
| 28 | + |
| 29 | + @contextlib.contextmanager |
| 30 | + def export(self, *args: str) -> Iterator[Path]: |
| 31 | + """Export the lock file to requirements format. |
| 32 | +
|
| 33 | + Args: |
| 34 | + args: Command-line arguments for ``poetry export``. |
| 35 | +
|
| 36 | + Yields: |
| 37 | + The path to the requirements file. |
| 38 | + """ |
| 39 | + with tempfile.TemporaryDirectory() as directory: |
| 40 | + requirements = Path(directory) / "requirements.txt" |
| 41 | + self.session.run( |
| 42 | + "poetry", |
| 43 | + "export", |
| 44 | + *args, |
| 45 | + "--format=requirements.txt", |
| 46 | + f"--output={requirements}", |
| 47 | + external=True, |
| 48 | + ) |
| 49 | + yield requirements |
| 50 | + |
| 51 | + def version(self) -> str: |
| 52 | + """Retrieve the package version. |
| 53 | +
|
| 54 | + Returns: |
| 55 | + The package version. |
| 56 | + """ |
| 57 | + output = self.session.run( |
| 58 | + "poetry", "version", external=True, silent=True, stderr=None |
33 | 59 | )
|
34 |
| - session.install(f"--constraint={requirements.name}", *args, **kwargs) |
| 60 | + return cast(str, output).split()[1] |
35 | 61 |
|
| 62 | + def build(self, *args: str) -> None: |
| 63 | + """Build the package. |
36 | 64 |
|
37 |
| -@nox.session(python="3.8") |
38 |
| -def black(session: Session) -> None: |
39 |
| - """Run black code formatter.""" |
40 |
| - args = session.posargs or locations |
41 |
| - install_with_constraints(session, "black") |
42 |
| - session.run("black", *args) |
| 65 | + Args: |
| 66 | + args: Command-line arguments for ``poetry build``. |
| 67 | + """ |
| 68 | + self.session.run("poetry", "build", *args, external=True) |
43 | 69 |
|
44 | 70 |
|
45 |
| -@nox.session(python=["3.8", "3.7"]) |
46 |
| -def lint(session: Session) -> None: |
47 |
| - """Lint using flake8.""" |
48 |
| - args = session.posargs or locations |
49 |
| - install_with_constraints( |
50 |
| - session, "flake8", "flake8-bandit", "flake8-black", "flake8-bugbear", |
| 71 | +def install_package(session: Session) -> None: |
| 72 | + """Build and install the package. |
| 73 | +
|
| 74 | + Build a wheel from the package, and install it into the virtual environment |
| 75 | + of the specified Nox session. |
| 76 | +
|
| 77 | + The package requirements are installed using the versions specified in |
| 78 | + Poetry's lock file. |
| 79 | +
|
| 80 | + Args: |
| 81 | + session: The Session object. |
| 82 | + """ |
| 83 | + poetry = Poetry(session) |
| 84 | + |
| 85 | + with poetry.export() as requirements: |
| 86 | + session.install(f"--requirement={requirements}") |
| 87 | + |
| 88 | + poetry.build("--format=wheel") |
| 89 | + |
| 90 | + version = poetry.version() |
| 91 | + session.install( |
| 92 | + "--no-deps", "--force-reinstall", f"dist/{package}-{version}-py3-none-any.whl" |
51 | 93 | )
|
52 |
| - session.run("flake8", *args) |
| 94 | + |
| 95 | + |
| 96 | +def install(session: Session, *args: str) -> None: |
| 97 | + """Install development dependencies into the session's virtual environment. |
| 98 | +
|
| 99 | + This function is a wrapper for nox.sessions.Session.install. |
| 100 | +
|
| 101 | + The packages must be managed as development dependencies in Poetry. |
| 102 | +
|
| 103 | + Args: |
| 104 | + session: The Session object. |
| 105 | + args: Command-line arguments for ``pip install``. |
| 106 | + """ |
| 107 | + poetry = Poetry(session) |
| 108 | + with poetry.export("--dev") as requirements: |
| 109 | + session.install(f"--constraint={requirements}", *args) |
| 110 | + |
| 111 | + |
| 112 | +@nox.session(name="pre-commit", python="3.8") |
| 113 | +def precommit(session: Session) -> None: |
| 114 | + """Lint using pre-commit.""" |
| 115 | + args = session.posargs or ["run", "--all-files", "--show-diff-on-failure"] |
| 116 | + install(session, "pre-commit") |
| 117 | + session.run("pre-commit", *args) |
53 | 118 |
|
54 | 119 |
|
55 | 120 | @nox.session(python="3.8")
|
56 | 121 | def safety(session: Session) -> None:
|
57 | 122 | """Scan dependencies for insecure packages."""
|
58 |
| - with tempfile.NamedTemporaryFile() as requirements: |
59 |
| - session.run( |
60 |
| - "poetry", |
61 |
| - "export", |
62 |
| - "--dev", |
63 |
| - "--format=requirements.txt", |
64 |
| - "--without-hashes", |
65 |
| - f"--output={requirements.name}", |
66 |
| - external=True, |
67 |
| - ) |
68 |
| - install_with_constraints(session, "safety") |
69 |
| - session.run("safety", "check", f"--file={requirements.name}", "--full-report") |
| 123 | + poetry = Poetry(session) |
| 124 | + with poetry.export("--dev", "--without-hashes") as requirements: |
| 125 | + install(session, "safety") |
| 126 | + session.run("safety", "check", f"--file={requirements}", "--bare") |
70 | 127 |
|
71 | 128 |
|
72 |
| -@nox.session(python=["3.8", "3.7"]) |
| 129 | +@nox.session(python=python_versions) |
73 | 130 | def mypy(session: Session) -> None:
|
74 | 131 | """Type-check using mypy."""
|
75 | 132 | args = session.posargs or locations
|
76 |
| - install_with_constraints(session, "mypy") |
| 133 | + install_package(session) |
| 134 | + install(session, "mypy") |
77 | 135 | session.run("mypy", *args)
|
0 commit comments