A comprehensive async cancellation system for Python streams
Project description
Cancelable
A comprehensive, production-ready async cancellation system for Python 3.13+ using anyio.
Table of Contents
Features
- Multiple Cancellation Sources: Timeout, manual tokens, OS signals, and custom conditions
- Composable Design: Combine multiple cancellation sources easily
- Stream Processing: Built-in support for cancelable async iterators
- Operation Tracking: Full lifecycle tracking with status and progress reporting
- Library Integrations: Ready-to-use FastAPI integration for request cancellation
- Type Safe: Full type hints and runtime validation with Pydantic
- Production Ready: Comprehensive error handling, logging, and performance optimized
Installation
Core Installation
The core library includes only essential dependencies (anyio and pydantic):
uv add hother-cancelable
Optional Extras
Cancelable provides optional extras for various integrations and use cases:
Available Extras
| Extra | Dependencies | Purpose |
|---|---|---|
fastapi |
fastapi | FastAPI middleware for request cancellation |
examples |
google-genai, pynput, psutil | Run example scripts and demonstrations |
Installing with Extras
FastAPI integration:
uv add "hother-cancelable[fastapi]"
Examples:
uv add "hother-cancelable[examples]"
All extras:
uv add "hother-cancelable[fastapi,examples]"
Quick Start
Basic Usage
from hother.cancelable import Cancelable
# Timeout-based cancellation
async with Cancelable.with_timeout(30.0) as cancel:
result = await long_running_operation()
# Manual cancellation with token
from hother.cancelable import CancellationToken
token = CancellationToken()
async with Cancelable.with_token(token) as cancel:
# In another task/thread: await token.cancel()
result = await interruptible_operation()
Stream Processing
# Cancelable stream processing
async with Cancelable.with_timeout(60.0) as cancel:
async for item in cancel.stream(data_source(), report_interval=100):
await process_item(item)
Function Decorators
from hother.cancelable import cancelable
@cancelable(timeout=30.0, register_globally=True)
async def process_data(data: list, cancelable: Cancelable = None):
for i, item in enumerate(data):
await cancelable.report_progress(f"Processing item {i+1}/{len(data)}")
await process_item(item)
Integrations
Cancelable provides seamless integration with FastAPI. See the integrations documentation for detailed guides and examples.
- FastAPI: Add cancellation middleware to FastAPI applications with automatic request-scoped cancellation
Documentation
To build and serve the documentation locally:
- Install the documentation dependencies:
uv sync --group doc
source .venv/bin/activate
- Serve the documentation:
mkdocs serve
Development
Dependency Groups
Cancelable uses dependency groups for development and documentation:
| Group | Purpose | Key Dependencies |
|---|---|---|
dev |
Development tools | pytest, ruff, basedpyright, twine, git-cliff |
doc |
Documentation building | mkdocs, mkdocs-material, mike |
Installation
Basic development setup:
uv sync --group dev
source .venv/bin/activate
lefthook install
This creates a virtual environment, installs all development dependencies, and installs the library in editable mode. It also sets up Lefthook git hooks.
Full development setup with extras:
Some tests and examples require optional extras. To run the full test suite:
# Install dev tools + all extras
uv sync --group dev --all-extras
Selective installation:
# Install with specific extras
uv sync --group dev --extra fastapi --extra examples
# Install documentation tools
uv sync --group doc
Quick Reference
Available extras:
fastapi- FastAPI middlewareexamples- Example scripts
Available groups:
dev- Development tools (pytest, ruff, basedpyright, etc.)doc- Documentation tools (mkdocs, mkdocs-material, etc.)
Git Hooks with Lefthook
This project uses Lefthook for managing git hooks. Hooks are automatically installed when you run make install-dev.
To run hooks manually:
# Run all pre-commit hooks
lefthook run pre-commit
Tests
Run core tests (without integration extras):
uv run pytest
Integration tests that require optional dependencies (fastapi) will be automatically skipped if the extras are not installed.
Run all tests including integrations:
# First install all extras
uv sync --all-extras
# Then run tests
uv run pytest
Run specific test categories:
# Run only unit tests
uv run pytest tests/unit
# Run only integration tests (requires extras)
uv run pytest tests/integration
Coverage
uv run pytest --cov=hother.cancelable
Building the package
uv build
Release process
This project uses python-semantic-release for fully automated versioning and releases. Every commit to the main branch is analyzed using conventional commits, and releases are created automatically when needed.
How It Works
- Commit with conventional format to the
mainbranch - GitHub Actions automatically analyzes commits, determines version bump, creates tag, updates changelog, publishes to PyPI, and creates GitHub release
- Documentation is automatically deployed when a release is published
No manual intervention required! 🎉
Version Bumping Rules
| Commit Type | Version Bump | Example |
|---|---|---|
feat: |
Minor | 0.5.0 → 0.6.0 |
fix:, perf:, refactor: |
Patch | 0.5.0 → 0.5.1 |
feat!:, BREAKING CHANGE: |
Major | 0.5.0 → 1.0.0 |
docs:, chore:, ci:, style:, test: |
No release | - |
Conventional Commit Examples
# Minor version bump (new feature)
git commit -m "feat: add streaming cancellation support"
# Patch version bump (bug fix)
git commit -m "fix: resolve race condition in token cancellation"
# Major version bump (breaking change)
git commit -m "feat!: redesign cancellation API
BREAKING CHANGE: CancellationToken.cancel() is now async"
Manual Release Trigger
If needed, you can manually trigger a release via GitHub Actions:
# Go to: Actions → Semantic Release → Run workflow → Run on main branch
Or use the gh CLI:
gh workflow run semantic-release.yml
Local Version Preview
Check what the next version would be without making changes:
# Check current version
grep 'version = ' pyproject.toml | cut -d'"' -f2
# Preview next version (requires being on main branch)
uv run semantic-release --noop version --print
PyPI Trusted Publishing
This project uses PyPI's Trusted Publishing for secure, token-free releases. The GitHub Actions workflow is automatically authorized to publish to PyPI via OIDC.
No API tokens needed! The workflow authenticates using:
- Publisher: GitHub Actions
- Repository:
hotherio/cancelable - Workflow:
semantic-release.yml
Release Checklist for Maintainers
When preparing for a release:
- Ensure all PRs use conventional commit format in titles
- Verify CI passes on main branch
- Commit messages follow conventional commits specification
- Breaking changes are documented in commit body with
BREAKING CHANGE: - Push to main or merge PR - release happens automatically!
Changelog
The changelog is automatically generated from conventional commits and updated on every release. View it at CHANGELOG.md.
Documentation Deployment
Documentation is automatically built and deployed when:
- A release is published (triggered by semantic-release)
- Changes are pushed to
docs/,mkdocs.yml, or the workflow file onmain
Manual deployment commands:
# Deploy a specific version
uv run mike deploy --push --update-aliases v0.5 latest
# Set default version
uv run mike set-default latest
# List deployed versions
uv run mike list
Check documentation locally:
uv run mkdocs serve
# or with mike
uv run mike serve
Generate the licenses:
uv run pip-licenses --from=mixed --order count -f md --output-file licenses.md
uv run pip-licenses --from=mixed --order count -f csv --output-file licenses.csv
Build the new documentation:
uv run mike deploy --push --update-aliases <version> latest
uv run mike set-default latest
uv run mike list
Checking the documentation locally
uv run mike serve
Development practices
Branching & Pull-Requests
Each git branch should have the format <tag>/item_<id> with eventually a descriptive suffix.
We us a Squash & Merge approach.
Conventional Commits
We use Conventional Commits.
Format: <type>(<scope>): <subject>
<scope> is optional
Example
feat: add hat wobble
^--^ ^------------^
| |
| +-> Summary in present tense.
|
+-------> Type: chore, docs, feat, fix, refactor, style, or test.
More Examples:
feat: (new feature for the user, not a new feature for build script)fix: (bug fix for the user, not a fix to a build script)docs: (changes to the documentation)style: (formatting, missing semi colons, etc; no production code change)refactor: (refactoring production code, eg. renaming a variable)test: (adding missing tests, refactoring tests; no production code change)chore: (updating grunt tasks etc; no production code change)build: (changes in the build system)ci: (changes in the CI/CD and deployment pipelines)perf: (significant performance improvement)revert: (revert a previous change)
Contributing
We welcome contributions! Please see our Contributing Guide for details on how to get started.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hother_cancelable-0.11.1.tar.gz.
File metadata
- Download URL: hother_cancelable-0.11.1.tar.gz
- Upload date:
- Size: 123.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c9c6b1d471ebe717e3684d45c67217f8a163b78f78623b95949c13c02137b416
|
|
| MD5 |
d1869c40a2efb0d18b9a03dfd9d4606d
|
|
| BLAKE2b-256 |
2dfe687110b97e83c52cdd1e8b41bc972a8767d7791ca8cf2f6407bc5bc12c7e
|
Provenance
The following attestation bundles were made for hother_cancelable-0.11.1.tar.gz:
Publisher:
semantic-release.yml on hotherio/cancelable
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hother_cancelable-0.11.1.tar.gz -
Subject digest:
c9c6b1d471ebe717e3684d45c67217f8a163b78f78623b95949c13c02137b416 - Sigstore transparency entry: 780757949
- Sigstore integration time:
-
Permalink:
hotherio/cancelable@254b6f5d21928fc06fbb8dc654be47f15cf5d41f -
Branch / Tag:
refs/heads/main - Owner: https://github.com/hotherio
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
semantic-release.yml@254b6f5d21928fc06fbb8dc654be47f15cf5d41f -
Trigger Event:
push
-
Statement type:
File details
Details for the file hother_cancelable-0.11.1-py3-none-any.whl.
File metadata
- Download URL: hother_cancelable-0.11.1-py3-none-any.whl
- Upload date:
- Size: 61.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bb0b86dbcbedb6ddf07ffa7f0c68b9ead3db357ebcf049138060626b9e0cc717
|
|
| MD5 |
6669a4a5d0c675c4a80eae5fd3202fca
|
|
| BLAKE2b-256 |
6e5dd63f3c62b71a81011bbf1bdacc750947c16bfe1a71782717b5b7365e9585
|
Provenance
The following attestation bundles were made for hother_cancelable-0.11.1-py3-none-any.whl:
Publisher:
semantic-release.yml on hotherio/cancelable
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hother_cancelable-0.11.1-py3-none-any.whl -
Subject digest:
bb0b86dbcbedb6ddf07ffa7f0c68b9ead3db357ebcf049138060626b9e0cc717 - Sigstore transparency entry: 780757950
- Sigstore integration time:
-
Permalink:
hotherio/cancelable@254b6f5d21928fc06fbb8dc654be47f15cf5d41f -
Branch / Tag:
refs/heads/main - Owner: https://github.com/hotherio
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
semantic-release.yml@254b6f5d21928fc06fbb8dc654be47f15cf5d41f -
Trigger Event:
push
-
Statement type: