Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
d21cb5a
Restructure into src layout with uv/hatchling packaging
Pythondeveloper6 Oct 27, 2026
a9114a9
Add retry, fallback, suppress and circuit breaker modules
Pythondeveloper6 Oct 27, 2026
4541ab6
Add caching (memoize, cache, once) and state (revert_on_failure, tran…
Pythondeveloper6 Oct 27, 2026
f62a747
Merge retry and circuit breaker modules
Pythondeveloper6 Oct 27, 2026
6b31a52
Merge caching and state modules
Pythondeveloper6 Oct 27, 2026
c84d67f
Add timing and logging decorator modules
Pythondeveloper6 Oct 27, 2026
9a29425
Add concurrency module: timeout, rate_limit, run_in_thread, throttle,…
Pythondeveloper6 Oct 27, 2026
c368c3e
Merge timing and logging modules
Pythondeveloper6 Oct 27, 2026
3823b89
Merge concurrency module
Pythondeveloper6 Oct 27, 2026
c448aa4
Add devtools module: deprecate, mock_data, no_debug, compose
Pythondeveloper6 Oct 27, 2026
8fb2d5f
Add CI, trusted-publishing release, docs build and contributor infras…
Pythondeveloper6 Oct 27, 2026
6a72392
Merge devtools module
Pythondeveloper6 Oct 27, 2026
d240c73
Merge CI, release and repository files
Pythondeveloper6 Oct 27, 2026
1e89d6a
Add validation module: validate_args, check_type, enforce_types
Pythondeveloper6 Oct 27, 2026
6199fa0
Merge validation module
Pythondeveloper6 Oct 27, 2026
d1f8588
Export the v0.2 public API and keep v0.1 imports working
Pythondeveloper6 Oct 27, 2026
e0e91db
Write the v0.2.0 README and CHANGELOG
Pythondeveloper6 Oct 27, 2026
bc1b89f
Merge README and CHANGELOG
Pythondeveloper6 Oct 27, 2026
ed1ae1d
Use absolute repository links in the README so they resolve on PyPI
Pythondeveloper6 Oct 27, 2026
6876501
Fix suppress typing, class revert, deprecate on classes without __ini…
Pythondeveloper6 Oct 27, 2026
eaa7321
Fix caching review findings: per-loop in-flight tasks, TTL sweep, bou…
Pythondeveloper6 Oct 27, 2026
8a20e81
Merge review fixes for retry, state, devtools and logs
Pythondeveloper6 Oct 27, 2026
772d446
Merge review fixes for caching
Pythondeveloper6 Oct 27, 2026
75fdbe9
Add MkDocs documentation site for v0.2.0
Pythondeveloper6 Oct 27, 2026
d69fb90
Merge MkDocs documentation site
Pythondeveloper6 Oct 27, 2026
8db558d
Run the documentation examples in the docs workflow
Pythondeveloper6 Oct 27, 2026
3787b28
Fix async timeout, async callables and singleflight edge cases
Pythondeveloper6 Oct 27, 2026
cf1e76a
Merge review fixes for concurrency and core
Pythondeveloper6 Oct 27, 2026
7f96a6a
Add python_wrap.testing helpers and process-wide test overrides
Pythondeveloper6 Oct 27, 2026
86180ab
Merge testing helpers
Pythondeveloper6 Oct 27, 2026
c03f6a2
Keep eventually() polling at a real interval under fast_mode
Pythondeveloper6 Oct 27, 2026
809d923
Add async toolkit (gather_limit, map_async, amap_iter, race, run_sync…
Pythondeveloper6 Oct 27, 2026
61225b6
Merge async toolkit and bulkhead
Pythondeveloper6 Oct 27, 2026
4711ffa
Add batch_calls (DataLoader-style batching) and hedge (hedged requests)
Pythondeveloper6 Oct 27, 2026
e855487
Keep decorator types through stacking, bind cls under classmethod, ty…
Pythondeveloper6 Oct 27, 2026
6b08f0e
Merge batch_calls and hedge
Pythondeveloper6 Oct 27, 2026
dc7d829
Merge typing fixes and docstring cleanup
Pythondeveloper6 Oct 27, 2026
10d3cd3
Replace remaining Sphinx roles in docstrings with Markdown code spans
Pythondeveloper6 Oct 27, 2026
77ad4ca
Add a process-wide event hook to _core for metrics
Pythondeveloper6 Oct 27, 2026
1e57ff9
Add events, metrics and log_context
Pythondeveloper6 Oct 27, 2026
2a5ed68
Add retry delay_from, max_time, stats, logging control and retrying()…
Pythondeveloper6 Oct 27, 2026
50ed400
Merge events, metrics and log context
Pythondeveloper6 Oct 27, 2026
6c65248
Merge retry feature bundle
Pythondeveloper6 Oct 27, 2026
c1187c7
Add policy() decorator bundles and wrap_methods class decorator
Pythondeveloper6 Oct 27, 2026
cf43c45
Merge policy and wrap_methods
Pythondeveloper6 Oct 27, 2026
2f3ccda
Add cached_property, exception caching, per-item TTL and cache events…
Pythondeveloper6 Oct 27, 2026
b382cbc
Add decorator micro-benchmarks and performance guide
Pythondeveloper6 Oct 27, 2026
cf54dae
Merge caching feature bundle
Pythondeveloper6 Oct 27, 2026
993eaaa
Export the new feature APIs from the package root
Pythondeveloper6 Oct 27, 2026
95e9a87
Merge benchmarks and performance page
Pythondeveloper6 Oct 27, 2026
b0d40fa
Speed up validation, audit/log_args and timing hot paths
Pythondeveloper6 Oct 27, 2026
70b4603
Merge hot-path optimisations for validation, logs and timing
Pythondeveloper6 Oct 27, 2026
9923200
Add cookbook of real-world recipes and runnable examples
Pythondeveloper6 Oct 27, 2026
caff1f8
Add deadlines, timeout context managers, shared rate limiters and cir…
Pythondeveloper6 Oct 27, 2026
01d2fa9
Merge cookbook and runnable examples
Pythondeveloper6 Oct 27, 2026
43104ae
Merge circuit, deadline and rate limiter features
Pythondeveloper6 Oct 27, 2026
6a52741
Export rate limiter, timeout and deadline APIs; run the cookbook over…
Pythondeveloper6 Oct 27, 2026
054ce27
Accept the Python 3.10 spelling of Optional in an enforce_types test
Pythondeveloper6 Oct 27, 2026
1ab22d1
Add thread-safety stress suite (opt-in with PW_STRESS=1)
Pythondeveloper6 Oct 27, 2026
a27c1ab
Merge thread-safety stress suite
Pythondeveloper6 Oct 27, 2026
1af6d8a
Typed transactional tx injection, require_transaction, real-time Fake…
Pythondeveloper6 Oct 27, 2026
c664784
Merge branch 'release/v0.2.0' into worktree-agent-aecd1b5b9e86079ca
Pythondeveloper6 Oct 27, 2026
c3d0f6c
Make Transaction completion atomic and override scopes overlap-safe
Pythondeveloper6 Oct 27, 2026
f6d111f
Add project-wide configure(), redact_extra and deadline-aware retry
Pythondeveloper6 Oct 27, 2026
190f34d
Wrap docstring line
Pythondeveloper6 Oct 27, 2026
0622711
Merge configure() defaults, deadline-aware retry and redact_extra
Pythondeveloper6 Oct 27, 2026
2d6ad70
Export the configuration API
Pythondeveloper6 Oct 27, 2026
7524f78
Merge polish: typed transactions, FakeClock start, docs checker modul…
Pythondeveloper6 Oct 27, 2026
5cdfd22
Export INJECTED and require_transaction
Pythondeveloper6 Oct 27, 2026
6883fee
Fix async memoize/once/cached_property under eager tasks; run async o…
Pythondeveloper6 Oct 27, 2026
8604d29
Merge async memoize/once race fixes
Pythondeveloper6 Oct 27, 2026
58d77d8
docs: refresh README, changelog, index, chooser and migration for the…
Pythondeveloper6 Oct 27, 2026
282833e
Merge README, CHANGELOG and docs landing updates
Pythondeveloper6 Oct 27, 2026
afc57ea
Fix bulkhead role names in policy docs
Pythondeveloper6 Oct 27, 2026
c054dc1
Speed up circuit_breaker, timeout, singleflight and batch_calls; fix …
Pythondeveloper6 Oct 27, 2026
4ce92b2
Merge performance round 2 and concurrency race fixes
Pythondeveloper6 Oct 27, 2026
4c57c46
Export limiter_of
Pythondeveloper6 Oct 27, 2026
6e52503
docs: refresh performance numbers after recent decorator speedups
Pythondeveloper6 Oct 27, 2026
1185140
Merge refreshed performance numbers
Pythondeveloper6 Oct 27, 2026
3739ce8
Type memoize cache lookups with the full argument list and document d…
Pythondeveloper6 Oct 27, 2026
568ce41
Import profiling, tracemalloc and email helpers on first use
Pythondeveloper6 Oct 27, 2026
3ae3e95
Keep deadlines on real time under test overrides and fix limiter and …
Pythondeveloper6 Oct 27, 2026
d0e2736
Start FakeClock on a whole second and install pytest for the docs exa…
Pythondeveloper6 Oct 27, 2026
c330d0c
Allow the rounded FakeClock start in its basics test
Pythondeveloper6 Oct 27, 2026
d442930
Give the debounce and race examples timing margins that hold on slow …
Pythondeveloper6 Oct 27, 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
38 changes: 38 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Bug report
description: Something behaves differently from what the documentation says.
labels: [bug]
body:
- type: textarea
id: what
attributes:
label: What happened?
description: What you did, what you expected, and what you got instead.
validations:
required: true
- type: textarea
id: repro
attributes:
label: Minimal reproduction
description: The smallest code that shows the problem, including the decorator and how it is called (sync, async, method).
render: python
validations:
required: true
- type: input
id: version
attributes:
label: python-wrap version
description: "Run: python -c 'import python_wrap; print(python_wrap.__version__)'"
validations:
required: true
- type: input
id: python
attributes:
label: Python version and operating system
placeholder: "3.13.1 on Windows 11"
validations:
required: true
- type: textarea
id: traceback
attributes:
label: Traceback or log output
render: text
8 changes: 8 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Security vulnerability
url: https://github.com/LitePy/PythonWrap/security/advisories/new
about: Please report security issues privately, not in a public issue.
- name: Documentation
url: https://python-wrap.readthedocs.io
about: Check the docs first; most questions are answered there.
27 changes: 27 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Feature request
description: Suggest a new decorator or an option on an existing one.
labels: [enhancement]
body:
- type: textarea
id: problem
attributes:
label: What problem are you trying to solve?
description: The situation first, not the solution.
validations:
required: true
- type: textarea
id: proposal
attributes:
label: What would you like to write?
description: A sketch of the API as you would want to call it.
render: python
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
description: Other libraries or hand-written code you tried, and why they fall short.
- type: markdown
attributes:
value: |
python-wrap has no runtime dependencies and supports sync and async code,
so proposals are weighed against both.
12 changes: 12 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## What and why

<!-- What does this change, and what motivated it? Link the issue if there is one. -->

## Checklist

- [ ] Tests added or updated (sync and async paths; use `fake_clock` rather than real sleeps)
- [ ] `uv run pytest` passes
- [ ] `uv run ruff check src tests` and `uv run ruff format --check src tests` pass
- [ ] `uv run mypy` passes
- [ ] Public functions have Google-style docstrings
- [ ] `CHANGELOG.md` updated under `Unreleased`
26 changes: 26 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Keeps the pinned GitHub Actions and the lockfile moving, in as few pull requests as
# possible. Grouped, weekly: they are reviewed together and it keeps the queue readable.
version: 2

updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
groups:
actions:
patterns: ["*"]
commit-message:
prefix: "ci"

# Refreshes uv.lock, which pins the dev and docs tooling (the package itself has no
# runtime dependencies).
- package-ecosystem: uv
directory: /
schedule:
interval: weekly
groups:
dev-tooling:
patterns: ["*"]
commit-message:
prefix: "deps"
147 changes: 147 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Continuous integration.
#
# Four jobs: lint (seconds), the test matrix, mypy, and a packaging check.
#
# python-wrap is pure Python with no dependencies, so the matrix is wide rather than
# deep: every interpreter the classifiers name, on all three operating systems. Time and
# threading behave differently on Windows (coarser clocks) and macOS (spawn, not fork),
# and a decorator library that schedules, throttles and times things is exactly where
# that shows up.
name: CI

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

# A force-push should not leave the previous run burning minutes on a dead commit.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

env:
FORCE_COLOR: "1"

jobs:
lint:
name: ruff
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- name: Install uv
uses: astral-sh/setup-uv@v10.3.2
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
python-version: "3.13"

- run: uv sync --group dev --frozen

- name: ruff check
run: uv run --no-sync ruff check src tests

- name: ruff format
run: uv run --no-sync ruff format --check --diff src tests

test:
name: py${{ matrix.python-version }} · ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
# One red cell should not hide the other fourteen.
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]

steps:
- uses: actions/checkout@v7

- name: Install uv
uses: astral-sh/setup-uv@v10.3.2
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
python-version: ${{ matrix.python-version }}

- run: uv sync --group dev --frozen

- name: pytest
run: uv run --no-sync pytest --cov --cov-report=term-missing --cov-report=xml

# One coverage report is enough; it is the same code on every cell. Keeping it as an
# artifact (rather than a third-party upload) needs no token and cannot fail the
# build for a reason unrelated to the code.
- name: Upload the coverage report
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.13'
uses: actions/upload-artifact@v7
with:
name: coverage
path: coverage.xml
if-no-files-found: warn

typecheck:
name: mypy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- name: Install uv
uses: astral-sh/setup-uv@v10.3.2
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
python-version: "3.13"

- run: uv sync --group dev --frozen

# Strict mode, configured in pyproject.toml; `files` already points at src/.
- name: mypy
run: uv run --no-sync mypy

build:
name: build & wheel contents
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- name: Install uv
uses: astral-sh/setup-uv@v10.3.2
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
python-version: "3.13"

- name: Build the sdist and wheel
run: uv build

- name: twine check
run: uvx twine check --strict dist/*

# The package ships `py.typed` so that type checkers use its annotations. A wheel
# that drops it still imports and still passes twine, and silently turns every
# downstream user's types into `Any`.
- name: The wheel ships py.typed
run: |
python3 -m zipfile -l dist/*.whl | tee wheel-listing.txt
grep -q 'python_wrap/py.typed' wheel-listing.txt

# Install the built wheel, not the source tree, into an empty venv and run from a
# scratch directory so `src/` cannot stand in for anything the wheel left out.
- name: Install the wheel into a clean environment
run: |
uv venv "$RUNNER_TEMP/smoke" --python 3.13
uv pip install --python "$RUNNER_TEMP/smoke" dist/*.whl
cd "$RUNNER_TEMP"
"$RUNNER_TEMP/smoke/bin/python" -c "import python_wrap; print(python_wrap.__version__)"

- name: Upload the distributions
uses: actions/upload-artifact@v7
with:
name: dist
path: dist/
if-no-files-found: error
50 changes: 50 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Build check for the documentation site.
#
# Read the Docs does the hosting (see .readthedocs.yaml). This workflow only catches
# breakage before it gets there: `--strict` turns a broken link, a page missing from the
# nav or a docstring mkdocstrings cannot render into a failed check on the pull request.
name: Docs

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

env:
FORCE_COLOR: "1"

jobs:
build:
name: mkdocs build --strict
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- name: Install uv
uses: astral-sh/setup-uv@v10.3.2
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
python-version: "3.13"

# The package itself is installed too, because mkdocstrings reads the docstrings
# from it. The dev group is included because some examples are pytest tests.
- run: uv sync --frozen --no-default-groups --group docs --group dev

- name: Build the site
run: uv run --no-sync mkdocs build --strict
env:
NO_MKDOCS_2_WARNING: "1"

# Every ```python block in docs/ is executed, so an example that drifts from the
# real API fails review instead of misleading a reader.
- name: Run the documentation examples
run: uv run --no-sync python scripts/check_docs_examples.py
60 changes: 0 additions & 60 deletions .github/workflows/publish.yml

This file was deleted.

Loading
Loading