Skip to content

Commit 1471165

Browse files
committed
Move interactive breakpoint demo from test to README example
Replace the always-skipped test_workflow_breakpoint_interactive (and its companion BreakpointWorkflow) with a runnable example snippet in the README's debugging section. Skipped tests accumulate as noise and rot silently; a README example is more discoverable for users and easier to keep accurate. The two automated tests (thread-placement assertions for both modes plus the defensive hook test) remain and continue to provide regression protection.
1 parent 935674e commit 1471165

2 files changed

Lines changed: 41 additions & 46 deletions

File tree

README.md

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1248,10 +1248,50 @@ onto the asyncio main thread instead of a worker thread pool. This lets `breakpo
12481248
inside workflow code open an interactive REPL — without it, pdb hangs because its `input()` call would run on a
12491249
thread that does not own the controlling TTY.
12501250

1251+
A minimal runnable example:
1252+
12511253
```python
1252-
worker = Worker(client, task_queue=..., workflows=[MyWorkflow], debug_mode=True)
1254+
import asyncio
1255+
from datetime import timedelta
1256+
1257+
from temporalio import workflow
1258+
from temporalio.client import Client
1259+
from temporalio.worker import Worker
1260+
1261+
1262+
@workflow.defn(sandboxed=False)
1263+
class DebugMeWorkflow:
1264+
@workflow.run
1265+
async def run(self) -> str:
1266+
x = 42
1267+
breakpoint() # interactive pdb prompt opens here
1268+
return f"x was {x}"
1269+
1270+
1271+
async def main() -> None:
1272+
client = await Client.connect("localhost:7233")
1273+
async with Worker(
1274+
client,
1275+
task_queue="debug-me",
1276+
workflows=[DebugMeWorkflow],
1277+
debug_mode=True,
1278+
):
1279+
result = await client.execute_workflow(
1280+
DebugMeWorkflow.run,
1281+
id="debug-me-wf",
1282+
task_queue="debug-me",
1283+
task_timeout=timedelta(minutes=10), # see caveat below
1284+
)
1285+
print(result)
1286+
1287+
1288+
if __name__ == "__main__":
1289+
asyncio.run(main())
12531290
```
12541291

1292+
Run with `python debug_me.py` (not under pytest, which captures stdin and breaks the REPL). At the `(Pdb)`
1293+
prompt try `p x`, `n`, `c`, `q`.
1294+
12551295
Two caveats when pausing at a breakpoint inside a workflow:
12561296

12571297
1. **Workflow task timeout.** Temporal expires a workflow task after ~10 seconds by default. If you sit at the

tests/worker/test_breakpoint_hang.py

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818
import threading
1919
import uuid
2020

21-
import pytest
22-
2321
from temporalio import workflow
2422
from temporalio.client import Client
2523
from temporalio.worker import Worker
@@ -89,49 +87,6 @@ async def test_workflow_runs_on_main_thread_in_debug_mode(client: Client):
8987
)
9088

9189

92-
@workflow.defn(sandboxed=False)
93-
class BreakpointWorkflow:
94-
"""Workflow that calls breakpoint() — for manual interactive testing only.
95-
96-
Run via:
97-
uv run pytest tests/worker/test_breakpoint_hang.py::test_workflow_breakpoint_interactive \\
98-
-E time-skipping -s --no-header -p no:cacheprovider
99-
100-
The `-s` flag disables pytest's stdin/stdout capture so pdb's REPL is
101-
usable. At the prompt try: `p x`, `n`, `c`, `q`.
102-
"""
103-
104-
@workflow.run
105-
async def run(self) -> str:
106-
x = 42
107-
y = "workflow state"
108-
print(
109-
f"\n[BreakpointWorkflow] about to hit breakpoint(); thread={threading.current_thread().name}"
110-
)
111-
breakpoint()
112-
return f"x={x}, y={y!r}"
113-
114-
115-
@pytest.mark.skip(
116-
reason="interactive — invoke directly with `pytest -s` to drive the pdb REPL"
117-
)
118-
async def test_workflow_breakpoint_interactive(client: Client):
119-
"""Drive a real pdb session inside a workflow. Skipped in CI."""
120-
task_queue = f"tq-{uuid.uuid4()}"
121-
async with Worker(
122-
client,
123-
task_queue=task_queue,
124-
workflows=[BreakpointWorkflow],
125-
debug_mode=True,
126-
):
127-
result = await client.execute_workflow(
128-
BreakpointWorkflow.run,
129-
id=f"wf-{uuid.uuid4()}",
130-
task_queue=task_queue,
131-
)
132-
print(f"\n[BreakpointWorkflow] result={result!r}")
133-
134-
13590
def test_breakpoint_hook_raises_on_workflow_thread(client: Client):
13691
"""The defensive hook fails loudly when breakpoint() is called from a
13792
`temporal_workflow_*` thread without debug mode."""

0 commit comments

Comments
 (0)