Skip to content

Commit

Permalink
✨ Add nox for project automation. Closes #6
Browse files Browse the repository at this point in the history
  • Loading branch information
sgraaf committed Oct 17, 2023
1 parent 6e5ef0c commit 8a78706
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 26 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/input-for-demo.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cookiecutter-python-package-demo


Demo of https://github.com/sgraaf/cookiecutter-python-package
Demo of https://github.com/sgraaf/cookiecutter-python-package.
Steven van de Graaf
steven@vandegraaf.xyz
sgraaf
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# cookiecutter-python-package

[![Supported Python Versions](https://img.shields.io/badge/python-3.8%20|%203.9%20|%203.10%20|%203.11%20|%203.12-blue)](https://github.com/sgraaf/cookiecutter-python-package)
[![Nox](https://img.shields.io/badge/%F0%9F%A6%8A-Nox-D85E00.svg)](https://github.com/wntrblm/nox)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v1.json)](https://github.com/charliermarsh/ruff)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)
Expand All @@ -22,6 +23,7 @@ cookiecutter gh:sgraaf/cookiecutter-python-package
- Code formatting with [Black](https://black.readthedocs.io/en/stable/) and [Prettier](https://prettier.io/)
- Static type-checking with [mypy](http://www.mypy-lang.org/)
- Testing with [pytest](https://docs.pytest.org/en/stable/index.html)
- Project automation with [Nox](https://nox.thea.codes/en/stable/)
- Continuous Integration with [GitHub Actions](https://github.com/features/actions)
- Documentation with [Sphinx](https://www.sphinx-doc.org/en/master/), [MyST](https://myst-parser.readthedocs.io/en/latest/), and [Read the Docs](https://readthedocs.org/) using the [Furo](https://pradyunsg.me/furo/) theme
- Automated release builds and uploads to [PyPI](https://pypi.org/)
Expand Down
64 changes: 40 additions & 24 deletions hooks/post_gen_project.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,39 @@
import os
import shutil
import subprocess
import sys


def possibly_install_pipx() -> None:
if shutil.which("pipx") is None:
# install pipx
subprocess.run(
[sys.executable, "-m", "pip", "install", "--user", "pipx"], check=False
)
# add pipx to PATH
subprocess.run([sys.executable, "-m", "pipx", "ensurepath"], check=False)


# perform git initialization
if "{{ cookiecutter.init_git }}" == "True":
# initialize Git repository
subprocess.run(["git", "init", "-b", "main"], check=False)

# possibly install pre-commit (and pipx)
if shutil.which("pre-commit") is None:
# possibly install pipx
possibly_install_pipx()

# install pre-commit
subprocess.run(
[sys.executable, "-m", "pipx", "install", "pre-commit"], check=False
)

# install and update pre-commit hooks
subprocess.run(["pre-commit", "install"], check=False)
subprocess.run(["pre-commit", "autoupdate"], check=False)
subprocess.run(["pre-commit", "install", "--install-hooks"], check=False)

# add and commit
subprocess.run(["git", "add", "."], check=False)
subprocess.run(
["git", "commit", "-m", "🍪 Initial commit from `cookiecutter-python-package`"],
Expand All @@ -13,26 +42,13 @@

# create venv and install dependencies
if "{{ cookiecutter.init_venv }}" == "True":
subprocess.run([sys.executable, "-m", "venv", ".venv"], check=False)
if os.name == "posix":
venv_executable = r".venv/bin/python"
else:
venv_executable = r".venv\Scripts\python"
subprocess.run(
[
venv_executable,
"-m",
"pip",
"install",
"--upgrade",
"pip",
"setuptools",
"wheel",
"flit",
],
check=False,
)
# install dependencies
subprocess.run([venv_executable, "-m", "flit", "install", "--symlink"], check=False)
# install pre-commit hooks
subprocess.run(["pre-commit", "install"], check=False)
# possibly install nox (and pipx)
if shutil.which("nox") is None:
# possibly install pipx
possibly_install_pipx()

# install nox
subprocess.run([sys.executable, "-m", "pipx", "install", "nox"], check=False)

# run nox "dev" session
subprocess.run(["nox", "--session", "dev"], check=False)
Empty file.
4 changes: 3 additions & 1 deletion {{cookiecutter.project_name}}/docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"myst_parser",
"sphinx.ext.autodoc",
"sphinx.ext.autodoc.typehints",
"sphinx.ext.doctest",
"sphinx.ext.intersphinx",
"sphinx.ext.napoleon",
"sphinx_copybutton",
Expand All @@ -29,8 +30,9 @@
templates_path = ["_templates"]
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]

# auto-generate header anchors
# auto-generate header anchors and suppress header warnings
myst_heading_anchors = 3
suppress_warnings = ["myst.header"]

intersphinx_mapping = {
"python": ("https://docs.python.org/3", None),
Expand Down
127 changes: 127 additions & 0 deletions {{cookiecutter.project_name}}/noxfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import os
from pathlib import Path

import nox

nox.options.sessions = ["pre_commit", "tests", "docs"]
nox.options.reuse_existing_virtualenvs = True
nox.options.error_on_external_run = True

ALL_PYTHON_VERSIONS = [
# [[[cog
# import re
# for line in open("pyproject.toml"):
# if (match := re.search(r"Programming Language :: Python :: (?P<version>3\.\d+)", line)) is not None:
# cog.outl(f'"{match.group("version")}",')
# ]]]
"3.8",
"3.9",
"3.10",
"3.11",
"3.12",
# [[[end]]]
]

# [[[cog
# import yaml
# rtd = yaml.safe_load(open(".readthedocs.yaml"))
# cog.outl(f'DOCS_PYTHON_VERSION = "{rtd["build"]["tools"]["python"]}"')
# ]]]
DOCS_PYTHON_VERSION = "3.11"
# [[[end]]]

VENV_DIR = Path(".venv").resolve()


@nox.session
def dev(session: nox.Session) -> None:
"""Sets up a Python development environment for the project."""
# install `virtualenv` CLI tool into nox's "dev" virtual environment (venv)
session.install("virtualenv")

# initialize development environment (venv)
session.run(
"virtualenv",
"--prompt",
"{{cookiecutter.project_name}}",
str(VENV_DIR),
silent=True,
)

# determine venv executable
if os.name == "posix":
venv_executable = VENV_DIR / "bin" / "python"
else:
venv_executable = VENV_DIR / "Scripts" / "python.exe"

# upgrade pip, setuptools and wheel
session.run(
str(venv_executable),
"-m",
"pip",
"install",
"--upgrade",
"pip",
"setuptools",
"wheel",
external=True,
silent=True,
)

# install the current package in editable mode (along with its dependencies)
session.run(
str(venv_executable),
"-m",
"pip",
"install",
"--editable",
".[dev]",
external=True,
silent=True,
)


@nox.session
def cog(session: nox.Session) -> None:
"""Run cog."""
session.install("cogapp", "PyYAML")
session.run("cog", *session.posargs, "-r", "noxfile.py")


@nox.session
def pre_commit(session: nox.Session) -> None:
"""Run pre-commit hooks."""
session.install("pre-commit")
session.run("pre-commit", "run", "--all-files")


@nox.session(python=ALL_PYTHON_VERSIONS, tags=["tests"])
def tests(session: nox.Session) -> None:
"""Run tests."""
session.install(".[tests]")
session.run("pytest", *session.posargs)


@nox.session(python=DOCS_PYTHON_VERSION)
def docs(session: nox.Session) -> None:
"""Builds and tests the docs."""
session.install(".[docs]")

# create a temporary directory to store the doctrees
tmp_dir = session.create_tmp()

for builder in ["html", "doctest"]:
# fmt: off
session.run(
"python", "-m", "sphinx",
"-T", # display full traceback when an exception occurs
"-E", # rebuild the environment
"-W", "--keep-going", # turn warnings into errors, but continue to the end of the build
"-b", builder, # selects a builder
"-d", str(Path(tmp_dir) / "doctrees"), # directory to save doctree pickles
"-D", "language=en", # set language
"docs", # source directory
str(Path("docs") / "_build" / "html"), # output directory
)
# fmt: on
session.run("python", "-m", "doctest", "README.md")
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
def add_one(x: int) -> int:
"""Add one to the input.
Example:
>>> from {{ cookiecutter.package_name }} import add_one
>>> add_one(1)
2
Args:
x: The input.
Expand Down

1 comment on commit 8a78706

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.