Skip to content

Commit f7725b5

Browse files
committed
Merge extensions into core package as PEP 771 extras
Signed-off-by: Sergio Herrera <627709+seherv@users.noreply.github.com>
1 parent 099d670 commit f7725b5

201 files changed

Lines changed: 994 additions & 2110 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.codecov.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,5 @@ coverage:
88

99
ignore:
1010
- dapr/proto # - Generated GRPC client
11-
- tests # - tests
11+
- tests # - tests (includes tests/ext/<name>/ for all extensions)
1212
- .venv # - environment
13-
- ext/dapr-ext-fastapi/tests # - fastapi extention tests

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: Python Dapr Client SDK Bug report (not workflow SDK)
3-
about: Report a bug in python-sdk (not dapr-ext-workflow)
3+
about: Report a bug in python-sdk (not dapr.ext.workflow)
44
title: "[BUG] <title>"
55
labels: kind/bug
66
assignees: ''

.github/ISSUE_TEMPLATE/feature_request.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: Python Dapr Client SDK Feature Request (not workflow)
3-
about: Create a Feature Request for python-sdk (not dapr-ext-workflow)
3+
about: Create a Feature Request for python-sdk (not dapr.ext.workflow)
44
title: "[FEATURE REQUEST] <title>"
55
labels: kind/enhancement
66
assignees: ''

.github/ISSUE_TEMPLATE/workflow_bug_report.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: WORKFLOW SDK Bug report
3-
about: Report a bug in dapr-ext-workflow
3+
about: Report a bug in dapr.ext.workflow
44
title: "[WORKFLOW SDK BUG] <title>"
55
labels: dapr-ext-workflow, kind/enhancement
66
assignees: ''

.github/ISSUE_TEMPLATE/workflow_feature_request.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: WORKFLOW SDK Feature Request
3-
about: Create a Feature Request for dapr-ext-workflow
3+
about: Create a Feature Request for dapr.ext.workflow
44
title: "[WORKFLOW SDK FEATURE REQUEST] <title>"
55
labels: dapr-ext-workflow, kind/enhancement
66
assignees: ''

.github/workflows/build-tag.yaml

