Skip to content

Commit 65fc9ae

Browse files
committed
Remove Mechanical Markdown
Signed-off-by: Sergio Herrera <627709+seherv@users.noreply.github.com>
1 parent ab2a3f4 commit 65fc9ae

29 files changed

+1066
-193
lines changed

AGENTS.md

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,14 @@ Each extension is a **separate PyPI package** with its own `setup.cfg`, `setup.p
6161

6262
## Examples (integration test suite)
6363

64-
The `examples/` directory serves as both user-facing documentation and the project's integration test suite. Examples are validated in CI using [mechanical-markdown](https://pypi.org/project/mechanical-markdown/), which executes bash code blocks from README files and asserts expected output.
64+
The `examples/` directory serves as both user-facing documentation and the project's integration test suite. Examples are validated by pytest-based integration tests in `tests/integration/`.
6565

66-
**See `examples/AGENTS.md`** for the full guide on example structure, validation, mechanical-markdown STEP blocks, and how to add new examples.
66+
**See `examples/AGENTS.md`** for the full guide on example structure and how to add new examples.
6767

6868
Quick reference:
6969
```bash
70-
tox -e examples # Run all examples (needs Dapr runtime)
71-
tox -e example-component -- state_store # Run a single example
72-
cd examples && ./validate.sh state_store # Run directly
70+
tox -e integration # Run all examples (needs Dapr runtime)
71+
tox -e integration -- test_state_store.py # Run a single example
7372
```
7473

7574
## Python version support
@@ -107,8 +106,8 @@ tox -e ruff
107106
# Run type checking
108107
tox -e type
109108

110-
# Validate examples (requires Dapr runtime)
111-
tox -e examples
109+
# Run integration tests / validate examples (requires Dapr runtime)
110+
tox -e integration
112111
```
113112

114113
To run tests directly without tox:
@@ -190,9 +189,8 @@ When completing any task on this project, work through this checklist. Not every
190189
### Examples (integration tests)
191190

192191
- [ ] If you added a new user-facing feature or building block, add or update an example in `examples/`
193-
- [ ] Ensure the example README has `<!-- STEP -->` blocks with `expected_stdout_lines` so it is validated in CI
194-
- [ ] If you added a new example, register it in `tox.ini` under `[testenv:examples]`
195-
- [ ] If you changed output format of existing functionality, update `expected_stdout_lines` in affected example READMEs
192+
- [ ] Add a corresponding pytest integration test in `tests/integration/`
193+
- [ ] If you changed output format of existing functionality, update expected output in the affected integration tests
196194
- [ ] See `examples/AGENTS.md` for full details on writing examples
197195

198196
### Documentation
@@ -204,7 +202,7 @@ When completing any task on this project, work through this checklist. Not every
204202

205203
- [ ] Run `tox -e ruff` — linting must be clean
206204
- [ ] Run `tox -e py311` (or your Python version) — all unit tests must pass
207-
- [ ] If you touched examples: `tox -e example-component -- <example-name>` to validate locally
205+
- [ ] If you touched examples: `tox -e integration -- test_<example-name>.py` to validate locally
208206
- [ ] Commits must be signed off for DCO: `git commit -s`
209207

210208
## Important files
@@ -219,7 +217,7 @@ When completing any task on this project, work through this checklist. Not every
219217
| `dev-requirements.txt` | Development/test dependencies |
220218
| `dapr/version/__init__.py` | SDK version string |
221219
| `ext/*/setup.cfg` | Extension package metadata and dependencies |
222-
| `examples/validate.sh` | Entry point for mechanical-markdown example validation |
220+
| `tests/integration/` | Pytest-based integration tests that validate examples |
223221

224222
## Gotchas
225223

@@ -228,6 +226,6 @@ When completing any task on this project, work through this checklist. Not every
228226
- **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.
229227
- **DCO signoff**: PRs will be blocked by the DCO bot if commits lack `Signed-off-by`. Always use `git commit -s`.
230228
- **Ruff version pinned**: Dev requirements pin `ruff === 0.14.1`. Use this exact version to match CI.
231-
- **Examples are integration tests**: Changing output format (log messages, print statements) can break example validation. Always check `expected_stdout_lines` in example READMEs when modifying user-visible output.
229+
- **Examples are integration tests**: Changing output format (log messages, print statements) can break integration tests. Always check expected output in `tests/integration/` when modifying user-visible output.
232230
- **Background processes in examples**: Examples that start background services (servers, subscribers) must include a cleanup step to stop them, or CI will hang.
233231
- **Workflow is the most active area**: See `ext/dapr-ext-workflow/AGENTS.md` for workflow-specific architecture and constraints.

CLAUDE.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,11 @@
11
@AGENTS.md
2+
3+
Use pathlib instead of os.path.
4+
Use modern Python (3.10+) features.
5+
Make all code strongly typed.
6+
Keep conditional nesting to a minimum, and use guard clauses when possible.
7+
Aim for medium "visual complexity": use intermediate variables to store results of nested/complex function calls, but don't create a new variable for everything.
8+
Avoid comments unless there is an unusual gotcha, a complex algorithm or anything an experienced code reviewer needs to be aware of. Focus on making better Google-style docstrings instead.
9+
10+
The user is not always right. Be skeptical and do not blindly comply if something doesn't make sense.
11+
Code should be production-ready.

