Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
15 changes: 10 additions & 5 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,17 @@ jobs:
python ${example}
done

# mkdocs.yml inherits .provide/foundry/base-mkdocs.yml, which is
# gitignored because it is an extract of provide-foundry rather than
# source of ours. Without this the build dies at config load, before it
# reads a page -- which is what every run of this workflow has done.
- name: 🏗️ Extract docs scaffolding
run: uv run --group docs python scripts/extract_docs_scaffolding.py

# Strict mode: any mkdocs warning fails the build, dangling links and
# griffe docstring complaints included.
- name: 📚 Build and Validate MkDocs
run: |
source .venv/bin/activate
# Build with strict mode (fails on warnings)
mkdocs build --clean --strict
echo "✅ MkDocs build completed successfully with no warnings"
run: uv run --group docs mkdocs build --clean --strict

- name: 🔗 Validate README Links
run: |
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -422,3 +422,9 @@ memray-output/

# Local act CI config (machine-specific)
.actrc

# Generated API reference. mkdocs-gen-files keeps these in a temp overlay
# during a build, but running .provide/foundry/gen_ref_pages.py directly
# writes them here for real -- 351 files that are not ours to commit.
docs/reference/provide/
docs/reference/SUMMARY.md
4 changes: 2 additions & 2 deletions docs/reference/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ from provide.foundation.eventsets.types import EventSet, EventMapping
| Feature | Purpose | Documentation |
|---------|---------|---------------|
| `@injectable` | Mark classes for dependency injection | [hub docs](provide/foundation/hub/index.md) |
| `Container` | Dependency injection container | [container docs](provide/foundation/hub/container/index.md) |
| `Container` | Dependency injection container | [container docs](provide/foundation/hub/container.md) |
| `EventSet` | Define custom event sets with emojis | [eventsets docs](provide/foundation/eventsets/index.md) |
| `EventMapping` | Map events to emoji representations | [eventsets docs](provide/foundation/eventsets/index.md) |

Expand Down Expand Up @@ -114,7 +114,7 @@ from provide.foundation.eventsets.types import EventSet, EventMapping

For a complete hierarchical view of all modules, classes, and functions:

**[📑 Full Module Index](SUMMARY/)** - Complete navigation tree
**[📑 Full Module Index](SUMMARY.md)** - Complete navigation tree

## Module Count

Expand Down
11 changes: 11 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,17 @@ dev = [
]
docs = [
"provide-testkit[docs]>=0.4.0",
# mkdocs.yml inherits .provide/foundry/base-mkdocs.yml and runs
# .provide/foundry/gen_ref_pages.py, neither of which is in this repo:
# .gitignore excludes .provide/foundry/ because it is an extract of this
# package. scripts/extract_docs_scaffolding.py writes it, and needs the
# package installed to do so.
#
# Floored at 0.4.1, not 0.4.0: the gen_ref_pages.py that 0.4.0 extracts
# never invokes itself under mkdocs-gen-files, so the reference section
# comes out empty and --strict fails on the dangling links out of
# docs/reference/index.md. 0.4.1 is the first release where this builds.
"provide-foundry>=0.4.1",
]
protobuf = [
"protobuf>=6.32.0",
Expand Down
54 changes: 54 additions & 0 deletions scripts/extract_docs_scaffolding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env python3
# SPDX-FileCopyrightText: Copyright (c) provide.io llc. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

"""Materialise the shared docs scaffolding that mkdocs.yml inherits from.

mkdocs.yml opens with `INHERIT: .provide/foundry/base-mkdocs.yml`, and
.gitignore excludes `.provide/foundry/` because it is an extract of the
provide-foundry package rather than source of ours. Nothing in a fresh checkout
creates it, so `mkdocs build` there fails before reading a single page:

Error: Inherited config file '.provide/foundry/base-mkdocs.yml' does not exist

which is what every documentation CI run has done. Locally the directory
happens to be present, so the failure is invisible on a developer machine.

Run this before mkdocs. It writes `.provide/foundry/` from the installed
provide-foundry: the base config, the theme referenced by `custom_dir`, the
shared partials, the docs helper scripts, and gen_ref_pages.py for the
mkdocs-gen-files plugin.

Exit codes:
0 - scaffolding extracted
1 - provide-foundry is not installed
"""

from __future__ import annotations

from pathlib import Path
import sys


def main() -> int:
"""Extract the docs scaffolding into the current working directory."""
try:
from provide.foundry.config import extract_base_mkdocs
except ImportError:
print(
"provide-foundry is not installed, so the docs scaffolding cannot be\n"
"extracted and `mkdocs build` will fail on its INHERIT line.\n"
"It belongs to the `docs` dependency group: `uv sync --group docs`.",
file=sys.stderr,
)
return 1

base_mkdocs = extract_base_mkdocs(Path.cwd())
print(f"✅ Docs scaffolding extracted to {base_mkdocs.parent}")
return 0


if __name__ == "__main__":
raise SystemExit(main())

# 🧱🏗️🔚
5 changes: 3 additions & 2 deletions src/provide/foundation/errors/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ def handle_error(
The fallback value if not re-raising.

Raises:
The original error if reraise=True.
Exception: The error passed in, re-raised unchanged, when reraise=True.

Examples:
>>> try:
Expand Down Expand Up @@ -277,7 +277,8 @@ def handle(self, error: Exception) -> Any:
Result from the handler function.

Raises:
The original error if reraise_unhandled=True and no handler matches.
Exception: The error passed in, re-raised unchanged, when
reraise_unhandled=True and no policy matches it.

Examples:
>>> result = handler.handle(ValidationError("Invalid"))
Expand Down
7 changes: 4 additions & 3 deletions src/provide/foundation/hub/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,11 @@ def container_group():
category: Command category for grouping
group: Whether this is a command group (not a command)
replace: Whether to replace existing registration
force_options: If True, all parameters with defaults become --options
(disables Position-Based Hybrid for first parameter)
registry: Custom registry (defaults to global)
**metadata: Additional metadata stored in CommandInfo.metadata
**metadata: Additional metadata stored in CommandInfo.metadata. The CLI
builder reads `force_options` from here: if True, every parameter
with a default becomes a --option, disabling the Position-Based
Hybrid rule for the first parameter.

Returns:
Decorator function or decorated function
Expand Down
6 changes: 4 additions & 2 deletions src/provide/foundation/resilience/retry.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,8 @@ def execute_sync(self, func: Callable[..., T], *args: Any, **kwargs: Any) -> T:
Result from successful execution

Raises:
Last exception if all retries are exhausted
Exception: The last exception raised by func, once every attempt
allowed by the policy has been used.

"""
last_exception = None
Expand Down Expand Up @@ -295,7 +296,8 @@ async def execute_async(self, func: Callable[..., Awaitable[T]], *args: Any, **k
Result from successful execution

Raises:
Last exception if all retries are exhausted
Exception: The last exception raised by func, once every attempt
allowed by the policy has been used.

"""
last_exception = None
Expand Down
2 changes: 0 additions & 2 deletions src/provide/foundation/utils/scoped_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,6 @@ def scope(self) -> Generator[None]:
Yields:
None (use cache methods within the context)

Raises:
No exceptions - cleanup is guaranteed even on errors
"""
if self._context_var.get() is None:
# No existing cache - create new scope
Expand Down
77 changes: 76 additions & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading