pydocket 0.7.0__tar.gz → 0.13.0b1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pydocket might be problematic. Click here for more details.
- pydocket-0.13.0b1/.coveragerc-memory +10 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/.github/workflows/chaos.yml +2 -0
- pydocket-0.13.0b1/.github/workflows/ci.yml +97 -0
- pydocket-0.13.0b1/.github/workflows/claude-code-review.yml +40 -0
- pydocket-0.13.0b1/.github/workflows/claude.yml +42 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/.github/workflows/publish.yml +9 -4
- {pydocket-0.7.0 → pydocket-0.13.0b1}/.gitignore +8 -8
- pydocket-0.13.0b1/CLAUDE.md +125 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/PKG-INFO +26 -4
- {pydocket-0.7.0 → pydocket-0.13.0b1}/README.md +16 -1
- {pydocket-0.7.0 → pydocket-0.13.0b1}/chaos/driver.py +36 -6
- pydocket-0.13.0b1/docs/advanced-patterns.md +690 -0
- pydocket-0.13.0b1/docs/dependencies.md +489 -0
- pydocket-0.13.0b1/docs/getting-started.md +198 -0
- pydocket-0.13.0b1/docs/production.md +407 -0
- pydocket-0.13.0b1/docs/testing.md +426 -0
- pydocket-0.13.0b1/examples/agenda_scatter.py +128 -0
- pydocket-0.13.0b1/examples/concurrency_control.py +114 -0
- pydocket-0.13.0b1/examples/fastapi_background_tasks.py +204 -0
- pydocket-0.13.0b1/examples/local_development.py +98 -0
- pydocket-0.13.0b1/examples/task_progress.py +111 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/mkdocs.yml +4 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/pyproject.toml +27 -6
- pydocket-0.13.0b1/sitecustomize.py +7 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/src/docket/__init__.py +8 -1
- pydocket-0.13.0b1/src/docket/agenda.py +202 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/src/docket/annotations.py +7 -1
- {pydocket-0.7.0 → pydocket-0.13.0b1}/src/docket/cli.py +427 -18
- {pydocket-0.7.0 → pydocket-0.13.0b1}/src/docket/dependencies.py +250 -60
- {pydocket-0.7.0 → pydocket-0.13.0b1}/src/docket/docket.py +229 -80
- pydocket-0.13.0b1/src/docket/execution.py +1212 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/src/docket/instrumentation.py +20 -1
- {pydocket-0.7.0 → pydocket-0.13.0b1}/src/docket/tasks.py +2 -2
- {pydocket-0.7.0 → pydocket-0.13.0b1}/src/docket/worker.py +298 -62
- pydocket-0.13.0b1/tests/cli/run.py +53 -0
- pydocket-0.13.0b1/tests/cli/test_clear.py +202 -0
- pydocket-0.13.0b1/tests/cli/test_module.py +10 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/tests/cli/test_parsing.py +7 -0
- pydocket-0.13.0b1/tests/cli/test_snapshot.py +342 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/tests/cli/test_striking.py +78 -108
- {pydocket-0.7.0 → pydocket-0.13.0b1}/tests/cli/test_tasks.py +32 -45
- pydocket-0.13.0b1/tests/cli/test_url_validation.py +59 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/tests/cli/test_version.py +5 -6
- pydocket-0.13.0b1/tests/cli/test_watch.py +404 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/tests/cli/test_worker.py +46 -68
- {pydocket-0.7.0 → pydocket-0.13.0b1}/tests/cli/test_workers.py +27 -29
- pydocket-0.13.0b1/tests/cli/waiting.py +133 -0
- pydocket-0.13.0b1/tests/conftest.py +210 -0
- pydocket-0.13.0b1/tests/test_agenda.py +404 -0
- pydocket-0.13.0b1/tests/test_concurrency_basic.py +31 -0
- pydocket-0.13.0b1/tests/test_concurrency_control.py +336 -0
- pydocket-0.13.0b1/tests/test_concurrency_refresh.py +196 -0
- pydocket-0.13.0b1/tests/test_dependencies.py +666 -0
- pydocket-0.13.0b1/tests/test_docket.py +210 -0
- pydocket-0.13.0b1/tests/test_execution_progress.py +835 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/tests/test_fundamentals.py +417 -7
- {pydocket-0.7.0 → pydocket-0.13.0b1}/tests/test_instrumentation.py +207 -4
- pydocket-0.13.0b1/tests/test_memory_backend.py +113 -0
- pydocket-0.13.0b1/tests/test_results.py +429 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/tests/test_striking.py +2 -0
- pydocket-0.13.0b1/tests/test_worker.py +1780 -0
- pydocket-0.13.0b1/uv.lock +2243 -0
- pydocket-0.7.0/.github/workflows/ci.yml +0 -65
- pydocket-0.7.0/docs/getting-started.md +0 -286
- pydocket-0.7.0/src/docket/execution.py +0 -436
- pydocket-0.7.0/tests/cli/conftest.py +0 -8
- pydocket-0.7.0/tests/cli/test_module.py +0 -22
- pydocket-0.7.0/tests/cli/test_snapshot.py +0 -175
- pydocket-0.7.0/tests/conftest.py +0 -176
- pydocket-0.7.0/tests/test_dependencies.py +0 -262
- pydocket-0.7.0/tests/test_docket.py +0 -14
- pydocket-0.7.0/tests/test_worker.py +0 -515
- pydocket-0.7.0/uv.lock +0 -1443
- {pydocket-0.7.0 → pydocket-0.13.0b1}/.cursor/rules/general.mdc +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/.cursor/rules/python-style.mdc +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/.github/codecov.yml +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/.github/workflows/docs.yml +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/.pre-commit-config.yaml +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/LICENSE +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/chaos/README.md +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/chaos/__init__.py +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/chaos/producer.py +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/chaos/run +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/chaos/tasks.py +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/docs/api-reference.md +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/docs/index.md +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/examples/__init__.py +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/examples/common.py +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/examples/find_and_flood.py +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/examples/self_perpetuating.py +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/src/docket/__main__.py +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/src/docket/py.typed +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/telemetry/.gitignore +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/telemetry/start +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/telemetry/stop +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/tests/__init__.py +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/tests/cli/__init__.py +0 -0
- {pydocket-0.7.0 → pydocket-0.13.0b1}/tests/test_execution.py +0 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
name: Docket CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_call:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Test Python ${{ matrix.python-version }}, ${{ matrix.backend.name }}
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
17
|
+
backend:
|
|
18
|
+
- name: "Redis 6.2, redis-py <5"
|
|
19
|
+
redis-version: "6.2"
|
|
20
|
+
redis-py-version: ">=5,<6"
|
|
21
|
+
- name: "Redis 7.4, redis-py >=5"
|
|
22
|
+
redis-version: "7.4"
|
|
23
|
+
redis-py-version: ">=5"
|
|
24
|
+
- name: "Valkey 8.0, redis-py >=5"
|
|
25
|
+
redis-version: "valkey-8.0"
|
|
26
|
+
redis-py-version: ">=5"
|
|
27
|
+
- name: "Memory (in-memory backend)"
|
|
28
|
+
redis-version: "memory"
|
|
29
|
+
redis-py-version: ">=5"
|
|
30
|
+
include:
|
|
31
|
+
- python-version: "3.10"
|
|
32
|
+
cov-threshold: 100
|
|
33
|
+
pytest-args: ""
|
|
34
|
+
# Python 3.11 coverage reporting is unstable, so use 98% threshold
|
|
35
|
+
- python-version: "3.11"
|
|
36
|
+
cov-threshold: 98
|
|
37
|
+
pytest-args: ""
|
|
38
|
+
- python-version: "3.12"
|
|
39
|
+
cov-threshold: 100
|
|
40
|
+
pytest-args: ""
|
|
41
|
+
- python-version: "3.13"
|
|
42
|
+
cov-threshold: 100
|
|
43
|
+
pytest-args: ""
|
|
44
|
+
- python-version: "3.14"
|
|
45
|
+
cov-threshold: 100
|
|
46
|
+
pytest-args: ""
|
|
47
|
+
# Memory backend: CLI tests are skipped via pytest skip markers because
|
|
48
|
+
# CLI rejects memory:// URLs. Use separate coverage config to exclude CLI.
|
|
49
|
+
- backend:
|
|
50
|
+
name: "Memory (in-memory backend)"
|
|
51
|
+
redis-version: "memory"
|
|
52
|
+
redis-py-version: ">=5"
|
|
53
|
+
cov-threshold: 98 # CLI tests are excluded from coverage and some lines are only covered by CLI tests
|
|
54
|
+
pytest-args: "--cov-config=.coveragerc-memory"
|
|
55
|
+
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/checkout@v4
|
|
58
|
+
|
|
59
|
+
- name: Install uv and set Python version
|
|
60
|
+
uses: astral-sh/setup-uv@v5
|
|
61
|
+
with:
|
|
62
|
+
python-version: ${{ matrix.python-version }}
|
|
63
|
+
enable-cache: true
|
|
64
|
+
cache-dependency-glob: "pyproject.toml"
|
|
65
|
+
|
|
66
|
+
- name: Install dependencies
|
|
67
|
+
run: uv sync --upgrade-package 'redis${{ matrix.backend.redis-py-version }}'
|
|
68
|
+
|
|
69
|
+
- name: Run tests
|
|
70
|
+
env:
|
|
71
|
+
REDIS_VERSION: ${{ matrix.backend.redis-version }}
|
|
72
|
+
run: uv run pytest --cov-branch --cov-fail-under=${{ matrix.cov-threshold }} --cov-report=xml --cov-report=term-missing:skip-covered ${{ matrix.pytest-args }}
|
|
73
|
+
|
|
74
|
+
- name: Upload coverage reports to Codecov
|
|
75
|
+
uses: codecov/codecov-action@v5
|
|
76
|
+
with:
|
|
77
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
78
|
+
flags: python-${{ matrix.python-version }}
|
|
79
|
+
|
|
80
|
+
prek:
|
|
81
|
+
name: Prek checks
|
|
82
|
+
runs-on: ubuntu-latest
|
|
83
|
+
steps:
|
|
84
|
+
- uses: actions/checkout@v4
|
|
85
|
+
|
|
86
|
+
- name: Install uv and set Python version
|
|
87
|
+
uses: astral-sh/setup-uv@v5
|
|
88
|
+
with:
|
|
89
|
+
python-version: "3.10"
|
|
90
|
+
enable-cache: true
|
|
91
|
+
cache-dependency-glob: "pyproject.toml"
|
|
92
|
+
|
|
93
|
+
- name: Install dependencies
|
|
94
|
+
run: uv sync
|
|
95
|
+
|
|
96
|
+
- name: Run prek
|
|
97
|
+
uses: j178/prek-action@v1
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: Claude Code Review
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [opened, synchronize]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
claude-review:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
pull-requests: read
|
|
13
|
+
issues: read
|
|
14
|
+
id-token: write
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout repository
|
|
18
|
+
uses: actions/checkout@v4
|
|
19
|
+
with:
|
|
20
|
+
fetch-depth: 1
|
|
21
|
+
|
|
22
|
+
- name: Run Claude Code Review
|
|
23
|
+
id: claude-review
|
|
24
|
+
uses: anthropics/claude-code-action@beta
|
|
25
|
+
with:
|
|
26
|
+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
27
|
+
model: "claude-opus-4-1-20250805"
|
|
28
|
+
|
|
29
|
+
# Direct prompt for automated review (no @claude mention needed)
|
|
30
|
+
direct_prompt: |
|
|
31
|
+
Please review this pull request and provide feedback on:
|
|
32
|
+
- Code quality and best practices
|
|
33
|
+
- Potential bugs or issues
|
|
34
|
+
- Performance considerations
|
|
35
|
+
- Security concerns
|
|
36
|
+
- Test coverage, which must be maintained at 100% for this project
|
|
37
|
+
|
|
38
|
+
Be constructive and helpful in your feedback.
|
|
39
|
+
|
|
40
|
+
use_sticky_comment: true
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Claude Code
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
issue_comment:
|
|
5
|
+
types: [created]
|
|
6
|
+
pull_request_review_comment:
|
|
7
|
+
types: [created]
|
|
8
|
+
issues:
|
|
9
|
+
types: [opened, assigned]
|
|
10
|
+
pull_request_review:
|
|
11
|
+
types: [submitted]
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
claude:
|
|
15
|
+
if: |
|
|
16
|
+
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
|
|
17
|
+
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
|
|
18
|
+
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
|
|
19
|
+
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
permissions:
|
|
22
|
+
contents: read
|
|
23
|
+
pull-requests: read
|
|
24
|
+
issues: read
|
|
25
|
+
id-token: write
|
|
26
|
+
actions: read # Required for Claude to read CI results on PRs
|
|
27
|
+
steps:
|
|
28
|
+
- name: Checkout repository
|
|
29
|
+
uses: actions/checkout@v4
|
|
30
|
+
with:
|
|
31
|
+
fetch-depth: 1
|
|
32
|
+
|
|
33
|
+
- name: Run Claude Code
|
|
34
|
+
id: claude
|
|
35
|
+
uses: anthropics/claude-code-action@beta
|
|
36
|
+
with:
|
|
37
|
+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
38
|
+
|
|
39
|
+
additional_permissions: |
|
|
40
|
+
actions: read
|
|
41
|
+
|
|
42
|
+
model: "claude-opus-4-1-20250805"
|
|
@@ -13,8 +13,11 @@ jobs:
|
|
|
13
13
|
name: Build and publish to PyPI
|
|
14
14
|
runs-on: ubuntu-latest
|
|
15
15
|
needs: ci
|
|
16
|
+
environment:
|
|
17
|
+
name: pypi
|
|
18
|
+
url: https://pypi.org/p/pydocket
|
|
16
19
|
permissions:
|
|
17
|
-
id-token: write
|
|
20
|
+
id-token: write # Required for trusted publishing and PEP 740 attestations
|
|
18
21
|
contents: read
|
|
19
22
|
|
|
20
23
|
steps:
|
|
@@ -31,10 +34,12 @@ jobs:
|
|
|
31
34
|
cache-dependency-glob: "pyproject.toml"
|
|
32
35
|
|
|
33
36
|
- name: Install build dependencies
|
|
34
|
-
run: uv pip install
|
|
37
|
+
run: uv pip install hatchling hatch-vcs
|
|
35
38
|
|
|
36
39
|
- name: Build package
|
|
37
40
|
run: uv build
|
|
38
41
|
|
|
39
|
-
- name: Publish to PyPI
|
|
40
|
-
|
|
42
|
+
- name: Publish to PyPI with PEP 740 attestations
|
|
43
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
44
|
+
with:
|
|
45
|
+
packages-dir: dist/
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
__pycache__/
|
|
1
|
+
*.egg-info
|
|
3
2
|
*.py[oc]
|
|
3
|
+
.coverage
|
|
4
|
+
.envrc
|
|
5
|
+
.python-version
|
|
6
|
+
.venv
|
|
7
|
+
.worktrees/
|
|
8
|
+
__pycache__/
|
|
4
9
|
build/
|
|
5
10
|
dist/
|
|
6
11
|
wheels/
|
|
7
|
-
*.egg-info
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
.venv
|
|
11
|
-
.coverage
|
|
12
|
-
.envrc
|
|
13
|
-
.python-version
|
|
13
|
+
.coverage.*
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
**Docket** (`pydocket` on PyPI) is a distributed background task system for Python functions with Redis-backed persistence. It enables scheduling both immediate and future work with comprehensive dependency injection, retry mechanisms, and fault tolerance.
|
|
8
|
+
|
|
9
|
+
**Key Requirements**: Python 3.10+, Redis 6.2+ or Valkey 8.0+
|
|
10
|
+
|
|
11
|
+
## Development Commands
|
|
12
|
+
|
|
13
|
+
### Testing
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Run full test suite with coverage and parallel execution
|
|
17
|
+
pytest
|
|
18
|
+
|
|
19
|
+
# Run specific test
|
|
20
|
+
pytest tests/test_docket.py::test_specific_function
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The project REQUIRES 100% test coverage
|
|
25
|
+
|
|
26
|
+
### Code Quality
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Lint and format code
|
|
30
|
+
ruff check
|
|
31
|
+
ruff format
|
|
32
|
+
|
|
33
|
+
# Type checking
|
|
34
|
+
pyright
|
|
35
|
+
pyright tests
|
|
36
|
+
|
|
37
|
+
# Run all prek hooks
|
|
38
|
+
uv run prek run --all-files
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Development Setup
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Install development dependencies
|
|
45
|
+
uv sync --group dev
|
|
46
|
+
|
|
47
|
+
# Install prek hooks
|
|
48
|
+
uv run prek install
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Git Workflow
|
|
52
|
+
|
|
53
|
+
- This project uses Github for issue tracking
|
|
54
|
+
- This project can use git worktrees under .worktrees/
|
|
55
|
+
|
|
56
|
+
## Core Architecture
|
|
57
|
+
|
|
58
|
+
### Key Classes
|
|
59
|
+
|
|
60
|
+
- **`Docket`** (`src/docket/docket.py`): Central task registry and scheduler
|
|
61
|
+
- `add()`: Schedule tasks for execution
|
|
62
|
+
- `replace()`: Replace existing scheduled tasks
|
|
63
|
+
- `cancel()`: Cancel pending tasks
|
|
64
|
+
- `strike()`/`restore()`: Conditionally block/unblock tasks
|
|
65
|
+
- `snapshot()`: Get current state for observability
|
|
66
|
+
|
|
67
|
+
- **`Worker`** (`src/docket/worker.py`): Task execution engine
|
|
68
|
+
- `run_forever()`/`run_until_finished()`: Main execution loops
|
|
69
|
+
- Handles concurrency, retries, and dependency injection
|
|
70
|
+
- Maintains heartbeat for liveness tracking
|
|
71
|
+
|
|
72
|
+
- **`Execution`** (`src/docket/execution.py`): Task execution context with metadata
|
|
73
|
+
|
|
74
|
+
### Dependencies System (`src/docket/dependencies.py`)
|
|
75
|
+
|
|
76
|
+
Rich dependency injection supporting:
|
|
77
|
+
|
|
78
|
+
- Context access: `CurrentDocket`, `CurrentWorker`, `CurrentExecution`
|
|
79
|
+
- Retry strategies: `Retry`, `ExponentialRetry`
|
|
80
|
+
- Special behaviors: `Perpetual` (self-rescheduling), `Timeout`
|
|
81
|
+
- Custom injection: `Depends()`
|
|
82
|
+
- Contextual logging: `TaskLogger`
|
|
83
|
+
|
|
84
|
+
### Redis Data Model
|
|
85
|
+
|
|
86
|
+
- **Streams**: `{docket}:stream` (ready tasks), `{docket}:strikes` (commands)
|
|
87
|
+
- **Sorted Sets**: `{docket}:queue` (scheduled tasks), `{docket}:workers` (heartbeats)
|
|
88
|
+
- **Hashes**: `{docket}:{key}` (parked task data)
|
|
89
|
+
- **Sets**: `{docket}:worker-tasks:{worker}` (worker capabilities)
|
|
90
|
+
|
|
91
|
+
### Task Lifecycle
|
|
92
|
+
|
|
93
|
+
1. Registration with `Docket.register()` or `@docket.task`
|
|
94
|
+
2. Scheduling: immediate → Redis stream, future → Redis sorted set
|
|
95
|
+
3. Worker processing: scheduler moves due tasks, workers consume via consumer groups
|
|
96
|
+
4. Execution: dependency injection, retry logic, acknowledgment
|
|
97
|
+
|
|
98
|
+
## Project Structure
|
|
99
|
+
|
|
100
|
+
### Source Code
|
|
101
|
+
|
|
102
|
+
- `src/docket/` - Main package
|
|
103
|
+
- `__init__.py` - Public API exports
|
|
104
|
+
- `docket.py` - Core Docket class
|
|
105
|
+
- `worker.py` - Worker implementation
|
|
106
|
+
- `execution.py` - Task execution context
|
|
107
|
+
- `dependencies.py` - Dependency injection system
|
|
108
|
+
- `tasks.py` - Built-in utility tasks
|
|
109
|
+
- `cli.py` - Command-line interface
|
|
110
|
+
|
|
111
|
+
### Testing and Examples
|
|
112
|
+
|
|
113
|
+
- `tests/` - Comprehensive test suite
|
|
114
|
+
- `examples/` - Usage examples
|
|
115
|
+
- `chaos/` - Chaos testing framework
|
|
116
|
+
|
|
117
|
+
## CLI Usage
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
# Run a worker
|
|
121
|
+
docket worker --url redis://localhost:6379/0 --tasks your.module --concurrency 4
|
|
122
|
+
|
|
123
|
+
# See all commands
|
|
124
|
+
docket --help
|
|
125
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pydocket
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.0b1
|
|
4
4
|
Summary: A distributed background task system for Python functions
|
|
5
5
|
Project-URL: Homepage, https://github.com/chrisguidry/docket
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/chrisguidry/docket/issues
|
|
@@ -19,18 +19,25 @@ Classifier: Development Status :: 4 - Beta
|
|
|
19
19
|
Classifier: License :: OSI Approved :: MIT License
|
|
20
20
|
Classifier: Operating System :: OS Independent
|
|
21
21
|
Classifier: Programming Language :: Python :: 3
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
24
|
Classifier: Programming Language :: Python :: 3.12
|
|
23
25
|
Classifier: Programming Language :: Python :: 3.13
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
27
|
Classifier: Typing :: Typed
|
|
25
|
-
Requires-Python: >=3.
|
|
28
|
+
Requires-Python: >=3.10
|
|
26
29
|
Requires-Dist: cloudpickle>=3.1.1
|
|
30
|
+
Requires-Dist: exceptiongroup>=1.2.0; python_version < '3.11'
|
|
31
|
+
Requires-Dist: fakeredis[lua]>=2.32.1
|
|
27
32
|
Requires-Dist: opentelemetry-api>=1.30.0
|
|
28
33
|
Requires-Dist: opentelemetry-exporter-prometheus>=0.51b0
|
|
29
34
|
Requires-Dist: prometheus-client>=0.21.1
|
|
35
|
+
Requires-Dist: py-key-value-aio[memory,redis]>=0.2.8
|
|
30
36
|
Requires-Dist: python-json-logger>=3.2.1
|
|
31
|
-
Requires-Dist: redis>=
|
|
37
|
+
Requires-Dist: redis>=5
|
|
32
38
|
Requires-Dist: rich>=13.9.4
|
|
33
39
|
Requires-Dist: typer>=0.15.1
|
|
40
|
+
Requires-Dist: typing-extensions>=4.12.0
|
|
34
41
|
Requires-Dist: uuid7>=0.1.0
|
|
35
42
|
Description-Content-Type: text/markdown
|
|
36
43
|
|
|
@@ -69,6 +76,7 @@ from docket import Docket, Worker
|
|
|
69
76
|
|
|
70
77
|
async with Docket() as docket:
|
|
71
78
|
async with Worker(docket) as worker:
|
|
79
|
+
worker.register(greet)
|
|
72
80
|
await worker.run_until_finished()
|
|
73
81
|
```
|
|
74
82
|
|
|
@@ -93,10 +101,12 @@ reference](https://chrisguidry.github.io/docket/api-reference/).
|
|
|
93
101
|
|
|
94
102
|
🧩 Fully type-complete and type-aware for your background task functions
|
|
95
103
|
|
|
104
|
+
💉 Dependency injection like FastAPI, Typer, and FastMCP for reusable resources
|
|
105
|
+
|
|
96
106
|
## Installing `docket`
|
|
97
107
|
|
|
98
108
|
Docket is [available on PyPI](https://pypi.org/project/pydocket/) under the package name
|
|
99
|
-
`pydocket`. It targets Python 3.
|
|
109
|
+
`pydocket`. It targets Python 3.10 or above.
|
|
100
110
|
|
|
101
111
|
With [`uv`](https://docs.astral.sh/uv/):
|
|
102
112
|
|
|
@@ -117,6 +127,18 @@ pip install pydocket
|
|
|
117
127
|
Docket requires a [Redis](http://redis.io/) server with Streams support (which was
|
|
118
128
|
introduced in Redis 5.0.0). Docket is tested with Redis 6 and 7.
|
|
119
129
|
|
|
130
|
+
For testing without Redis, Docket includes [fakeredis](https://github.com/cunla/fakeredis-py) for in-memory operation:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from docket import Docket
|
|
134
|
+
|
|
135
|
+
async with Docket(name="my-docket", url="memory://my-docket") as docket:
|
|
136
|
+
# Use docket normally - all operations are in-memory
|
|
137
|
+
...
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
See [Testing with Docket](https://chrisguidry.github.io/docket/testing/#using-in-memory-backend-no-redis-required) for more details.
|
|
141
|
+
|
|
120
142
|
# Hacking on `docket`
|
|
121
143
|
|
|
122
144
|
We use [`uv`](https://docs.astral.sh/uv/) for project management, so getting set up
|
|
@@ -33,6 +33,7 @@ from docket import Docket, Worker
|
|
|
33
33
|
|
|
34
34
|
async with Docket() as docket:
|
|
35
35
|
async with Worker(docket) as worker:
|
|
36
|
+
worker.register(greet)
|
|
36
37
|
await worker.run_until_finished()
|
|
37
38
|
```
|
|
38
39
|
|
|
@@ -57,10 +58,12 @@ reference](https://chrisguidry.github.io/docket/api-reference/).
|
|
|
57
58
|
|
|
58
59
|
🧩 Fully type-complete and type-aware for your background task functions
|
|
59
60
|
|
|
61
|
+
💉 Dependency injection like FastAPI, Typer, and FastMCP for reusable resources
|
|
62
|
+
|
|
60
63
|
## Installing `docket`
|
|
61
64
|
|
|
62
65
|
Docket is [available on PyPI](https://pypi.org/project/pydocket/) under the package name
|
|
63
|
-
`pydocket`. It targets Python 3.
|
|
66
|
+
`pydocket`. It targets Python 3.10 or above.
|
|
64
67
|
|
|
65
68
|
With [`uv`](https://docs.astral.sh/uv/):
|
|
66
69
|
|
|
@@ -81,6 +84,18 @@ pip install pydocket
|
|
|
81
84
|
Docket requires a [Redis](http://redis.io/) server with Streams support (which was
|
|
82
85
|
introduced in Redis 5.0.0). Docket is tested with Redis 6 and 7.
|
|
83
86
|
|
|
87
|
+
For testing without Redis, Docket includes [fakeredis](https://github.com/cunla/fakeredis-py) for in-memory operation:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from docket import Docket
|
|
91
|
+
|
|
92
|
+
async with Docket(name="my-docket", url="memory://my-docket") as docket:
|
|
93
|
+
# Use docket normally - all operations are in-memory
|
|
94
|
+
...
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
See [Testing with Docket](https://chrisguidry.github.io/docket/testing/#using-in-memory-backend-no-redis-required) for more details.
|
|
98
|
+
|
|
84
99
|
# Hacking on `docket`
|
|
85
100
|
|
|
86
101
|
We use [`uv`](https://docs.astral.sh/uv/) for project management, so getting set up
|
|
@@ -73,7 +73,20 @@ async def main(
|
|
|
73
73
|
tasks: int = 20000,
|
|
74
74
|
producers: int = 5,
|
|
75
75
|
workers: int = 10,
|
|
76
|
+
base_version: str | None = None,
|
|
76
77
|
):
|
|
78
|
+
if base_version is None:
|
|
79
|
+
process = await asyncio.create_subprocess_exec(
|
|
80
|
+
"git",
|
|
81
|
+
"describe",
|
|
82
|
+
"--tags",
|
|
83
|
+
"--abbrev=0",
|
|
84
|
+
stdout=subprocess.PIPE,
|
|
85
|
+
stderr=subprocess.PIPE,
|
|
86
|
+
)
|
|
87
|
+
stdout, _ = await process.communicate()
|
|
88
|
+
base_version = stdout.decode("utf-8").strip()
|
|
89
|
+
|
|
77
90
|
async with (
|
|
78
91
|
run_redis("7.4.2") as (redis_url, redis_container),
|
|
79
92
|
Docket(
|
|
@@ -105,11 +118,17 @@ async def main(
|
|
|
105
118
|
)
|
|
106
119
|
|
|
107
120
|
async def spawn_producer() -> Process:
|
|
121
|
+
docket_version = base_version if random.random() < 0.5 else "main"
|
|
122
|
+
base_command = ["uv", "run"]
|
|
123
|
+
if docket_version != "main":
|
|
124
|
+
logger.info("Using pydocket %s for producer", docket_version)
|
|
125
|
+
base_command.extend(["--with", f"pydocket=={docket_version}"])
|
|
126
|
+
else:
|
|
127
|
+
logger.info("Using main pydocket for producer")
|
|
128
|
+
|
|
129
|
+
command = [*base_command, "-m", "chaos.producer", str(tasks_per_producer)]
|
|
108
130
|
return await asyncio.create_subprocess_exec(
|
|
109
|
-
*
|
|
110
|
-
"-m",
|
|
111
|
-
"chaos.producer",
|
|
112
|
-
str(tasks_per_producer),
|
|
131
|
+
*command,
|
|
113
132
|
env=environment | {"OTEL_SERVICE_NAME": "chaos-producer"},
|
|
114
133
|
stdout=subprocess.DEVNULL,
|
|
115
134
|
stderr=subprocess.DEVNULL,
|
|
@@ -122,8 +141,16 @@ async def main(
|
|
|
122
141
|
logger.info("Spawning %d workers...", workers)
|
|
123
142
|
|
|
124
143
|
async def spawn_worker() -> Process:
|
|
125
|
-
|
|
126
|
-
|
|
144
|
+
docket_version = base_version if random.random() < 0.5 else "main"
|
|
145
|
+
base_command = ["uv", "run"]
|
|
146
|
+
if docket_version != "main":
|
|
147
|
+
logger.info("Using pydocket %s for worker", docket_version)
|
|
148
|
+
base_command.extend(["--with", f"pydocket=={docket_version}"])
|
|
149
|
+
else:
|
|
150
|
+
logger.info("Using main pydocket for worker")
|
|
151
|
+
|
|
152
|
+
command = [
|
|
153
|
+
*base_command,
|
|
127
154
|
"-m",
|
|
128
155
|
"docket",
|
|
129
156
|
"worker",
|
|
@@ -135,6 +162,9 @@ async def main(
|
|
|
135
162
|
"chaos.tasks:chaos_tasks",
|
|
136
163
|
"--redelivery-timeout",
|
|
137
164
|
"5s",
|
|
165
|
+
]
|
|
166
|
+
return await asyncio.create_subprocess_exec(
|
|
167
|
+
*command,
|
|
138
168
|
env=environment | {"OTEL_SERVICE_NAME": "chaos-worker"},
|
|
139
169
|
stdout=subprocess.DEVNULL,
|
|
140
170
|
stderr=subprocess.DEVNULL,
|