Skip to content

Commit 7dd6d70

Browse files
authored
Merge pull request #4 from koliver-kv/develop
Noxfile tooling and ruff implementation
2 parents c0eda70 + ca853ae commit 7dd6d70

21 files changed

+1549
-1315
lines changed

.github/workflows/docs.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ jobs:
88
- uses: actions/checkout@v3
99
- uses: actions/setup-python@v4.5.0
1010
with:
11-
python-version: "3.8"
11+
python-version: "3.9"
1212
- run: |
1313
pip install --constraint=.github/workflows/constraints.txt pip
1414
pip install --constraint=.github/workflows/constraints.txt nox
1515
- name: Build documentation
1616
run: nox --force-color --session=docs
17-
- uses: actions/upload-artifact@v3
17+
- uses: actions/upload-artifact@v4
1818
with:
1919
name: docs
2020
path: docs/_build

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
project = "Hypermodern Python Cookiecutter"
77
author = "Claudio Jolowicz"
8-
copyright = f"{datetime.now().year}, {author}"
8+
copyright = f"{datetime.now().year}, {author}" # noqa: A001
99
extensions = ["sphinx.ext.intersphinx", "myst_parser"]
1010
intersphinx_mapping = {"mypy": ("https://mypy.readthedocs.io/en/stable/", None)}
1111
language = "en"

docs/guide.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2666,10 +2666,10 @@ You can also read the articles on [this blog][hypermodern python blog].
26662666
[nox]: https://nox.thea.codes/
26672667
[package metadata]: https://packaging.python.org/en/latest/specifications/core-metadata/
26682668
[pep 257]: http://www.python.org/dev/peps/pep-0257/
2669-
[pep 440]: https://www.python.org/dev/peps/pep-0440/
2670-
[pep 517]: https://www.python.org/dev/peps/pep-0517/
2671-
[pep 518]: https://www.python.org/dev/peps/pep-0518/
2672-
[pep 561]: https://www.python.org/dev/peps/pep-0561/
2669+
[pep 440]: https://peps.python.org/pep-0440/
2670+
[pep 517]: https://peps.python.org/pep-0517/
2671+
[pep 518]: https://peps.python.org/pep-0518/
2672+
[pep 561]: https://peps.python.org/pep-0561/
26732673
[pep 8]: http://www.python.org/dev/peps/pep-0008/
26742674
[pep8-naming codes]: https://github.com/pycqa/pep8-naming#pep-8-naming-conventions
26752675
[pep8-naming]: https://github.com/pycqa/pep8-naming
@@ -2740,6 +2740,6 @@ You can also read the articles on [this blog][hypermodern python blog].
27402740
[versions and constraints]: https://python-poetry.org/docs/dependency-specification/
27412741
[virtual environment]: https://docs.python.org/3/tutorial/venv.html
27422742
[virtualenv]: https://virtualenv.pypa.io/
2743-
[wheel]: https://www.python.org/dev/peps/pep-0427/
2743+
[wheel]: https://peps.python.org/pep-0427/
27442744
[xdoctest]: https://github.com/Erotemic/xdoctest
27452745
[yaml]: https://yaml.org/

noxfile.py

