diff --git a/README.md b/README.md
index 77a4eac..46b6b6e 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@ provides a simple implementation of the
[factorial algorithm](https://en.wikipedia.org/wiki/Factorial) (`fact.lib`) and a command line
interface (`fact.cli`).
-# Package Management
+## Package Management
This package uses [uv](https://docs.astral.sh/uv/) to manage dependencies and
isolated [Python virtual environments](https://docs.python.org/3/library/venv.html).
@@ -41,7 +41,7 @@ To upgrade all uv-managed Python versions to their latest patch releases:
uv python upgrade
```
-## Dependencies
+### Dependencies
Dependencies are defined in [`pyproject.toml`](./pyproject.toml) and specific versions are locked
into [`uv.lock`](./uv.lock). This allows for exact reproducible environments across
@@ -59,7 +59,7 @@ To upgrade all dependencies to their latest versions:
uv lock --upgrade
```
-## Packaging
+### Packaging
This project is designed as a Python package, meaning that it can be bundled up and redistributed
as a single compressed file.
@@ -82,7 +82,7 @@ This will generate `dist/fact-1.0.0.tar.gz` and `dist/fact-1.0.0-py3-none-any.wh
> Read more about the [advantages of wheels](https://pythonwheels.com/) to understand why
> generating wheel distributions are important.
-## Publish Distributions to PyPI
+### Publish Distributions to PyPI
Source and wheel redistributable packages can
be [published to PyPI](https://docs.astral.sh/uv/guides/package/) or installed
@@ -96,7 +96,7 @@ uv publish
> To enable publishing, remove the `"Private :: Do Not Upload"`
> [trove classifier](https://pypi.org/classifiers/).
-# Enforcing Code Quality
+## Enforcing Code Quality
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
@@ -113,7 +113,7 @@ To run all default sessions:
uv run nox
```
-## Unit Testing
+### Unit Testing
Unit testing is performed with [pytest](https://pytest.org/). pytest has become the de facto Python
unit testing framework. Some key advantages over the built-in
@@ -145,7 +145,7 @@ To pass arguments to `pytest` through `nox`:
uv run nox -s test -- -k invalid_factorial
```
-## Code Style Checking
+### Code Style Checking
[PEP 8](https://peps.python.org/pep-0008/) is the universally accepted style guide for Python
code. PEP 8 code compliance is verified using [Ruff][Ruff]. Ruff is configured in the
@@ -168,7 +168,7 @@ To automatically fix fixable lint errors, run:
uv run nox -s lint_fix
```
-## Automated Code Formatting
+### Automated Code Formatting
[Ruff][Ruff] is used to automatically format code and group and sort imports.
@@ -178,7 +178,7 @@ To automatically format code, run:
uv run nox -s fmt
```
-## Type Checking
+### Type Checking
[Type annotations](https://docs.python.org/3/library/typing.html) allows developers to include
optional static typing information to Python source code. This allows static analyzers such
@@ -202,7 +202,7 @@ uv run nox -s type_check
See also [awesome-python-typing](https://github.com/typeddjango/awesome-python-typing).
-### Distributing Type Annotations
+#### Distributing Type Annotations
[PEP 561](https://www.python.org/dev/peps/pep-0561/) defines how a Python package should
communicate the presence of inline type annotations to static type
@@ -212,16 +212,16 @@ provides further examples on how to do this.
Mypy looks for the existence of a file named [`py.typed`](./src/fact/py.typed) in the root of the
installed package to indicate that inline type annotations should be checked.
-## Continuous Integration
+### Continuous Integration
Continuous integration is provided by [GitHub Actions](https://github.com/features/actions). This
runs all tests, lints, and type checking for every commit and pull request to the repository.
GitHub Actions is configured in [`.github/workflows/ci.yml`](./.github/workflows/ci.yml).
-# Documentation
+## Documentation
-## Generating a User Guide
+### Generating a User Guide
[Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) is a powerful static site
generator that combines easy-to-write Markdown, with a number of Markdown extensions that increase
@@ -265,7 +265,7 @@ automatically build the user guide and publish it to [GitHub Pages](https://page
This is configured in the `docs_github_pages` Nox session. This hosted user guide
can be viewed at .
-## Generating API Documentation
+### Generating API Documentation
This project uses [mkdocstrings](https://github.com/mkdocstrings/mkdocstrings) plugin for
MkDocs, which renders
@@ -287,12 +287,12 @@ Returns:
"""
```
-# Project Structure
+## Project Structure
Traditionally, Python projects place the source for their packages in the root of the project
structure, like:
-``` {.sourceCode .}
+```text {.sourceCode .}
fact
├── fact
│ ├── __init__.py
@@ -339,7 +339,7 @@ by `pytest` when using Nox and the solution this blueprint promotes because it i
even though it deviates from the traditional Python project structure. It results is a directory
structure like:
-``` {.sourceCode .}
+```text {.sourceCode .}
fact
├── src
│ └── fact
@@ -353,7 +353,7 @@ fact
└── pyproject.toml
```
-# Licensing
+## Licensing
Licensing for the project is defined in:
@@ -391,7 +391,7 @@ nox > pip-licenses --summary
1 PSF-2.0
```
-# Container
+## Container
[Docker](https://www.docker.com/) is a tool that allows for software to be packaged into isolated
containers. It is not necessary to use Docker in a Python project, but for the purposes of
@@ -416,14 +416,14 @@ To run the image in a container:
docker run --rm --interactive --tty fact 5
```
-# AI Agents
+## AI Agents
AI agentic coding tools are configured in [AGENTS.md](./AGENTS.md). For more details, see
.
-# Miscellaneous
+## Miscellaneous
-## Shebang Line
+### Shebang Line
The proper [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) line for Python scripts is:
@@ -451,7 +451,7 @@ print(httpx.get("https://example.com").text)
On Windows, remove the `-S` from the shebang and execute the script using the `py` launcher.
-## Package Dependency Tree
+### Package Dependency Tree
`uv tree` is a command for listing installed packages in the form of a dependency tree. For large
projects, it is often difficult to determine dependency relationships solely from manually
@@ -472,7 +472,7 @@ fact v1.0.0
└── shellingham v1.5.4 (extra: standard)
```
-# Badge
+## Badge
Support [`python-blueprint`](https://github.com/johnthagen/python-blueprint) by adding the badge
to your project:
@@ -484,7 +484,7 @@ to your project:
[python-blueprint-badge]: https://img.shields.io/badge/%F0%9F%97%BA%EF%B8%8F-python--blueprint-2dcf59.svg
```
-# PyCharm Configuration
+## PyCharm Configuration
> [!TIP]
> Looking for a vivid dark color scheme for PyCharm?
@@ -518,7 +518,7 @@ project:
- Editor | Code Style | Python | Wrapping and Braces | "From" Import Statements
- ☑ Force parentheses if multiline
-## Ruff Integration
+### Ruff Integration
PyCharm natively supports [Ruff](https://docs.astral.sh/ruff/editors/setup/#pycharm) linting and
formatting.
@@ -538,7 +538,7 @@ errors will be shown within the editor.
> on
> how to Right-Click | Mark Directory as | Excluded.
-## Nox Support
+### 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**
@@ -550,4 +550,4 @@ recommended way to launch Nox from PyCharm is to create a **Python**
- Select **Modify Options** | Check **Emulate terminal in output console** to enable colors to be
rendered properly
-
+
diff --git a/docs/licenses/license_report.md b/docs/licenses/license_report.md
index e4378e4..ff1bb46 100644
--- a/docs/licenses/license_report.md
+++ b/docs/licenses/license_report.md
@@ -11,6 +11,6 @@ search:
## License Files
-```
+```text
--8<-- "docs/licenses/license_files.txt"
```
diff --git a/noxfile.py b/noxfile.py
index 8627330..b9791c5 100644
--- a/noxfile.py
+++ b/noxfile.py
@@ -46,6 +46,7 @@ def test(s: Session) -> None:
id="sort_imports",
),
param(["ruff", "format", "."], id="format"),
+ param(["rumdl", "fmt", "."], id="md_format"),
],
)
def fmt(s: Session, command: list[str]) -> None:
@@ -58,6 +59,7 @@ def fmt(s: Session, command: list[str]) -> None:
[
param(["ruff", "check", "."], id="lint_check"),
param(["ruff", "format", "--check", "."], id="format_check"),
+ param(["rumdl", "check", "."], id="md_format_check"),
],
)
def lint(s: Session, command: list[str]) -> None:
diff --git a/pyproject.toml b/pyproject.toml
index 9be484f..cb9d831 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -53,6 +53,7 @@ type_check = [
]
lint = [
"ruff",
+ "rumdl",
]
docs = [
"mkdocs-material",
@@ -133,6 +134,14 @@ split-on-trailing-comma = false
[tool.ruff.lint.flake8-tidy-imports]
ban-relative-imports = "all"
+[tool.rumdl]
+line-length = 99
+flavor = "mkdocs"
+disable = [
+ "MD033",
+]
+
+
[tool.pytest.ini_options]
addopts = [
"--strict-config",
diff --git a/uv.lock b/uv.lock
index 7f37439..7912f80 100644
--- a/uv.lock
+++ b/uv.lock
@@ -522,6 +522,7 @@ licenses = [
]
lint = [
{ name = "ruff" },
+ { name = "rumdl" },
]
nox = [
{ name = "nox-uv" },
@@ -549,7 +550,10 @@ docs = [
{ name = "mkdocstrings", extras = ["python"] },
]
licenses = [{ name = "pip-licenses-cli" }]
-lint = [{ name = "ruff" }]
+lint = [
+ { name = "ruff" },
+ { name = "rumdl" },
+]
nox = [{ name = "nox-uv" }]
test = [
{ name = "pytest" },
@@ -1438,6 +1442,21 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/fc/07/d781f8f8e1ac24bef9f3269cf62ffb1407ca24c3a8f12e5e22874f90528c/ruff-0.16.6-py3-none-win_arm64.whl", hash = "sha256:7a976c79b958f94e50a022a19f0f8c87387448020935ec14fc74331bd0a7f2c5", size = 10412850, upload-time = "2026-09-03T16:57:26.416Z" },
]
+[[package]]
+name = "rumdl"
+version = "0.2.73"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8a/c9/fdbf8321f63d8d540efd2ef01c2e5a17f103c028152f7138cd3401f60aa5/rumdl-0.2.73.tar.gz", hash = "sha256:14237a069b4fc9ead00535062b08826c37235a086d170fc2d57f20634892e66a", size = 4695281, upload-time = "2026-09-11T19:07:25.699Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/46/24/b8c4f5d21446a660aa866dd1bb2aa488e781eaf0cafaec0756a3a95c77c5/rumdl-0.2.73-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:0b909fac1ad77a68e34619945f7456bea835057d79568ad8c6eb4b0b4f15d74b", size = 7125343, upload-time = "2026-09-11T19:07:18.27Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/35/e2c23352a635578de3692755da99762cf35ab33c82f5ba9951e3c7a35f49/rumdl-0.2.73-py3-none-macosx_11_0_arm64.whl", hash = "sha256:51e9efa2b97babc3181d71a089ae8f842f01eaf78b8aa3c7018e7d10ce3f1928", size = 6736640, upload-time = "2026-09-11T19:07:12.734Z" },
+ { url = "https://files.pythonhosted.org/packages/97/c3/61b4f3b2c7be4b4f15d5f45484e8c127e0d03402ed83e3861054b189db5a/rumdl-0.2.73-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:b0ed815d4c957c0448325b23d799247714112cdcf344062527a4fa9b25785da7", size = 6894769, upload-time = "2026-09-11T19:07:15.011Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/03/ed945f603896d44d74bdc8c395efc2e846500ed18ed523174060e00637d2/rumdl-0.2.73-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:c56ede961c39eb4f8682cd9330f0fd645b0c7bf791de1c4ba717a41c0ce65000", size = 7326038, upload-time = "2026-09-11T19:07:21.892Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/87/c4bd9e7f9915f386c00856619c95a8266ef2283aa57c6bf39dc020dff41a/rumdl-0.2.73-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:baa6177f18b2a6f86b340369bc3836197d27d21618779226c50c670eab807fc7", size = 6867407, upload-time = "2026-09-11T19:07:16.69Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/7c/98e633d0c6d47da59d714fdb21fbff3cf2b6bf9d98e21264d9f5d1e291c3/rumdl-0.2.73-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7a9869b6061be47d451d19d3d4ba5b3140a1b5b44e8a614ef005f5044746976f", size = 7296231, upload-time = "2026-09-11T19:07:23.84Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/9e/105c61dacbdbb0f4b5ba26d64b258a71a1589dd14a0025d39920789fd636/rumdl-0.2.73-py3-none-win_amd64.whl", hash = "sha256:f27963c641811a3de38797a6aaa3a6ab326b6ba6e85985973015e2e75bec0628", size = 7343232, upload-time = "2026-09-11T19:07:20.034Z" },
+]
+
[[package]]
name = "shellingham"
version = "1.5.4"