Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
3 changes: 1 addition & 2 deletions .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,5 @@ coverage:

ignore:
- dapr/proto # - Generated GRPC client
- tests # - tests
- tests # - tests (includes tests/ext/<name>/ for all extensions)
- .venv # - environment
- ext/dapr-ext-fastapi/tests # - fastapi extention tests
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: Python Dapr Client SDK Bug report (not workflow SDK)
about: Report a bug in python-sdk (not dapr-ext-workflow)
about: Report a bug in python-sdk (not dapr.ext.workflow)
title: "[BUG] <title>"
labels: kind/bug
assignees: ''
Expand Down
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: Python Dapr Client SDK Feature Request (not workflow)
about: Create a Feature Request for python-sdk (not dapr-ext-workflow)
about: Create a Feature Request for python-sdk (not dapr.ext.workflow)
title: "[FEATURE REQUEST] <title>"
labels: kind/enhancement
assignees: ''
Expand Down
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/workflow_bug_report.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: WORKFLOW SDK Bug report
about: Report a bug in dapr-ext-workflow
about: Report a bug in dapr.ext.workflow
title: "[WORKFLOW SDK BUG] <title>"
labels: dapr-ext-workflow, kind/enhancement
assignees: ''
Expand Down
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/workflow_feature_request.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: WORKFLOW SDK Feature Request
about: Create a Feature Request for dapr-ext-workflow
about: Create a Feature Request for dapr.ext.workflow
title: "[WORKFLOW SDK FEATURE REQUEST] <title>"
labels: dapr-ext-workflow, kind/enhancement
assignees: ''
Expand Down
56 changes: 12 additions & 44 deletions .github/workflows/build-tag.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ jobs:
run: uv run mypy
- name: Run unit-tests
run: |
uv run coverage run -m pytest tests ext -m "not e2e" --ignore=tests/integration --ignore=tests/examples --import-mode=importlib
uv run coverage run -m pytest tests -m "not e2e" --ignore=tests/integration --ignore=tests/examples --import-mode=importlib
uv run coverage xml
- name: Upload test coverage
uses: codecov/codecov-action@v6
Expand All @@ -93,46 +93,14 @@ jobs:
TWINE_PASSWORD: ${{ secrets.PYPI_UPLOAD_PASS }}
run: |
python -m build --wheel
twine upload dist/*
- name: Build and publish dapr-ext-workflow
env:
TWINE_PASSWORD: ${{ secrets.PYPI_UPLOAD_PASS }}
run: |
cd ext/dapr-ext-workflow
python -m build --wheel
twine upload dist/*
- name: Build and publish Dapr Flask Extension
env:
TWINE_PASSWORD: ${{ secrets.PYPI_UPLOAD_PASS }}
run: |
cd ext/flask_dapr
python -m build --wheel
twine upload dist/*
- name: Build and publish dapr-ext-grpc
env:
TWINE_PASSWORD: ${{ secrets.PYPI_UPLOAD_PASS }}
run: |
cd ext/dapr-ext-grpc
python -m build --wheel
twine upload dist/*
- name: Build and publish dapr-ext-fastapi
env:
TWINE_PASSWORD: ${{ secrets.PYPI_UPLOAD_PASS }}
run: |
cd ext/dapr-ext-fastapi
python -m build --wheel
twine upload dist/*
- name: Build and publish dapr-ext-langgraph
env:
TWINE_PASSWORD: ${{ secrets.PYPI_UPLOAD_PASS }}
run: |
cd ext/dapr-ext-langgraph
python -m build --wheel
twine upload dist/*
- name: Build and publish dapr-ext-strands
env:
TWINE_PASSWORD: ${{ secrets.PYPI_UPLOAD_PASS }}
run: |
cd ext/dapr-ext-strands
python -m build --wheel
twine upload dist/*
# --skip-existing makes re-runs retry-safe. PyPI does not allow
# overwriting an existing release; replacing a tagged version's
# contents requires bumping VERSION and re-tagging.
twine upload --skip-existing dist/*
# The previously-separate distributions (dapr-ext-fastapi, dapr-ext-grpc,
# dapr-ext-langgraph, dapr-ext-strands, dapr-ext-workflow, flask-dapr)
# are intentionally NOT republished. Bundling the extension code into
# the `dapr` wheel means publishing a forwarding stub would leave files
# under `dapr/ext/<name>/` claimed by both distributions, and `pip
# uninstall <legacy>` would delete files that core dapr now provides.
Comment thread
seherv marked this conversation as resolved.
# See RELEASE.md and the runtime warning in dapr/__init__.py.
2 changes: 1 addition & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ jobs:
run: uv run mypy
- name: Run unit-tests
run: |
uv run coverage run -m pytest tests ext -m "not e2e" --ignore=tests/integration --ignore=tests/examples --import-mode=importlib
uv run coverage run -m pytest tests -m "not e2e" --ignore=tests/integration --ignore=tests/examples --import-mode=importlib
Comment thread
seherv marked this conversation as resolved.
uv run coverage xml
- name: Upload test coverage
uses: codecov/codecov-action@v6
94 changes: 50 additions & 44 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,52 +14,56 @@ License: Apache 2.0
## Project structure

```
dapr/ # Core SDK package
dapr/ # Core SDK package (single PyPI dist: `pip install dapr`)
├── actor/ # Actor framework (virtual actor model)
├── aio/ # Async I/O modules
├── clients/ # Dapr clients (gRPC and HTTP)
├── common/ # Shared utilities
├── conf/ # Configuration (settings, environment)
├── proto/ # Auto-generated gRPC protobuf stubs (DO NOT EDIT)
├── serializers/ # JSON and pluggable serializers
└── version/ # Version metadata
├── version/ # Version metadata
└── ext/ # Extensions, installable as extras to the base package
├── fastapi/ # FastAPI integration ← see dapr/ext/fastapi/AGENTS.md (`pip install dapr[fastapi]`)
├── flask/ # Flask integration ← see dapr/ext/flask/AGENTS.md (`pip install dapr[flask]`)
├── grpc/ # gRPC App extension ← see dapr/ext/grpc/AGENTS.md (`pip install dapr[grpc]`)
├── langgraph/ # LangGraph checkpointer ← see dapr/ext/langgraph/AGENTS.md (`pip install dapr[langgraph]`)
├── strands/ # Strands agent sessions ← see dapr/ext/strands/AGENTS.md (`pip install dapr[strands]`)
└── workflow/ # Workflow authoring ← see dapr/ext/workflow/AGENTS.md (`pip install dapr[workflow]`)

ext/ # Extension packages (each is a separate PyPI package)
├── dapr-ext-workflow/ # Workflow authoring ← see ext/dapr-ext-workflow/AGENTS.md
├── dapr-ext-grpc/ # gRPC App extension ← see ext/dapr-ext-grpc/AGENTS.md
├── dapr-ext-fastapi/ # FastAPI integration ← see ext/dapr-ext-fastapi/AGENTS.md
├── dapr-ext-langgraph/ # LangGraph checkpointer ← see ext/dapr-ext-langgraph/AGENTS.md
├── dapr-ext-strands/ # Strands agent sessions ← see ext/dapr-ext-strands/AGENTS.md
└── flask_dapr/ # Flask integration ← see ext/flask_dapr/AGENTS.md
flask_dapr/ # Deprecation shim: re-exports dapr.ext.flask with FutureWarning
Comment thread
seherv marked this conversation as resolved.

tests/ # Unit tests (mirrors dapr/ package structure)
├── examples/ # Output-based tests that run examples and check stdout
├── integration/ # Programmatic SDK tests using DaprClient directly
├── ext/ # Extension tests, one subdir per extension
├── examples/ # Output-based tests that run examples and check stdout
├── integration/ # Programmatic SDK tests using DaprClient directly
examples/ # User-facing example applications ← see examples/AGENTS.md
docs/ # Sphinx documentation source
tools/ # Build and release scripts
```

## Key architectural patterns

- **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.
- **Namespace packages**: `dapr.ext` is an implicit PEP 420 namespace package. See the Gotchas section below before adding anything at that path.
- **Client architecture**: `DaprGrpcClient` (primary, high-performance) and HTTP-based clients. Both implement shared interfaces.
- **Actor model**: `Actor` base class, `ActorInterface` with `@actormethod` decorator, `ActorProxy`/`ActorProxyFactory` for client-side references, `ActorRuntime` for server-side hosting.
- **Serialization**: Pluggable via `Serializer` base class. `DefaultJSONSerializer` is the default.
- **Proto files**: Auto-generated from Dapr proto definitions. Never edit files under `dapr/proto/` directly.

## Extension overview

Each extension is a **separate PyPI package** with its own `pyproject.toml`, `setup.py`, `tests/`, and `AGENTS.md`.
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`.

| Extension | Package | Purpose | Active development |
|-----------|---------|---------|-------------------|
| `dapr-ext-workflow` | `dapr.ext.workflow` | Durable workflow orchestration (durabletask vendored internally) | **High** — major focus area |
| `dapr-ext-grpc` | `dapr.ext.grpc` | gRPC server for Dapr callbacks (methods, pub/sub, bindings, jobs) | Moderate |
| `dapr-ext-fastapi` | `dapr.ext.fastapi` | FastAPI integration for pub/sub and actors | Moderate |
| `flask_dapr` | `flask_dapr` | Flask integration for pub/sub and actors | Low |
| `dapr-ext-langgraph` | `dapr.ext.langgraph` | LangGraph checkpoint persistence to Dapr state store | Moderate |
| `dapr-ext-strands` | `dapr.ext.strands` | Strands agent session management via Dapr state store | New |
| Extra | Import path | Purpose | Active development |
|-------|-------------|---------|--------------------|
| `dapr[workflow]` | `dapr.ext.workflow` | Durable workflow orchestration (durabletask vendored internally) | **High**, major focus area |
| `dapr[grpc]` | `dapr.ext.grpc` | gRPC server for Dapr callbacks (methods, pub/sub, bindings, jobs) | Moderate |
| `dapr[fastapi]` | `dapr.ext.fastapi` | FastAPI integration for pub/sub and actors | Moderate |
| `dapr[flask]` | `dapr.ext.flask` | Flask integration for pub/sub and actors (legacy `flask_dapr` import path is a deprecated shim) | Low |
| `dapr[langgraph]` | `dapr.ext.langgraph` | LangGraph checkpoint persistence to Dapr state store | Moderate |
| `dapr[strands]` | `dapr.ext.strands` | Strands agent session management via Dapr state store | New |

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.

## Examples and testing

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

```bash
# Run all unit tests
uv run python -m unittest discover -v ./tests

# Extension tests (run each separately)
uv run python -m unittest discover -v ./ext/dapr-ext-workflow/tests
uv run pytest -m "not e2e" ./ext/dapr-ext-workflow/tests/durabletask/
uv run python -m unittest discover -v ./ext/dapr-ext-grpc/tests
uv run python -m unittest discover -v ./ext/dapr-ext-fastapi/tests
uv run python -m unittest discover -v ./ext/dapr-ext-langgraph/tests
uv run python -m unittest discover -v ./ext/dapr-ext-strands/tests
uv run python -m unittest discover -v ./ext/flask_dapr/tests
# All unit tests. pytest is required: `unittest discover` silently skips the
# pytest-style tests under tests/ext/flask and tests/ext/workflow/durabletask.
uv run pytest -m "not e2e" ./tests --ignore=tests/integration --ignore=tests/examples

# Single extension (unittest discover works for these — no pytest-style tests):
uv run python -m unittest discover -v ./tests/ext/workflow
uv run python -m unittest discover -v ./tests/ext/grpc
uv run python -m unittest discover -v ./tests/ext/fastapi
uv run python -m unittest discover -v ./tests/ext/langgraph
uv run python -m unittest discover -v ./tests/ext/strands

# pytest-style suites:
uv run pytest -m "not e2e" ./tests/ext/workflow/durabletask/
uv run pytest ./tests/ext/flask/test_shim_deprecation.py

# Run linting and formatting
uv run ruff check --fix && uv run ruff format
Expand Down Expand Up @@ -143,7 +150,7 @@ uv run ruff check --fix && uv run ruff format
uv run mypy
```

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]`.
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]`.

## Commit and PR conventions

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

### Unit tests

- [ ] Add or update unit tests under `tests/` (core SDK) or `ext/*/tests/` (extensions)
- [ ] Tests use `unittest` — follow the existing test patterns in the relevant directory
- [ ] Verify tests pass: `python -m unittest discover -v ./tests` (or the relevant test directory)
- [ ] Add or update unit tests under `tests/` (core SDK) or `tests/ext/<name>/` (extensions)
- [ ] 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).
- [ ] 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)

### Linting and type checking

- [ ] Run `uv run ruff check --fix && uv run ruff format` and fix any remaining issues
- [ ] Run `uv run mypy` if you changed files covered by mypy (actor, aio, clients, conf, serializers, ext-grpc, ext-fastapi, flask_dapr)
- [ ] 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.*`)

### Examples (integration tests)

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

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

## Important files

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

## Gotchas

- **Namespace packages**: Do not add `__init__.py` to the top-level `dapr/` directory in extensions — it will break namespace package resolution.
- **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.
- **Proto files**: Never manually edit anything under `dapr/proto/`. These are generated.
- **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.
- **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`.
- **DCO signoff**: PRs will be blocked by the DCO bot if commits lack `Signed-off-by`. Always use `git commit -s`.
- **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.
- **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.
- **Background processes in examples**: Examples that start background services (servers, subscribers) must include a cleanup step to stop them, or CI will hang.
- **Workflow is the most active area**: See `ext/dapr-ext-workflow/AGENTS.md` for workflow-specific architecture and constraints.
- **Workflow is the most active area**: See `dapr/ext/workflow/AGENTS.md` for workflow-specific architecture and constraints.
Loading
Loading