Lines changed: 93 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Nox sessions."""
22

3+
from __future__ import annotations
4+
35
from pathlib import Path
46
import shutil
57

@@ -8,25 +10,97 @@
810
from nox.sessions import Session
911

1012

13+
python_versions = ["3.12", "3.11", "3.10", "3.9"]
14+
1115
nox.options.sessions = ["docs"]
12-
owner, repository = "cjolowicz", "cookiecutter-hypermodern-python"
16+
owner, repository = "56kyle", "cookiecutter-hypermodern-python"
1317
labels = "cookiecutter", "documentation"
1418
bump_paths = "README.md", "docs/guide.rst", "docs/index.rst", "docs/quickstart.md"
1519

16-
REPO_ROOT: Path = Path(__file__).parent
17-
NEXTGEN_STARTER_CACHE_FOLDER: Path = platformdirs.user_cache_path(
18-
appname="cookiecutter-hypermodern-python",
19-
appauthor="56kyle",
20-
ensure_exists=True
21-
)
20+
REPO_ROOT: Path = Path(__file__).parent.resolve()
21+
TEMPLATE_FOLDER: Path = REPO_ROOT / "{{cookiecutter.project_name}}"
22+
23+
24+
COOKIECUTTER_HYPERMODERN_PYTHON_CACHE_FOLDER: Path = Path(
25+
platformdirs.user_cache_path(
26+
appname="cookiecutter-hypermodern-python",
27+
appauthor="56kyle",
28+
ensure_exists=True,
29+
)
30+
).resolve()
2231

23-
PROJECT_DEMOS_FOLDER: Path = NEXTGEN_STARTER_CACHE_FOLDER / "project_demos"
32+
PROJECT_DEMOS_FOLDER: Path = COOKIECUTTER_HYPERMODERN_PYTHON_CACHE_FOLDER / "project_demos"
2433
DEFAULT_DEMO_NAME: str = "demo-project"
34+
DEMO_ROOT_FOLDER: Path = PROJECT_DEMOS_FOLDER / DEFAULT_DEMO_NAME
2535

36+
GENERATE_DEMO_PROJECT_OPTIONS: tuple[str, ...] = (
37+
*("--repo-folder", REPO_ROOT),
38+
*("--demos-cache-folder", PROJECT_DEMOS_FOLDER),
39+
*("--demo-name", DEFAULT_DEMO_NAME),
40+
)
41+
42+
SYNC_POETRY_WITH_DEMO_OPTIONS: tuple[str, ...] = (
43+
*("--template-folder", TEMPLATE_FOLDER),
44+
*("--demos-cache-folder", PROJECT_DEMOS_FOLDER),
45+
*("--demo-name", DEFAULT_DEMO_NAME),
46+
)
2647

27-
@nox.session(name="generate-demo-project")
28-
def generate_demo_project(session: Session) -> Session:
29-
pass
48+
49+
@nox.session(name="generate-demo-project", python=python_versions[-1])
50+
def generate_demo_project(session: Session) -> None:
51+
session.install("cookiecutter", "platformdirs", "loguru")
52+
session.run(
53+
"python",
54+
"tools/generate-demo-project.py",
55+
*GENERATE_DEMO_PROJECT_OPTIONS,
56+
external=True,
57+
)
58+
59+
60+
@nox.session(name="sync-poetry-with-demo", python=python_versions[-1])
61+
def sync_poetry_with_demo(session: Session) -> None:
62+
session.install("cookiecutter", "platformdirs", "loguru")
63+
session.run(
64+
"python",
65+
"tools/sync-poetry-with-demo.py",
66+
*SYNC_POETRY_WITH_DEMO_OPTIONS,
67+
external=True,
68+
)
69+
70+
71+
@nox.session(name="poetry-in-demo", python=python_versions[-1])
72+
def poetry_in_demo(session: Session) -> None:
73+
session.install("cookiecutter", "platformdirs", "loguru")
74+
session.run(
75+
"python",
76+
"tools/generate-demo-project.py",
77+
*GENERATE_DEMO_PROJECT_OPTIONS,
78+
external=True,
79+
)
80+
original_dir: Path = Path.cwd()
81+
session.cd(DEMO_ROOT_FOLDER)
82+
session.run("poetry", *session.posargs)
83+
session.cd(original_dir)
84+
session.run(
85+
"python",
86+
"tools/sync-poetry-with-demo.py",
87+
*SYNC_POETRY_WITH_DEMO_OPTIONS,
88+
external=True,
89+
)
90+
91+
92+
@nox.session(name="poetry-lock")
93+
def poetry_lock(session: Session) -> None:
94+
"""Shorthand for poetry-in-demo -- lock."""
95+
session._runner.posargs = ["lock", *session.posargs]
96+
poetry_in_demo(session)
97+
98+
99+
@nox.session(name="poetry-update")
100+
def poetry_update(session: Session) -> None:
101+
"""Shorthand for poetry-in-demo -- update."""
102+
session._runner.posargs = ["update", *session.posargs]
103+
poetry_in_demo(session)
30104

31105

32106
@nox.session(name="prepare-release")
@@ -77,7 +151,14 @@ def docs(session: Session) -> None:
77151
@nox.session
78152
def linkcheck(session: Session) -> None:
79153
"""Build the documentation."""
80-
args = session.posargs or ["-b", "linkcheck", "-W", "--keep-going", "docs", "docs/_build"]
154+
args = session.posargs or [
155+
"-b",
156+
"linkcheck",
157+
"-W",
158+
"--keep-going",
159+
"docs",
160+
"docs/_build",
161+
]
81162

82163
builddir = Path("docs", "_build")
83164
if builddir.exists():

tools/dependencies-table.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,11 @@ def main() -> None:
5252
if package["name"] in dependencies
5353
}
5454

55-
table = {
56-
format_dependency(dependency): descriptions[dependency]
57-
for dependency in sorted(dependencies)
58-
}
55+
table = {format_dependency(dependency): descriptions[dependency] for dependency in sorted(dependencies)}
5956

6057
width = max(len(name) for name in table)
6158
width2 = max(len(description) for description in table.values())
62-
separator = LINE_FORMAT.format(
63-
name="=" * width, width=width, description="=" * width2
64-
)
59+
separator = LINE_FORMAT.format(name="=" * width, width=width, description="=" * width2)
6560

6661
print(separator)
6762

tools/generate-demo-project.py

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,29 @@
1-
import shutil
2-
import sys
3-
from pathlib import Path
1+
"""Python script for generating a demo project."""
42

53
import shutil
64
import sys
5+
from pathlib import Path
76

87
import click
98

109
from cookiecutter.main import cookiecutter
11-
from loguru import logger
1210

13-
from tools.util import DEFAULT_DEMO_NAME
14-
from tools.util import PROJECT_DEMOS_FOLDER
15-
from tools.util import REPO_ROOT
11+
12+
FOLDER_TYPE: click.Path = click.Path(dir_okay=True, file_okay=False, resolve_path=True, path_type=Path)
1613

1714

18-
def generate_demo_project() -> Path:
19-
"""Generates a demo project and returns its Path."""
20-
PROJECT_DEMOS_FOLDER.mkdir(exist_ok=True)
21-
_remove_any_existing_demo(PROJECT_DEMOS_FOLDER)
15+
def generate_demo_project(repo_folder: Path, demos_cache_folder: Path, demo_name: str) -> Path:
16+
"""Generates a demo project and returns its root path."""
17+
demos_cache_folder.mkdir(exist_ok=True)
18+
_remove_any_existing_demo(demos_cache_folder)
2219
cookiecutter(
23-
template=str(REPO_ROOT),
20+
template=str(repo_folder),
2421
no_input=True,
25-
extra_context={
26-
"project_name": DEFAULT_DEMO_NAME,
27-
},
22+
extra_context={"project_name": demo_name},
2823
overwrite_if_exists=True,
29-
output_dir=str(PROJECT_DEMOS_FOLDER)
24+
output_dir=str(demos_cache_folder),
3025
)
31-
return PROJECT_DEMOS_FOLDER / DEFAULT_DEMO_NAME
26+
return demos_cache_folder / demo_name
3227

3328

3429
def _remove_any_existing_demo(parent_path: Path) -> None:
@@ -38,14 +33,17 @@ def _remove_any_existing_demo(parent_path: Path) -> None:
3833

3934

4035
@click.command()
41-
def main() -> None:
42-
"""Geneates a demo project."""
36+
@click.option("--repo-folder", "-r", required=True, type=FOLDER_TYPE)
37+
@click.option("--demos-cache-folder", "-c", required=True, type=FOLDER_TYPE)
38+
@click.option("--demo-name", "-d", required=True, type=str)
39+
def main(repo_folder: Path, demos_cache_folder: Path, demo_name: str) -> None:
40+
"""Updates the poetry.lock file."""
4341
try:
44-
generate_demo_project()
42+
generate_demo_project(repo_folder=repo_folder, demos_cache_folder=demos_cache_folder, demo_name=demo_name)
4543
except Exception as error:
4644
click.secho(f"error: {error}", fg="red")
4745
sys.exit(1)
4846

4947

50-
if __name__ == '__main__':
48+
if __name__ == "__main__":
5149
main(prog_name="generate-demo-project")

tools/prepare-github-release.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@
1313

1414
def git(*args: str, **kwargs: Any) -> str:
1515
try:
16-
process = subprocess.run(
17-
["git", *args], check=True, capture_output=True, text=True
18-
)
16+
process = subprocess.run(["git", *args], check=True, capture_output=True, text=True)
1917
return process.stdout
2018
except subprocess.CalledProcessError as error:
2119
print(error.stdout, end="")

tools/publish-github-release.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,14 @@ def publish_release(*, owner: str, repository_name: str, token: str, tag: str) -
1313
try:
1414
[pull_request] = list(repository.pull_requests(head=f"{owner}:release-{tag}"))
1515
except ValueError:
16-
raise RuntimeError(
17-
f"there should be exactly one pull request for {owner}:release-{tag}"
18-
)
16+
raise RuntimeError(f"there should be exactly one pull request for {owner}:release-{tag}")
1917

2018
pull_request = repository.pull_request(pull_request.number)
2119

2220
try:
2321
[*_, commit] = pull_request.commits()
2422
except ValueError:
25-
raise RuntimeError(
26-
f"there should be at least one commit associated with #{pull_request.number}"
27-
)
23+
raise RuntimeError(f"there should be at least one commit associated with #{pull_request.number}")
2824

2925
try:
3026
[release] = [release for release in repository.releases() if release.draft]

tools/sync-poetry-lock-to-demo.py

Lines changed: 0 additions & 43 deletions
This file was deleted.

0 commit comments

Comments
 (0)