Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
682a907
Update dependencies (#301)
johnthagen Aug 11, 2025
1481320
Format markdown files (#302)
johnthagen Aug 11, 2025
36df582
Fix reference to unmaintained pip-licenses to pip-licenses-cli
johnthagen Sep 11, 2025
d6e9c14
Update dependencies (#306)
johnthagen Sep 13, 2025
bcabedb
Migrate to autodoc to mkdocs-api-autonav (#307)
johnthagen Sep 13, 2025
d83ca15
Update dependencies (#308)
johnthagen Sep 30, 2025
b6d2671
Add Python 3.14 support, update default to 3.13, and drop Python 3.9 …
Copilot Oct 8, 2025
9acfd4f
Update to Debian trixie container images (#311)
Copilot Oct 8, 2025
70e3095
Update to uv 0.9.0 (#312)
johnthagen Oct 8, 2025
f402a4d
Update GitHub Actions to latest major versions (#313)
Copilot Oct 8, 2025
2b4f5a8
Update dependencies (#316)
johnthagen Oct 15, 2025
39e1051
Update to uv 0.9.5 (#317)
johnthagen Oct 21, 2025
71ed797
Update dependencies (#318)
johnthagen Oct 30, 2025
a17da2c
Generate a license report (#319)
johnthagen Nov 6, 2025
7844c96
Update to `mkdocs-material` 9.7.0 (#321)
johnthagen Nov 12, 2025
744b433
Add missing LICENSE.txt file to container wheel build environment (#325)
johnthagen Dec 1, 2025
8bd63ac
Update dependencies (#324)
johnthagen Dec 1, 2025
dfd82c7
Update dependencies (#326)
johnthagen Dec 13, 2025
c7d82a2
Document native PyCharm Ruff integration (#327)
johnthagen Dec 13, 2025
7eaea2c
Update dependencies (#328)
johnthagen Jan 1, 2026
4eb9570
Update copyright year (#329)
johnthagen Jan 1, 2026
0c3dc15
Explicitly set GitHub Actions permissions (#330)
johnthagen Jan 6, 2026
e1b3920
Update dependencies (#332)
johnthagen Jan 17, 2026
d4fbf75
Support free threading (#333)
johnthagen Jan 17, 2026
730d1e4
Update to uv 0.10 and Ruff 0.15 (#335)
johnthagen Feb 6, 2026
0fc1fbd
Document `uv python upgrade` (#336)
johnthagen Feb 6, 2026
4d9f02d
Replace deprecated `typer-slim` with `typer` (#337)
johnthagen Feb 11, 2026
8e5925b
Update dependencies and handle recent Material for Mkdocs changes (#338)
johnthagen Feb 26, 2026
832085b
Add a `python-blueprint` badge (#339)
johnthagen Mar 5, 2026
d1f752c
Remove top-level badge tip
johnthagen Mar 5, 2026
3421a65
Fix badge emoji representation on some platforms
johnthagen Mar 6, 2026
584c18d
Upgrade to uv 0.11 (#340)
johnthagen Mar 25, 2026
52af555
Update dependencies (#343)
johnthagen Mar 31, 2026
b4e04f7
Update dependencies (#345)
johnthagen Apr 29, 2026
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
19 changes: 11 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,31 @@

on: [push, pull_request]

permissions:
contents: read

Check warning on line 6 in .github/workflows/ci.yml

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Move this read permission from workflow level to job level.

See more on https://sonarcloud.io/project/issues?id=Onyx-Nostalgia_python-blueprint&issues=AZuUH-6chQxfwyl96986&open=AZuUH-6chQxfwyl96986&pullRequest=6

Check notice

Code scanning / SonarCloud

Read permissions should be defined at the job level Low

Move this read permission from workflow level to job level. See more on SonarQube Cloud

env:
UV_VERSION: "0.8.3"
UV_VERSION: "0.11.8"

jobs:
test:
runs-on: ubuntu-24.04
strategy:
matrix:
python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13" ]
python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14", "3.14t" ]
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Install uv
uses: astral-sh/setup-uv@v5
uses: astral-sh/setup-uv@v7
# Caching is enabled by default for GitHub-hosted runners:
# https://github.com/astral-sh/setup-uv?tab=readme-ov-file#enable-caching
with:
version: ${{ env.UV_VERSION }}

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}

Expand All @@ -40,15 +43,15 @@
nox-session: ["lint", "type_check", "docs"]
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Install uv
uses: astral-sh/setup-uv@v5
uses: astral-sh/setup-uv@v7
with:
version: ${{ env.UV_VERSION }}

- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version-file: ".python-version"

Expand Down
10 changes: 6 additions & 4 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,24 @@ on:
branches: [main]

env:
UV_VERSION: "0.8.3"
UV_VERSION: "0.11.8"

jobs:
deploy:
permissions:
contents: write
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Install uv
uses: astral-sh/setup-uv@v5
uses: astral-sh/setup-uv@v7
with:
version: ${{ env.UV_VERSION }}

- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version-file: ".python-version"

Expand Down
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.12
3.13
8 changes: 4 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
# subtle errors when using MUSL.
# - These Python packages usually only provide binary wheels for GLIBC, so the packages
# will need to be recompiled fully within the container images, increasing build times.
FROM python:3.12-slim-bookworm AS python_builder
FROM python:3.13-slim-trixie AS python_builder

# Pin uv to a specific version to make container builds reproducible.
ENV UV_VERSION=0.8.3
ENV UV_VERSION=0.11.8
ENV UV_PYTHON_DOWNLOADS=never

# Set ENV variables that make Python more friendly to running inside a container.
Expand Down Expand Up @@ -41,7 +41,7 @@ RUN pip install "uv==${UV_VERSION}"
ENV UV_PROJECT_ENVIRONMENT=/opt/venv

# Copy in project dependency specification.
COPY pyproject.toml uv.lock ./
COPY pyproject.toml uv.lock LICENSE.txt ./

# Install only project dependencies, as this is cached until pyproject.toml uv.lock are updated.
RUN uv sync --locked --no-default-groups --no-install-project
Expand All @@ -58,7 +58,7 @@ RUN uv sync --locked --no-default-groups --no-editable

## Final Image
# The image used in the final image MUST match exactly to the python_builder image.
FROM python:3.12-slim-bookworm
FROM python:3.13-slim-trixie

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONBUFFERED=1
Expand Down
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2018-2025 John Hagen
Copyright (c) 2018-2026 John Hagen

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
119 changes: 74 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
[![Nox][nox-badge]](https://github.com/wntrblm/nox)
[![Ruff][ruff-badge]](https://github.com/astral-sh/ruff)
[![Type checked with mypy][mypy-badge]](https://mypy-lang.org/)
[![Python Blueprint][python-blueprint-badge]](https://github.com/johnthagen/python-blueprint)

[github-actions-badge]: https://github.com/johnthagen/python-blueprint/actions/workflows/ci.yml/badge.svg
[uv-badge]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json
[nox-badge]: https://img.shields.io/badge/%F0%9F%A6%8A-Nox-D85E00.svg
[ruff-badge]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
[mypy-badge]: https://www.mypy-lang.org/static/mypy_badge.svg
[python-blueprint-badge]: https://img.shields.io/badge/%F0%9F%97%BA%EF%B8%8F-python--blueprint-2dcf59.svg

Example Python project that demonstrates how to create a Python package using the latest
Python testing, linting, and type checking tooling. The project contains a `fact` package that
Expand All @@ -33,10 +35,10 @@ To [install a specific version of Python](https://docs.astral.sh/uv/guides/insta
uv python install 3.13
```

To upgrade a specific version of Python to the latest patch release:
To upgrade all uv-managed Python versions to their latest patch releases:

```shell
uv python install --reinstall 3.13
uv python upgrade
```

## Dependencies
Expand Down Expand Up @@ -66,7 +68,7 @@ Packaging is configured by:

- [`pyproject.toml`](./pyproject.toml)

To package the project as both a
To package the project as both a
[source distribution](https://packaging.python.org/en/latest/flow/#the-source-distribution-sdist)
and a [wheel](https://packaging.python.org/en/latest/specifications/binary-distribution-format/):

Expand Down Expand Up @@ -98,8 +100,8 @@ uv publish

Automated code quality checks are performed using [Nox](https://nox.thea.codes/en/stable/) and
[`nox-uv`](https://github.com/dantebben/nox-uv). Nox will automatically create virtual environments
and run commands based on [`noxfile.py`](./noxfile.py) for unit testing, PEP 8 style guide checking, type
checking and documentation generation.
and run commands based on [`noxfile.py`](./noxfile.py) for unit testing, PEP 8 style guide
checking, type checking and documentation generation.

> [!NOTE]
> `nox` is installed into the virtual environment automatically by the `uv sync` command
Expand Down Expand Up @@ -254,11 +256,11 @@ run:

```shell
uv run nox -s docs_serve
```
```

and open <http://127.0.0.1:8000> in a browser.

Each time the `main` Git branch is updated, the
Each time the `main` Git branch is updated, the
[`.github/workflows/pages.yml`](.github/workflows/pages.yml) GitHub Action will
automatically build the user guide and publish it to [GitHub Pages](https://pages.github.com/).
This is configured in the `docs_github_pages` Nox session. This hosted user guide
Expand Down Expand Up @@ -313,7 +315,7 @@ distribution has been packaged and installed, thereby catching any errors in pac
installation scripts, which are common. Having the Python packages in the project root subverts
this isolation for two reasons:

1. Calling `python` in the project root (for example, `python -m pytest tests/`)
1. Calling `python` in the project root (for example, `python -m pytest tests/`)
[causes Python to add the current working directory](https://docs.pytest.org/en/latest/pythonpath.html#invoking-pytest-versus-python-m-pytest)
(the project root) to `sys.path`, which Python uses to find modules. Because the source
package `fact` is in the project root, it shadows the `fact` package installed in the Nox
Expand All @@ -332,7 +334,7 @@ prevent this, there are three possible solutions:
to `tests`.
3. Move the source packages to a dedicated `src` folder.

The dedicated `src` directory is the
The dedicated `src` directory is the
[recommended solution](https://docs.pytest.org/en/latest/pythonpath.html#test-modules-conftest-py-files-inside-packages)
by `pytest` when using Nox and the solution this blueprint promotes because it is the least brittle
even though it deviates from the traditional Python project structure. It results is a directory
Expand Down Expand Up @@ -361,17 +363,33 @@ Licensing for the project is defined in:

This project uses a common permissive license, the MIT license.

You may also want to list the licenses of all the packages that your Python project depends on.
To automatically list the licenses for all dependencies in (and their transitive dependencies)
using [pip-licenses](https://github.com/raimon49/pip-licenses):
A license report of all third party packages and their transitive dependencies is generated and
built into the user guide. This allows application developers to comply with these licenses, which
require that the license be included when the library is shipped to end users.

To automatically list the licenses for all dependencies and regenerate the license report using
[pip-licenses-cli](https://github.com/stefan6419846/pip-licenses-cli):

```shell
$ uv run nox -s licenses
...
Name Version License
Pygments 2.19.1 BSD License
click 8.1.8 BSD License
markdown-it-py 3.0.0 MIT License
nox > pip-licenses
Name Version License
Pygments 2.19.2 BSD License
click 8.3.0 BSD-3-Clause
markdown-it-py 4.0.0 MIT License
mdurl 0.1.2 MIT License
rich 14.2.0 MIT License
shellingham 1.5.4 ISC License (ISCL)
typer-slim 0.20.0 MIT License
typing_extensions 4.15.0 PSF-2.0
nox > pip-licenses --summary
Count License
1 BSD License
1 BSD-3-Clause
1 ISC License (ISCL)
4 MIT License
1 PSF-2.0
```

# Container
Expand Down Expand Up @@ -423,6 +441,7 @@ to allow easy execution on macOS and Linux.
# ///

import httpx

print(httpx.get("https://example.com").text)
```

Expand All @@ -449,6 +468,18 @@ fact v1.0.0
└── shellingham v1.5.4 (extra: standard)
```

# Badge

Support [`python-blueprint`](https://github.com/johnthagen/python-blueprint) by adding the badge
to your project:

[![Python Blueprint][python-blueprint-badge]](https://github.com/johnthagen/python-blueprint)

```md
[![Python Blueprint][python-blueprint-badge]](https://github.com/johnthagen/python-blueprint)
[python-blueprint-badge]: https://img.shields.io/badge/%F0%9F%97%BA%EF%B8%8F-python--blueprint-2dcf59.svg
```

# PyCharm Configuration

> [!TIP]
Expand All @@ -460,55 +491,53 @@ project:

- Settings | Search "Hard wrap at" (Note, this will be automatically set by
[`.editorconfig`](./.editorconfig))
- Editor | Code Style | General | Hard wrap at: 99
- Editor | Code Style | General | Hard wrap at: 99

- Settings | Search "Optimize Imports"
- Editor | Code Style | Python | Imports
- ☑ Sort import statements
- ☑ Sort imported names in "from" imports
- ☐ Sort plain and "from" imports separately within a group
- ☐ Sort case-insensitively
- Structure of "from" imports
- ◎ Leave as is
- ◉ Join imports with the same source
- ◎ Always split imports
- Editor | Code Style | Python | Imports
- ☑ Sort import statements
- ☑ Sort imported names in "from" imports
- ☐ Sort plain and "from" imports separately within a group
- ☐ Sort case-insensitively
- Structure of "from" imports
- ◎ Leave as is
- ◉ Join imports with the same source
- ◎ Always split imports

- Settings | Search "Docstrings"
- Tools | Python Integrated Tools | Docstrings | Docstring Format: Google
- Tools | Python Integrated Tools | Docstrings | Docstring Format: Google

- Settings | Search "pytest"
- Tools | Python Integrated Tools | Testing | Default test runner: pytest
- Tools | Python Integrated Tools | Testing | Default test runner: pytest

- Settings | Search "Force parentheses"
- Editor | Code Style | Python | Wrapping and Braces | "From" Import Statements
- ☑ Force parentheses if multiline
- Editor | Code Style | Python | Wrapping and Braces | "From" Import Statements
- ☑ Force parentheses if multiline

## Ruff Integration

Integrate [Ruff](https://docs.astral.sh/ruff/editors/setup/#pycharm) linting and
formatting into PyCharm.

### Linting and Formatting
PyCharm natively supports [Ruff](https://docs.astral.sh/ruff/editors/setup/#pycharm) linting and
formatting.

1. Install the [Ruff PyCharm Plugin](https://plugins.jetbrains.com/plugin/20574-ruff)
2. Open Preferences or Settings | Tools | Ruff
- **Check**: Run Ruff when the python file is saved
- **Check**: Use Import Optimizer
- **Check**: Use ruff format
1. Open Preferences or Settings | Python | Tools | Ruff
- **Check**: Enable
- Open **All Actions on Save...**
- **Check**: Reformat Code
- Files:Python

Now, on <kbd>ctrl+s</kbd>, the current source file will be automatically formatted and imports
sorted on save.
Now, on <kbd>ctrl+s</kbd>, the current source file will be automatically formatted and linting
errors will be shown within the editor.

> [!TIP]
> These tools work best if you properly mark directories as excluded from the project that should
> be, such as `.nox`. See
> <https://www.jetbrains.com/help/pycharm/project-tool-window.html#content_pane_context_menu> on
> These tools work best if you properly mark directories as excluded from the project that should
> be, such as `.nox`. See
> <https://www.jetbrains.com/help/pycharm/project-tool-window.html#content_pane_context_menu> on
> how to Right-Click | Mark Directory as | Excluded.

## Nox Support

[PyCharm does not yet natively support Nox](https://youtrack.jetbrains.com/issue/PY-37302). The
recommended way to launch Nox from PyCharm is to create a **Python**
recommended way to launch Nox from PyCharm is to create a **Python**
[Run Configuration](https://www.jetbrains.com/help/pycharm/run-debug-configuration.html).

- Beside **Script Path**, press `▼` and select **Module name**: `nox`
Expand Down
33 changes: 0 additions & 33 deletions docs/gen_ref_pages.py

This file was deleted.

Loading