Lines changed: 12 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ jobs:
6868
run: uv run mypy
6969
- name: Run unit-tests
7070
run: |
71-
uv run coverage run -m pytest tests ext -m "not e2e" --ignore=tests/integration --ignore=tests/examples --import-mode=importlib
71+
uv run coverage run -m pytest tests -m "not e2e" --ignore=tests/integration --ignore=tests/examples --import-mode=importlib
7272
uv run coverage xml
7373
- name: Upload test coverage
7474
uses: codecov/codecov-action@v6
@@ -93,46 +93,14 @@ jobs:
9393
TWINE_PASSWORD: ${{ secrets.PYPI_UPLOAD_PASS }}
9494
run: |
9595
python -m build --wheel
96-
twine upload dist/*
97-
- name: Build and publish dapr-ext-workflow
98-
env:
99-
TWINE_PASSWORD: ${{ secrets.PYPI_UPLOAD_PASS }}
100-
run: |
101-
cd ext/dapr-ext-workflow
102-
python -m build --wheel
103-
twine upload dist/*
104-
- name: Build and publish Dapr Flask Extension
105-
env:
106-
TWINE_PASSWORD: ${{ secrets.PYPI_UPLOAD_PASS }}
107-
run: |
108-
cd ext/flask_dapr
109-
python -m build --wheel
110-
twine upload dist/*
111-
- name: Build and publish dapr-ext-grpc
112-
env:
113-
TWINE_PASSWORD: ${{ secrets.PYPI_UPLOAD_PASS }}
114-
run: |
115-
cd ext/dapr-ext-grpc
116-
python -m build --wheel
117-
twine upload dist/*
118-
- name: Build and publish dapr-ext-fastapi
119-
env:
120-
TWINE_PASSWORD: ${{ secrets.PYPI_UPLOAD_PASS }}
121-
run: |
122-
cd ext/dapr-ext-fastapi
123-
python -m build --wheel
124-
twine upload dist/*
125-
- name: Build and publish dapr-ext-langgraph
126-
env:
127-
TWINE_PASSWORD: ${{ secrets.PYPI_UPLOAD_PASS }}
128-
run: |
129-
cd ext/dapr-ext-langgraph
130-
python -m build --wheel
131-
twine upload dist/*
132-
- name: Build and publish dapr-ext-strands
133-
env:
134-
TWINE_PASSWORD: ${{ secrets.PYPI_UPLOAD_PASS }}
135-
run: |
136-
cd ext/dapr-ext-strands
137-
python -m build --wheel
138-
twine upload dist/*
96+
# --skip-existing makes the step retry-safe: if a previous attempt
97+
# partially succeeded and the workflow is re-run, twine treats an
98+
# already-uploaded file as success instead of failing the step.
99+
twine upload --skip-existing dist/*
100+
# The previously-separate distributions (dapr-ext-fastapi, dapr-ext-grpc,
101+
# dapr-ext-langgraph, dapr-ext-strands, dapr-ext-workflow, flask-dapr)
102+
# are intentionally NOT republished. Bundling the extension code into
103+
# the `dapr` wheel means publishing a forwarding stub would leave files
104+
# under `dapr/ext/<name>/` claimed by both distributions, and `pip
105+
# uninstall <legacy>` would delete files that core dapr now provides.
106+
# See RELEASE.md and the runtime warning in dapr/__init__.py.

.github/workflows/build.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ jobs:
9696
run: uv run mypy
9797
- name: Run unit-tests
9898
run: |
99-
uv run coverage run -m pytest tests ext -m "not e2e" --ignore=tests/integration --ignore=tests/examples --import-mode=importlib
99+
uv run coverage run -m pytest tests -m "not e2e" --ignore=tests/integration --ignore=tests/examples --import-mode=importlib
100100
uv run coverage xml
101101
- name: Upload test coverage
102102
uses: codecov/codecov-action@v6

AGENTS.md

Lines changed: 50 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -14,52 +14,56 @@ License: Apache 2.0
1414
## Project structure
1515

1616
```
17-
dapr/ # Core SDK package
17+
dapr/ # Core SDK package (single PyPI dist: `pip install dapr`)
1818
├── actor/ # Actor framework (virtual actor model)
1919
├── aio/ # Async I/O modules
2020
├── clients/ # Dapr clients (gRPC and HTTP)
2121
├── common/ # Shared utilities
2222
├── conf/ # Configuration (settings, environment)
2323
├── proto/ # Auto-generated gRPC protobuf stubs (DO NOT EDIT)
2424
├── serializers/ # JSON and pluggable serializers
25-
└── version/ # Version metadata
25+
├── version/ # Version metadata
26+
└── ext/ # Extensions, installable as extras to the base package
27+
├── fastapi/ # FastAPI integration ← see dapr/ext/fastapi/AGENTS.md (`pip install dapr[fastapi]`)
28+
├── flask/ # Flask integration ← see dapr/ext/flask/AGENTS.md (`pip install dapr[flask]`)
29+
├── grpc/ # gRPC App extension ← see dapr/ext/grpc/AGENTS.md (`pip install dapr[grpc]`)
30+
├── langgraph/ # LangGraph checkpointer ← see dapr/ext/langgraph/AGENTS.md (`pip install dapr[langgraph]`)
31+
├── strands/ # Strands agent sessions ← see dapr/ext/strands/AGENTS.md (`pip install dapr[strands]`)
32+
└── workflow/ # Workflow authoring ← see dapr/ext/workflow/AGENTS.md (`pip install dapr[workflow]`)
2633
27-
ext/ # Extension packages (each is a separate PyPI package)
28-
├── dapr-ext-workflow/ # Workflow authoring ← see ext/dapr-ext-workflow/AGENTS.md
29-
├── dapr-ext-grpc/ # gRPC App extension ← see ext/dapr-ext-grpc/AGENTS.md
30-
├── dapr-ext-fastapi/ # FastAPI integration ← see ext/dapr-ext-fastapi/AGENTS.md
31-
├── dapr-ext-langgraph/ # LangGraph checkpointer ← see ext/dapr-ext-langgraph/AGENTS.md
32-
├── dapr-ext-strands/ # Strands agent sessions ← see ext/dapr-ext-strands/AGENTS.md
33-
└── flask_dapr/ # Flask integration ← see ext/flask_dapr/AGENTS.md
34+
flask_dapr/ # Deprecation shim: re-exports dapr.ext.flask with FutureWarning
3435
3536
tests/ # Unit tests (mirrors dapr/ package structure)
36-
├── examples/ # Output-based tests that run examples and check stdout
37-
├── integration/ # Programmatic SDK tests using DaprClient directly
37+
├── ext/ # Extension tests, one subdir per extension
38+
├── examples/ # Output-based tests that run examples and check stdout
39+
├── integration/ # Programmatic SDK tests using DaprClient directly
3840
examples/ # User-facing example applications ← see examples/AGENTS.md
3941
docs/ # Sphinx documentation source
4042
tools/ # Build and release scripts
4143
```
4244

4345
## Key architectural patterns
4446

45-
- **Namespace packages**: The `dapr` namespace is shared across the core SDK and extensions via `find_namespace_packages`. Extensions live in `ext/` but install into the `dapr.ext.*` namespace. Do not add `__init__.py` to namespace package roots in extensions.
47+
- **Namespace packages**: `dapr.ext` is an implicit PEP 420 namespace package. See the Gotchas section below before adding anything at that path.
4648
- **Client architecture**: `DaprGrpcClient` (primary, high-performance) and HTTP-based clients. Both implement shared interfaces.
4749
- **Actor model**: `Actor` base class, `ActorInterface` with `@actormethod` decorator, `ActorProxy`/`ActorProxyFactory` for client-side references, `ActorRuntime` for server-side hosting.
4850
- **Serialization**: Pluggable via `Serializer` base class. `DefaultJSONSerializer` is the default.
4951
- **Proto files**: Auto-generated from Dapr proto definitions. Never edit files under `dapr/proto/` directly.
5052

5153
## Extension overview
5254

53-
Each extension is a **separate PyPI package** with its own `pyproject.toml`, `setup.py`, `tests/`, and `AGENTS.md`.
55+
Extensions are bundled into the core `dapr` wheel and exposed as installable extras. Each one lives under `dapr/ext/<name>/` with its own `AGENTS.md`.
5456

55-
| Extension | Package | Purpose | Active development |
56-
|-----------|---------|---------|-------------------|
57-
| `dapr-ext-workflow` | `dapr.ext.workflow` | Durable workflow orchestration (durabletask vendored internally) | **High** — major focus area |
58-
| `dapr-ext-grpc` | `dapr.ext.grpc` | gRPC server for Dapr callbacks (methods, pub/sub, bindings, jobs) | Moderate |
59-
| `dapr-ext-fastapi` | `dapr.ext.fastapi` | FastAPI integration for pub/sub and actors | Moderate |
60-
| `flask_dapr` | `flask_dapr` | Flask integration for pub/sub and actors | Low |
61-
| `dapr-ext-langgraph` | `dapr.ext.langgraph` | LangGraph checkpoint persistence to Dapr state store | Moderate |
62-
| `dapr-ext-strands` | `dapr.ext.strands` | Strands agent session management via Dapr state store | New |
57+
| Extra | Import path | Purpose | Active development |
58+
|-------|-------------|---------|--------------------|
59+
| `dapr[workflow]` | `dapr.ext.workflow` | Durable workflow orchestration (durabletask vendored internally) | **High**, major focus area |
60+
| `dapr[grpc]` | `dapr.ext.grpc` | gRPC server for Dapr callbacks (methods, pub/sub, bindings, jobs) | Moderate |
61+
| `dapr[fastapi]` | `dapr.ext.fastapi` | FastAPI integration for pub/sub and actors | Moderate |
62+
| `dapr[flask]` | `dapr.ext.flask` | Flask integration for pub/sub and actors (legacy `flask_dapr` import path is a deprecated shim) | Low |
63+
| `dapr[langgraph]` | `dapr.ext.langgraph` | LangGraph checkpoint persistence to Dapr state store | Moderate |
64+
| `dapr[strands]` | `dapr.ext.strands` | Strands agent session management via Dapr state store | New |
65+
66+
The previously-separate distributions (`dapr-ext-*`, `flask-dapr`) are no longer published. `dapr/__init__.py` emits a `FutureWarning` if it detects a legacy install at import time; see `RELEASE.md` for the migration recipe.
6367

6468
## Examples and testing
6569

@@ -95,17 +99,20 @@ uv sync --all-packages --group dev
9599
Tests use Python's built-in `unittest` framework with `coverage`. The vendored durabletask tests use `pytest`.
96100

97101
```bash
98-
# Run all unit tests
99-
uv run python -m unittest discover -v ./tests
100-
101-
# Extension tests (run each separately)
102-
uv run python -m unittest discover -v ./ext/dapr-ext-workflow/tests
103-
uv run pytest -m "not e2e" ./ext/dapr-ext-workflow/tests/durabletask/
104-
uv run python -m unittest discover -v ./ext/dapr-ext-grpc/tests
105-
uv run python -m unittest discover -v ./ext/dapr-ext-fastapi/tests
106-
uv run python -m unittest discover -v ./ext/dapr-ext-langgraph/tests
107-
uv run python -m unittest discover -v ./ext/dapr-ext-strands/tests
108-
uv run python -m unittest discover -v ./ext/flask_dapr/tests
102+
# All unit tests. pytest is required: `unittest discover` silently skips the
103+
# pytest-style tests under tests/ext/flask and tests/ext/workflow/durabletask.
104+
uv run pytest -m "not e2e" ./tests --ignore=tests/integration --ignore=tests/examples
105+
106+
# Single extension (unittest discover works for these — no pytest-style tests):
107+
uv run python -m unittest discover -v ./tests/ext/workflow
108+
uv run python -m unittest discover -v ./tests/ext/grpc
109+
uv run python -m unittest discover -v ./tests/ext/fastapi
110+
uv run python -m unittest discover -v ./tests/ext/langgraph
111+
uv run python -m unittest discover -v ./tests/ext/strands
112+
113+
# pytest-style suites:
114+
uv run pytest -m "not e2e" ./tests/ext/workflow/durabletask/
115+
uv run pytest ./tests/ext/flask/test_shim_deprecation.py
109116

110117
# Run linting and formatting
111118
uv run ruff check --fix && uv run ruff format
@@ -143,7 +150,7 @@ uv run ruff check --fix && uv run ruff format
143150
uv run mypy
144151
```
145152

146-
MyPy is configured to check: `dapr/actor/`, `dapr/aio/`, `dapr/clients/`, `dapr/conf/`, `dapr/serializers/`, `ext/dapr-ext-grpc/`, `ext/dapr-ext-fastapi/`, `ext/flask_dapr/`, and `examples/demo_actor/`. Proto stubs (`dapr.proto.*`) have errors ignored. Configuration lives in `pyproject.toml` under `[tool.mypy]`.
153+
MyPy checks the `dapr` and `flask_dapr` packages (covering all bundled extensions under `dapr.ext.*`). Proto stubs (`dapr.proto.*`) have errors ignored, and unstubbed third-party libs (`langgraph.*`, `langchain.*`, `strands.*`, `strands_agents.*`, `grpc.aio`) are marked `ignore_missing_imports`. Configuration in `pyproject.toml` under `[tool.mypy]`.
147154

148155
## Commit and PR conventions
149156

@@ -171,14 +178,14 @@ When completing any task on this project, work through this checklist. Not every
171178

172179
### Unit tests
173180

174-
- [ ] Add or update unit tests under `tests/` (core SDK) or `ext/*/tests/` (extensions)
175-
- [ ] Tests use `unittest`follow the existing test patterns in the relevant directory
176-
- [ ] Verify tests pass: `python -m unittest discover -v ./tests` (or the relevant test directory)
181+
- [ ] Add or update unit tests under `tests/` (core SDK) or `tests/ext/<name>/` (extensions)
182+
- [ ] Tests are predominantly `unittest.TestCase`; follow the existing patterns in the relevant directory. A few pytest-style tests exist for fixture-dependent scenarios (e.g. `pytest.warns` for the `flask_dapr` shim).
183+
- [ ] Verify tests pass: `uv run pytest -m "not e2e" ./tests --ignore=tests/integration --ignore=tests/examples` (must use pytest; `unittest discover` silently skips pytest-style tests)
177184

178185
### Linting and type checking
179186

180187
- [ ] Run `uv run ruff check --fix && uv run ruff format` and fix any remaining issues
181-
- [ ] Run `uv run mypy` if you changed files covered by mypy (actor, aio, clients, conf, serializers, ext-grpc, ext-fastapi, flask_dapr)
188+
- [ ] Run `uv run mypy` if you changed files covered by mypy (the `dapr` and `flask_dapr` packages, which includes all bundled extensions under `dapr.ext.*`)
182189

183190
### Examples (integration tests)
184191

@@ -195,29 +202,28 @@ When completing any task on this project, work through this checklist. Not every
195202
### Final verification
196203

197204
- [ ] Run `uv run ruff check --fix && uv run ruff format` — linting must be clean
198-
- [ ] Run `uv run python -m unittest discover -v ./tests` — all unit tests must pass
205+
- [ ] Run `uv run pytest -m "not e2e" ./tests --ignore=tests/integration --ignore=tests/examples` — all unit tests must pass
199206
- [ ] If you touched examples: `uv run pytest tests/examples/test_<example-name>.py` to validate locally
200207
- [ ] Commits must be signed off for DCO: `git commit -s`
201208

202209
## Important files
203210

204211
| File | Purpose |
205212
|------|---------|
206-
| `pyproject.toml` | Package metadata, dependencies, ruff, mypy, and uv workspace config |
213+
| `pyproject.toml` | Package metadata, extras, dependencies, ruff, mypy, and uv workspace config |
207214
| `uv.lock` | Locked dependency versions (reproducible installs) |
208-
| `setup.py` | PyPI publish helper (handles dev version suffixing) |
209-
| `ext/*/pyproject.toml` | Extension package metadata and dependencies |
215+
| `dapr/__init__.py` | Imports `_detect_legacy_extension_dists` to warn about legacy `dapr-ext-*` / `flask-dapr` installs that collide with the bundled extension files |
210216
| `dapr/version/version.py` | SDK version string |
211217
| `tests/examples/` | Output-based tests that validate examples by checking stdout |
212218
| `tests/integration/` | Programmatic SDK tests using DaprClient directly |
213219

214220
## Gotchas
215221

216-
- **Namespace packages**: Do not add `__init__.py` to the top-level `dapr/` directory in extensions — it will break namespace package resolution.
222+
- **Namespace packages**: `dapr.ext` is a PEP 420 implicit namespace package. Do **not** create `dapr/ext/__init__.py`; that would block any future externally-published `dapr.ext.*` distribution from coexisting with the core wheel on install.
217223
- **Proto files**: Never manually edit anything under `dapr/proto/`. These are generated.
218-
- **Extension independence**: Each extension is a separate PyPI package. Core SDK changes should not break extensions; extension changes should not require core SDK changes unless intentional.
224+
- **Bundled extensions**: live under `dapr/ext/<name>/`, opted in via extras (`dapr[fastapi]`, etc.). The legacy `dapr-ext-*` and `flask-dapr` distributions are no longer published; legacy installs must be uninstalled before upgrading or `import dapr` will emit a `FutureWarning`.
219225
- **DCO signoff**: PRs will be blocked by the DCO bot if commits lack `Signed-off-by`. Always use `git commit -s`.
220226
- **Ruff version pinned**: `pyproject.toml` pins `ruff==0.14.1` in `[dependency-groups].dev`. Use `uv sync --all-packages --group dev` to get the exact version.
221227
- **Examples are tested by output matching**: Changing output format (log messages, print statements) can break `tests/examples/`. Always check expected output there when modifying user-visible output.
222228
- **Background processes in examples**: Examples that start background services (servers, subscribers) must include a cleanup step to stop them, or CI will hang.
223-
- **Workflow is the most active area**: See `ext/dapr-ext-workflow/AGENTS.md` for workflow-specific architecture and constraints.
229+
- **Workflow is the most active area**: See `dapr/ext/workflow/AGENTS.md` for workflow-specific architecture and constraints.

0 commit comments

Comments
 (0)