Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ templates for Python packages?
guidelines, with a WebAssembly version integrated with the guide. All checks
cross-linked.
- Follows [PyPA][] best practices and regularly updated.
- Uses uv for high performance CI and task running.

Be sure you have read the [Scientific-Python Development Guide][] first, and
possibly used them on a project or two. This is _not_ a minimal example or
Expand Down Expand Up @@ -126,6 +127,7 @@ backports structure with a small typing example.

- GitHub Actions runs testing for the generation itself
- Uses nox so cookie development can be checked locally
- Uses uv for high performance CI
- GitHub actions deploy
- C++ backends include cibuildwheel for wheel builds
- Uses PyPI trusted publisher deployment
Expand All @@ -138,8 +140,9 @@ backports structure with a small typing example.
- Includes spell checking
- An pylint nox target can be used to run pylint, which integrated GHA
annotations
- A ReadTheDocs-ready Sphinx docs folder and `[docs]` extra
- A test folder and pytest `[test]` extra
- A ReadTheDocs-ready Sphinx docs folder and `docs` dependency-group
- A test folder and pytest `test` dependency-group
- A dev group for `uv run` integration
- A noxfile is included with a few common targets

#### For developers:
Expand Down
42 changes: 33 additions & 9 deletions docs/_includes/pyproject.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,27 +63,25 @@ Poetry and Setuptools).

### Extras

It is recommended to use extras instead of or in addition to making requirement
files. These extras a) correctly interact with install requires and other
built-in tools, b) are available directly when installing via PyPI, and c) are
allowed in `requirements.txt`, `install_requires`, `pyproject.toml`, and most
other places requirements are passed.
Sometimes you want to ship a package with optional dependencies. For example,
you might have extra requirements that are only needed for running a CLI, or for
plotting. Users must opt-in to get these dependencies by adding them to the
package or wheel name when installing, like `package[cli,mpl]`.

Here is an example of a simple extras:

```toml
[project.optional-dependencies]
test = [
"pytest >=6.0",
cli = [
"click",
]
mpl = [
"matplotlib >=2.0",
]
```

Self dependencies can be used by using the name of the package, such as
`dev = ["package[test,examples]"]`, but this requires Pip 21.2 or newer. We
recommend providing at least `test` and `docs`.
`all = ["package[cli,mpl]"]`, but this requires Pip 21.2 or newer.

### Command line

Expand All @@ -100,6 +98,32 @@ function, followed by a colon, then the function to call. If you use
`__main__.py` as the file, then `python -m` followed by the module will also
work to call the app (`__name__` will be `"__main__"` in that case).

### Development dependencies

It is recommended to use dependency-groups instead of making requirement files.
This allows you to specify dependencies that are only needed for development;
unlike extras, they are not available when installing via PyPI, but they are
available for local installation, and the `dev` group is even installed by
default when using `uv`.

Here is an example:

```toml
[dependency-groups]
test = [
"pytest >=6.0",
]
dev = [
{ include-group = "test" },
]
```

You can include one dependency group in another. Most tools allow you to install
groups using `--group`, like `pip` (25.1+), `uv pip`, and the high level `uv`
interface. You do not need to install the package, though usually you do (the
high level `uv` interface does). Nox, Tox, and cibuildwheel all support groups
too.

[metadata]: https://packaging.python.org/en/latest/specifications/core-metadata/
[trove classifiers]: https://pypi.org/classifiers/
[spdx]: https://spdx.org/licenses
18 changes: 9 additions & 9 deletions docs/pages/guides/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,17 +272,16 @@ with code_fence("yaml"):
version: 2

build:
os: ubuntu-22.04
os: ubuntu-24.04
tools:
python: "3.12"
python: "3.13"
commands:
- asdf plugin add uv
- asdf install uv latest
- asdf global uv latest
- uv venv
- uv pip install .[docs]
- .venv/bin/python -m sphinx -T -b html -d docs/_build/doctrees -D
language=en docs $READTHEDOCS_OUTPUT/html
- uv sync --group docs
- uv run python -m sphinx -T -b html -d docs/_build/doctrees -D language=en
docs $READTHEDOCS_OUTPUT/html
```
<!-- prettier-ignore-end -->
<!-- [[[end]]] -->
Expand Down Expand Up @@ -335,12 +334,13 @@ with code_fence("python"):
]]] -->
<!-- prettier-ignore-start -->
```python
@nox.session(reuse_venv=True)
@nox.session(reuse_venv=True, default=False)
def docs(session: nox.Session) -> None:
"""
Build the docs. Pass --non-interactive to avoid serving. First positional argument is the target directory.
"""

