Skip to content

Commit fc08204

Browse files
committed
standalone activity samples to pair with docs for quickstart
Signed-off-by: Phil Prasek <prasek@gmail.com>
1 parent 591c531 commit fc08204

File tree

8 files changed

+259
-0
lines changed

8 files changed

+259
-0
lines changed
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Standalone Activity
2+
3+
This sample shows how to execute Activities directly from a Temporal Client, without a Workflow.
4+
5+
For full documentation, see [Standalone Activities - Python SDK](https://docs.temporal.io/develop/python/standalone-activities).
6+
7+
### Sample directory structure
8+
9+
- [my_activity.py](./my_activity.py) - Activity definition with `@activity.defn`
10+
- [worker.py](./worker.py) - Worker that registers and runs the Activity
11+
- [execute_activity.py](./execute_activity.py) - Execute a Standalone Activity and wait for the result
12+
- [start_activity.py](./start_activity.py) - Start a Standalone Activity, get a handle, then wait for the result
13+
- [list_activities.py](./list_activities.py) - List Standalone Activity Executions
14+
- [count_activities.py](./count_activities.py) - Count Standalone Activity Executions
15+
16+
### Quickstart
17+
18+
**1. Start the Temporal dev server**
19+
20+
```bash
21+
temporal server start-dev
22+
```
23+
24+
**2. Run the Worker** (in a separate terminal)
25+
26+
```bash
27+
uv run hello_standalone_activity/worker.py
28+
```
29+
30+
**3. Execute a Standalone Activity** (in a separate terminal)
31+
32+
Execute and wait for the result:
33+
34+
```bash
35+
uv run hello_standalone_activity/execute_activity.py
36+
```
37+
38+
Or use the Temporal CLI:
39+
40+
```bash
41+
temporal activity execute \
42+
--type compose_greeting \
43+
--activity-id my-standalone-activity-id \
44+
--task-queue my-standalone-activity-task-queue \
45+
--start-to-close-timeout 10s \
46+
--input '{"greeting": "Hello", "name": "World"}'
47+
```
48+
49+
**4. Start a Standalone Activity (without waiting)**
50+
51+
Start, get a handle, then wait for the result:
52+
53+
```bash
54+
uv run hello_standalone_activity/start_activity.py
55+
```
56+
57+
Or use the Temporal CLI:
58+
59+
```bash
60+
temporal activity start \
61+
--type compose_greeting \
62+
--activity-id my-standalone-activity-id \
63+
--task-queue my-standalone-activity-task-queue \
64+
--start-to-close-timeout 10s \
65+
--input '{"greeting": "Hello", "name": "World"}'
66+
```
67+
68+
**5. List Standalone Activities**
69+
70+
```bash
71+
uv run hello_standalone_activity/list_activities.py
72+
```
73+
74+
Or use the Temporal CLI:
75+
76+
```bash
77+
temporal activity list --query "TaskQueue = 'my-standalone-activity-task-queue'"
78+
```
79+
80+
Note: `list` and `count` are only available in the [Standalone Activity prerelease CLI](https://github.com/temporalio/cli/releases/tag/v1.6.2-standalone-activity).
81+
82+
**6. Count Standalone Activities**
83+
84+
```bash
85+
uv run hello_standalone_activity/count_activities.py
86+
```
87+
88+
Or use the Temporal CLI:
89+
90+
```bash
91+
temporal activity count --query "TaskQueue = 'my-standalone-activity-task-queue'"
92+
```
93+
94+
### Temporal Cloud
95+
96+
The same code works against Temporal Cloud - just set environment variables. No code changes needed.
97+
98+
**Connect with mTLS:**
99+
100+
```bash
101+
export TEMPORAL_ADDRESS=<your-namespace>.<your-account-id>.tmprl.cloud:7233
102+
export TEMPORAL_NAMESPACE=<your-namespace>.<your-account-id>
103+
export TEMPORAL_TLS_CLIENT_CERT_PATH='path/to/your/client.pem'
104+
export TEMPORAL_TLS_CLIENT_KEY_PATH='path/to/your/client.key'
105+
```
106+
107+
**Connect with an API key:**
108+
109+
```bash
110+
export TEMPORAL_ADDRESS=<region>.<cloud_provider>.api.temporal.io:7233
111+
export TEMPORAL_NAMESPACE=<your-namespace>.<your-account-id>
112+
export TEMPORAL_API_KEY=<your-api-key>
113+
```
114+
115+
Then run the worker and starter as shown above.

hello_standalone_activity/__init__.py

Whitespace-only changes.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import asyncio
2+
3+
from temporalio.client import Client
4+
from temporalio.envconfig import ClientConfig
5+
6+
7+
async def my_application():
8+
connect_config = ClientConfig.load_client_connect_config()
9+
connect_config.setdefault("target_host", "localhost:7233")
10+
client = await Client.connect(**connect_config)
11+
12+
resp = await client.count_activities(
13+
query="TaskQueue = 'my-standalone-activity-task-queue'",
14+
)
15+
16+
print("Total activities:", resp.count)
17+
18+
for group in resp.groups:
19+
print(f"Group {group.group_values}: {group.count}")
20+
21+
22+
if __name__ == "__main__":
23+
asyncio.run(my_application())
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import asyncio
2+
from datetime import timedelta
3+
4+
from temporalio.client import Client
5+
from temporalio.envconfig import ClientConfig
6+
7+
from hello_standalone_activity.my_activity import ComposeGreetingInput, compose_greeting
8+
9+
10+
async def my_application():
11+
connect_config = ClientConfig.load_client_connect_config()
12+
connect_config.setdefault("target_host", "localhost:7233")
13+
client = await Client.connect(**connect_config)
14+
15+
activity_result = await client.execute_activity(
16+
compose_greeting,
17+
args=[ComposeGreetingInput("Hello", "World")],
18+
id="my-standalone-activity-id",
19+
task_queue="my-standalone-activity-task-queue",
20+
start_to_close_timeout=timedelta(seconds=10),
21+
)
22+
print(f"Activity result: {activity_result}")
23+
24+
25+
if __name__ == "__main__":
26+
asyncio.run(my_application())
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import asyncio
2+
3+
from temporalio.client import Client
4+
from temporalio.envconfig import ClientConfig
5+
6+
7+
async def my_application():
8+
connect_config = ClientConfig.load_client_connect_config()
9+
connect_config.setdefault("target_host", "localhost:7233")
10+
client = await Client.connect(**connect_config)
11+
12+
activities = client.list_activities(
13+
query="TaskQueue = 'my-standalone-activity-task-queue'",
14+
)
15+
16+
async for info in activities:
17+
print(
18+
f"ActivityID: {info.activity_id}, Type: {info.activity_type}, Status: {info.status}"
19+
)
20+
21+
22+
if __name__ == "__main__":
23+
asyncio.run(my_application())
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from dataclasses import dataclass
2+
3+
from temporalio import activity
4+
5+
6+
@dataclass
7+
class ComposeGreetingInput:
8+
greeting: str
9+
name: str
10+
11+
12+
@activity.defn
13+
def compose_greeting(input: ComposeGreetingInput) -> str:
14+
activity.logger.info("Running activity with parameter %s" % input)
15+
return f"{input.greeting}, {input.name}!"
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import asyncio
2+
from datetime import timedelta
3+
4+
from temporalio.client import Client
5+
from temporalio.envconfig import ClientConfig
6+
7+
from hello_standalone_activity.my_activity import ComposeGreetingInput, compose_greeting
8+
9+
10+
async def my_application():
11+
connect_config = ClientConfig.load_client_connect_config()
12+
connect_config.setdefault("target_host", "localhost:7233")
13+
client = await Client.connect(**connect_config)
14+
15+
# Start the activity without waiting for the result
16+
activity_handle = await client.start_activity(
17+
compose_greeting,
18+
args=[ComposeGreetingInput("Hello", "World")],
19+
id="my-standalone-activity-id",
20+
task_queue="my-standalone-activity-task-queue",
21+
start_to_close_timeout=timedelta(seconds=10),
22+
)
23+
print(f"Started activity: {activity_handle.id}")
24+
25+
# Wait for the result
26+
activity_result = await activity_handle.result()
27+
print(f"Activity result: {activity_result}")
28+
29+
30+
if __name__ == "__main__":
31+
asyncio.run(my_application())
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import asyncio
2+
from concurrent.futures import ThreadPoolExecutor
3+
4+
from temporalio.client import Client
5+
from temporalio.envconfig import ClientConfig
6+
from temporalio.worker import Worker
7+
8+
from hello_standalone_activity.my_activity import compose_greeting
9+
10+
11+
async def main():
12+
connect_config = ClientConfig.load_client_connect_config()
13+
connect_config.setdefault("target_host", "localhost:7233")
14+
client = await Client.connect(**connect_config)
15+
worker = Worker(
16+
client,
17+
task_queue="my-standalone-activity-task-queue",
18+
activities=[compose_greeting],
19+
activity_executor=ThreadPoolExecutor(5),
20+
)
21+
print("worker running...", end="", flush=True)
22+
await worker.run()
23+
24+
25+
if __name__ == "__main__":
26+
asyncio.run(main())

0 commit comments

Comments
 (0)