README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,19 +121,17 @@ tox -e py311
121121
tox -e type
122122
```
123123

124-
8. Run examples
124+
8. Run integration tests (validates the examples)
125125

126126
```bash
127-
tox -e examples
127+
tox -e integration
128128
```
129129

130-
[Dapr Mechanical Markdown](https://github.com/dapr/mechanical-markdown) is used to test the examples.
131-
132130
If you need to run the examples against a pre-released version of the runtime, you can use the following command:
133131
- Get your daprd runtime binary from [here](https://github.com/dapr/dapr/releases) for your platform.
134132
- Copy the binary to your dapr home folder at $HOME/.dapr/bin/daprd.
135133
Or using dapr cli directly: `dapr init --runtime-version <release version>`
136-
- Now you can run the example with `tox -e examples`.
134+
- Now you can run the examples with `tox -e integration`.
137135

138136

139137
## Documentation

examples/AGENTS.md

Lines changed: 17 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,22 @@
11
# AGENTS.md — Dapr Python SDK Examples
22

3-
The `examples/` directory serves as both **user-facing documentation** and the project's **integration test suite**. Each example is a self-contained application validated automatically in CI using [mechanical-markdown](https://pypi.org/project/mechanical-markdown/), which executes bash code blocks embedded in README files and asserts expected output.
3+
The `examples/` directory serves as both **user-facing documentation** and the project's **integration test suite**. Each example is a self-contained application validated by pytest-based integration tests in `tests/integration/`.
44

55
## How validation works
66

7-
1. `examples/validate.sh` is the entry point — it `cd`s into an example directory and runs `mm.py -l README.md`
8-
2. `mm.py` (mechanical-markdown) parses `<!-- STEP -->` HTML comment blocks in the README
9-
3. Each STEP block wraps a fenced bash code block that gets executed
10-
4. stdout/stderr is captured and checked against `expected_stdout_lines` / `expected_stderr_lines`
11-
5. Validation fails if any expected output line is missing
7+
1. Each example has a corresponding test file in `tests/integration/` (e.g., `test_state_store.py`)
8+
2. Tests use a `DaprRunner` helper (defined in `conftest.py`) that wraps `dapr run` commands
9+
3. `DaprRunner.run()` executes a command and captures stdout; `DaprRunner.start()`/`stop()` manage background services
10+
4. Tests assert that expected output lines appear in the captured output
1211

1312
Run examples locally (requires a running Dapr runtime via `dapr init`):
1413

1514
```bash
1615
# All examples
17-
tox -e examples
16+
tox -e integration
1817

1918
# Single example
20-
tox -e example-component -- state_store
21-
22-
# Or directly
23-
cd examples && ./validate.sh state_store
19+
tox -e integration -- test_state_store.py
2420
```
2521

2622
In CI (`validate_examples.yaml`), examples run on all supported Python versions (3.10-3.14) on Ubuntu with a full Dapr runtime including Docker, Redis, and (for LLM examples) Ollama.
@@ -31,7 +27,7 @@ Each example follows this pattern:
3127

3228
```
3329
examples/<example-name>/
34-
├── README.md # Documentation + mechanical-markdown STEP blocks (REQUIRED)
30+
├── README.md # Documentation (REQUIRED)
3531
├── *.py # Python application files
3632
├── requirements.txt # Dependencies (optional — many examples rely on the installed SDK)
3733
├── components/ # Dapr component YAML configs (if needed)
@@ -46,53 +42,6 @@ Common Python file naming conventions:
4642
- Client/caller side: `*-caller.py`, `publisher.py`, `*_client.py`
4743
- Standalone: `state_store.py`, `crypto.py`, etc.
4844

49-
## Mechanical-markdown STEP block format
50-
51-
STEP blocks are HTML comments wrapping fenced bash code in the README:
52-
53-
````markdown
54-
<!-- STEP
55-
name: Run the example
56-
expected_stdout_lines:
57-
- '== APP == Got state: value'
58-
- '== APP == State deleted'
59-
background: false
60-
sleep: 5
61-
timeout_seconds: 30
62-
output_match_mode: substring
63-
match_order: none
64-
-->
65-
66-
```bash
67-
dapr run --app-id myapp --resources-path ./components/ python3 example.py
68-
```
69-
70-
<!-- END_STEP -->
71-
````
72-
73-
### STEP block attributes
74-
75-
| Attribute | Description |
76-
|-----------|-------------|
77-
| `name` | Descriptive name for the step |
78-
| `expected_stdout_lines` | List of strings that must appear in stdout |
79-
| `expected_stderr_lines` | List of strings that must appear in stderr |
80-
| `background` | `true` to run in background (for long-running services) |
81-
| `sleep` | Seconds to wait after starting before moving to the next step |
82-
| `timeout_seconds` | Max seconds before the step is killed |
83-
| `output_match_mode` | `substring` for partial matching (default is exact) |
84-
| `match_order` | `none` if output lines can appear in any order |
85-
86-
### Tips for writing STEP blocks
87-
88-
- Use `background: true` with `sleep:` for services that need to stay running (servers, subscribers)
89-
- Use `timeout_seconds:` to prevent CI hangs on broken examples
90-
- Use `output_match_mode: substring` when output contains timestamps or dynamic content
91-
- Use `match_order: none` when multiple concurrent operations produce unpredictable ordering
92-
- Always include a cleanup step (e.g., `dapr stop --app-id ...`) when using background processes
93-
- Make `expected_stdout_lines` specific enough to validate correctness, but not so brittle they break on cosmetic changes
94-
- Dapr prefixes app output with `== APP ==` — use this in expected lines
95-
9645
## Dapr component YAML format
9746