doc_deps = nox.project.dependency_groups(PROJECT, "docs")
parser = argparse.ArgumentParser()
parser.add_argument(
"-b", dest="builder", default="html", help="Build target (default: html)"
Expand All @@ -349,7 +349,7 @@ def docs(session: nox.Session) -> None:
args, posargs = parser.parse_known_args(session.posargs)
serve = args.builder == "html" and session.interactive

session.install("-e.[docs]", "sphinx-autobuild")
session.install("-e.", *doc_deps, "sphinx-autobuild")

shared_args = (
"-n", # nitpicky mode
Expand Down Expand Up @@ -396,7 +396,7 @@ with code_fence("python"):
]]] -->
<!-- prettier-ignore-start -->
```python
@nox.session
@nox.session(default=False)
def build_api_docs(session: nox.Session) -> None:
"""
Build (regenerate) API docs.
Expand Down
6 changes: 3 additions & 3 deletions docs/pages/guides/gha_basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,11 @@ tests:
python-version: ${{ matrix.python-version }}
allow-prereleases: true

- name: Install package
run: python -m pip install -e .[test]
- name: Download uv
uses: astral/setup-uv@v6

- name: Test package
run: python -m pytest
run: uv run pytest
```

{% endraw %}
Expand Down
20 changes: 13 additions & 7 deletions docs/pages/guides/gha_wheels.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,18 @@ Linux and Windows):

```toml
[tool.cibuildwheel]
test-extras = "test"
test-groups = ["test"]
test-command = "pytest {project}/tests"
build-frontend = "build[uv]"
# Optional
build-verbosity = 1
```

The `test-extras` will cause the pip install to use `[test]`. The `test-command`
will use pytest to run your tests. You can also set the build verbosity (`-v` in
pip) if you want to.
The build frontend is set to `build[uv]`, which is faster than the default build
backend; you just need uv installed, but that's easy to do. The `test-extras`
will cause the pip install to use the dependency-group(s) specified.. The
`test-command` will use pytest to run your tests. You can also set the build
verbosity (`-v` in pip) if you want to.

## Making an SDist

Expand Down Expand Up @@ -114,6 +117,8 @@ build_wheels:
fetch-depth: 0
submodules: true

- uses: astral-sh/setup-uv@v6

- uses: pypa/[email protected]

- name: Upload wheels
Expand Down Expand Up @@ -150,8 +155,8 @@ you want a different supported image, set `CIBW_MANYLINUX_X86_64_IMAGE`,
`CIBW_MANYLINUX_I686_IMAGE`, etc. If you always need a specific image, you can
set that in the `pyproject.toml` file instead.

You can speed up the build by specifying the `build[uv]` build-frontend option
and pre-installing `uv` on the runners.
You can skip specifying the `build[uv]` build-frontend option and pre-installing
`uv` on the runners, but it will be a slower.

## Publishing

Expand Down Expand Up @@ -237,7 +242,8 @@ the sdist, for example).

> Other architectures
>
> On Travis, `cibuildwheel` even has the ability to create ARM and PowerPC
> GitHub Actions supports ARM on Linux and Windows as well. On Travis,
> `cibuildwheel` even has the ability to create rarer architectures like PowerPC
> builds natively. IBM Z builds are also available but in beta. However, due to
> Travis CI's recent dramatic reduction on open source support, emulating these
> architectures on GHA or Azure is probably better. Maybe look into Cirrus CI,
Expand Down
23 changes: 9 additions & 14 deletions docs/pages/guides/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@ def tests(session: nox.Session) -> None:
"""
Run the unit and regular tests.
"""
session.install(".[test]")
pyproject = nox.project.load_toml()
deps = nox.project.dependency_groups(pyproject, "test")
session.install("-e.", *deps)
session.run("pytest", *session.posargs)
```

Expand All @@ -129,15 +131,6 @@ You can see all defined sessions (along with the docstrings) using:
$ nox -l
```

It is a good idea to list the sessions you want by default by setting
`nox.options.sessions` near the top of your file:

```python
nox.options.sessions = ["lint", "tests"]
```

This will keep you from running extra things like `docs` by default.

### Parametrizing

You can parametrize sessions. either on Python or on any other item.
Expand Down Expand Up @@ -212,7 +205,8 @@ def tests(session: nox.Session) -> None:
"""
Run the unit and regular tests.
"""
session.install("-e.[test]")
test_deps = nox.project.dependency_groups(PROJECT, "test")
session.install("-e.", *test_deps)
session.run("pytest", *session.posargs)
```
<!-- prettier-ignore-end -->
Expand All @@ -226,12 +220,13 @@ with code_fence("python"):
]]] -->
<!-- prettier-ignore-start -->
```python
@nox.session(reuse_venv=True)
@nox.session(reuse_venv=True, default=False)
def docs(session: nox.Session) -> None:
"""
Build the docs. Pass --non-interactive to avoid serving. First positional argument is the target directory.
"""

doc_deps = nox.project.dependency_groups(PROJECT, "docs")
parser = argparse.ArgumentParser()
parser.add_argument(
"-b", dest="builder", default="html", help="Build target (default: html)"
Expand All @@ -240,7 +235,7 @@ def docs(session: nox.Session) -> None:
args, posargs = parser.parse_known_args(session.posargs)
serve = args.builder == "html" and session.interactive

session.install("-e.[docs]", "sphinx-autobuild")
session.install("-e.", *doc_deps, "sphinx-autobuild")

shared_args = (
"-n", # nitpicky mode
Expand Down Expand Up @@ -287,7 +282,7 @@ from pathlib import Path
DIR = Path(__file__).parent.resolve()


@nox.session
@nox.session(default=False)
def build(session: nox.Session) -> None:
"""
Build an SDist and wheel.
Expand Down
2 changes: 1 addition & 1 deletion noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ def tests(session: nox.Session, backend: str, vcs: bool) -> None:
session.chdir(cookie)

name = f"cookie-{backend}"
session.install(".[test]")
session.install(".", "--group=test")
session.run("python", "-m", "pytest", "-ra")
version = session.run(
"python",
Expand Down
38 changes: 8 additions & 30 deletions {{cookiecutter.project_name}}/.github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ description of best practices for developing scientific packages.
# Quick development

The fastest way to start with development is to use nox. If you don't have nox,
you can use `pipx run nox` to run it without installing, or `pipx install nox`.
If you don't have pipx (pip for applications), then you can install with
`pip install pipx` (the only case were installing an application with regular
pip is reasonable). If you use macOS, then pipx and nox are both in brew, use
`brew install pipx nox`.
you can use `uvx nox` to run it without installing, or `uv tool install nox`. If
you don't have uv, you can
[install it a variety of ways](https://docs.astral.sh/uv/getting-started/installation/),
including with pip, pipx, brew, and just downloading the binary (single file).

To use, run `nox`. This will lint and test using every installed version of
Python on your system, skipping ones that are not installed. You can also run
Expand All @@ -30,38 +29,17 @@ environment for each run.

You can set up a development environment by running:

{% if cookiecutter.backend == "poetry" -%}

```bash
poetry install
```

{%- else -%}

```bash
python3 -m venv .venv
source ./.venv/bin/activate
pip install -v -e .[dev]
uv sync
```

If you have the
[Python Launcher for Unix](https://github.com/brettcannon/python-launcher), you
can instead do:

```bash
py -m venv .venv
py -m install -v -e .[dev]
```

{%- endif %}

# Pre-commit

You should prepare pre-commit, which will help you by checking that commits pass
required checks:

```bash
pip install pre-commit # or brew install pre-commit on macOS
uv tool install pre-commit # or brew install pre-commit on macOS
pre-commit install # Will install a pre-commit hook into the git repo
```

Expand All @@ -73,15 +51,15 @@ You can also/alternatively run `pre-commit run` (changes only) or
Use pytest to run the unit checks:

```bash
pytest
uv run pytest
```

# Coverage

Use pytest-cov to generate coverage reports:

```bash
pytest --cov={{ cookiecutter.project_name }}
uv run pytest --cov={{ cookiecutter.project_name }}
```

# Building docs
Expand Down
Loading
Loading