9847
Components in `components/` directories follow the standard Dapr resource format:
@@ -182,69 +131,18 @@ The `workflow` example includes: `simple.py`, `task_chaining.py`, `fan_out_fan_i
182131
1. Create a directory under `examples/` with a descriptive kebab-case name
183132
2. Add Python source files and a `requirements.txt` referencing the needed SDK packages
184133
3. Add Dapr component YAMLs in a `components/` subdirectory if the example uses state, pubsub, etc.
185-
4. Write a `README.md` with:
186-
- Introduction explaining what the example demonstrates
187-
- Pre-requisites section (Dapr CLI, Python 3.10+, any special tools)
188-
- Install instructions (`pip3 install dapr dapr-ext-grpc` etc.)
189-
- Running instructions with `<!-- STEP -->` blocks wrapping `dapr run` commands
190-
- Expected output section
191-
- Cleanup step to stop background processes
192-
5. Register the example in `tox.ini` under `[testenv:examples]` commands:
193-
```
194-
./validate.sh your-example-name
195-
```
196-
6. Test locally: `cd examples && ./validate.sh your-example-name`
197-
198-
## Common README template
199-
200-
```markdown
201-
# Dapr [Building Block] Example
202-
203-
This example demonstrates how to use the Dapr [building block] API with the Python SDK.
204-
205-
## Pre-requisites
206-
207-
- [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started)
208-
- Python 3.10+
209-
210-
## Install Dapr python-SDK
211-
212-
\`\`\`bash
213-
pip3 install dapr dapr-ext-grpc
214-
\`\`\`
215-
216-
## Run the example
217-
218-
<!-- STEP
219-
name: Run example
220-
expected_stdout_lines:
221-
- '== APP == Expected output here'
222-
timeout_seconds: 30
223-
-->
224-
225-
\`\`\`bash
226-
dapr run --app-id myapp --resources-path ./components/ python3 example.py
227-
\`\`\`
228-
229-
<!-- END_STEP -->
230-
231-
## Cleanup
232-
233-
<!-- STEP
234-
name: Cleanup
235-
-->
236-
237-
\`\`\`bash
238-
dapr stop --app-id myapp
239-
\`\`\`
240-
241-
<!-- END_STEP -->
242-
```
134+
4. Write a `README.md` with introduction, pre-requisites, install instructions, and running instructions
135+
5. Add a corresponding test in `tests/integration/test_<example_name>.py`:
136+
- Use the `@pytest.mark.example_dir('<example-name>')` marker to set the working directory
137+
- Use `dapr.run()` for scripts that exit on their own, `dapr.start()`/`dapr.stop()` for long-running services
138+
- Assert expected output lines appear in the captured output
139+
6. Test locally: `tox -e integration -- test_<example_name>.py`
243140

244141
## Gotchas
245142

246-
- **Output format changes break CI**: If you modify print statements or log output in SDK code, check whether any example's `expected_stdout_lines` depend on that output.
247-
- **Background processes must be cleaned up**: Missing cleanup steps cause CI to hang.
143+
- **Output format changes break tests**: If you modify print statements or log output in SDK code, check whether any integration test's expected lines depend on that output.
144+
- **Background processes must be cleaned up**: The `DaprRunner` fixture handles cleanup on teardown, but tests should still call `dapr.stop()` to capture output.
248145
- **Dapr prefixes output**: Application stdout appears as `== APP == <line>` when run via `dapr run`.
249146
- **Redis is available in CI**: The CI environment has Redis running on `localhost:6379` — most component YAMLs use this.
250147
- **Some examples need special setup**: `crypto` generates keys, `configuration` seeds Redis, `conversation` needs LLM config — check individual READMEs.
148+
- **Infinite-loop example scripts**: Some example scripts (e.g., `invoke-caller.py`) have `while True` loops for demo purposes. Integration tests must either bypass these with HTTP API calls or use `dapr.run(until=...)` for early termination.

examples/validate.sh

Lines changed: 0 additions & 4 deletions
This file was deleted.

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,8 @@ ignore = ["E501","E203", "E712", "E722", "E713"]
2222

2323
[tool.ruff.format]
2424
quote-style = 'single'
25+
26+
[tool.pytest.ini_options]
27+
markers = [
28+
'example_dir(name): set the example directory for the dapr fixture',
29+
]

0 commit comments

Comments
 (0)