Skip to content

Commit 4362c80

Browse files
committed
Merge remote-tracking branch 'origin/main' into convert-activities-to-sync
2 parents 6c450c8 + cd23a9c commit 4362c80

File tree

45 files changed

+3480
-3815
lines changed

Some content is hidden

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

45 files changed

+3480
-3815
lines changed

.github/workflows/ci.yml

+8-9
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ jobs:
2121
runsOn: macos-14
2222
runs-on: ${{ matrix.runsOn || matrix.os }}
2323
steps:
24+
- uses: astral-sh/setup-uv@v5
2425
- name: Print build information
2526
run: "echo head_ref: ${{ github.head_ref }}, ref: ${{ github.ref }}, os: ${{ matrix.os }}, python: ${{ matrix.python }}"
2627
- uses: actions/checkout@v4
@@ -29,10 +30,8 @@ jobs:
2930
- uses: actions/setup-python@v5
3031
with:
3132
python-version: ${{ matrix.python }}
32-
# Using fixed Poetry version until
33-
# https://github.com/python-poetry/poetry/pull/7694 is fixed
34-
- run: python -m pip install --upgrade wheel "poetry==1.4.0" poethepoet
35-
- run: poetry install --with pydantic_converter --with dsl --with encryption --with trio_async
33+
- run: uv tool install poethepoet
34+
- run: uv sync --group=dsl --group=encryption --group=trio-async
3635
- run: poe lint
3736
- run: mkdir junit-xml
3837
- run: poe test -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml
@@ -41,17 +40,17 @@ jobs:
4140
- name: Uninstall pydantic
4241
shell: bash
4342
run: |
44-
echo y | poetry run pip uninstall pydantic
45-
echo y | poetry run pip uninstall pydantic-core
46-
poetry run pip install pydantic==1.10
43+
echo y | uv run pip uninstall pydantic
44+
echo y | uv run pip uninstall pydantic-core
45+
uv run pip install pydantic==1.10
4746
poe test -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}--pydantic-v1.xml tests/pydantic_converter_v1/workflow_test.py
4847
4948
# On latest, run gevent test
5049
- name: Gevent test
5150
if: ${{ matrix.python == '3.12' }}
5251
run: |
53-
poetry install --with gevent
54-
poetry run python gevent_async/test/run_combined.py
52+
uv sync --group gevent
53+
uv run gevent_async/test/run_combined.py
5554
5655
- name: Upload junit-xml artifacts
5756
uses: actions/upload-artifact@v4

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.venv
22
.idea
33
__pycache__
4+
/.vscode/

README.md

+10-7
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
11
# Temporal Python SDK Samples
22

3-
This is the set of Python samples for the [Python SDK](https://github.com/temporalio/sdk-python).
3+
This is a collection of samples showing how to use the [Python SDK](https://github.com/temporalio/sdk-python).
44

55
## Usage
66

77
Prerequisites:
88

9-
* Python >= 3.9
10-
* [Poetry](https://python-poetry.org)
9+
* [uv](https://docs.astral.sh/uv/)
1110
* [Temporal CLI installed](https://docs.temporal.io/cli#install)
1211
* [Local Temporal server running](https://docs.temporal.io/cli/server#start-dev)
1312

13+
The SDK requires Python >= 3.9. You can install Python using uv. For example,
14+
15+
uv python install 3.13
16+
1417
With this repository cloned, run the following at the root of the directory:
1518

16-
poetry install
19+
uv sync
1720

18-
That loads all required dependencies. Then to run a sample, usually you just run it in Python. For example:
21+
That loads all required dependencies. Then to run a sample, usually you just run it under uv. For example:
1922

20-
poetry run python hello/hello_activity.py
23+
uv run hello/hello_activity.py
2124

2225
Some examples require extra dependencies. See each sample's directory for specific instructions.
2326

@@ -81,7 +84,7 @@ Some examples require extra dependencies. See each sample's directory for specif
8184

8285
Running the tests requires `poe` to be installed.
8386

84-
python -m pip install poethepoet
87+
uv tool install poethepoet
8588

8689
Once you have `poe` installed you can run:
8790

activity_worker/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ First run the Go workflow worker by running this in the `go_workflow` directory
88

99
Then in another terminal, run the sample from this directory:
1010

11-
poetry run python activity_worker.py
11+
uv run activity_worker.py
1212

1313
The Python code will invoke the Go workflow which will execute the Python activity and return.

bedrock/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ These examples use Amazon's Python SDK (Boto3). To configure Boto3 to use your A
2020

2121
For these sample, the optional `bedrock` dependency group must be included. To include, run:
2222

23-
poetry install --with bedrock
23+
uv sync --group bedrock
2424

2525
There are 3 Bedrock samples, see the README.md in each sub-directory for instructions on running each.

bedrock/basic/README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ A basic Bedrock workflow. Starts a workflow with a prompt, generates a response
44

55
To run, first see `samples-python` [README.md](../../README.md), and `bedrock` [README.md](../README.md) for prerequisites specific to this sample. Once set up, run the following from this directory:
66

7-
1. Run the worker: `poetry run python run_worker.py`
7+
1. Run the worker: `uv run run_worker.py`
88
2. In another terminal run the client with a prompt:
99

10-
e.g. `poetry run python send_message.py 'What animals are marsupials?'`
10+
e.g. `uv run send_message.py 'What animals are marsupials?'`

bedrock/entity/README.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@ Multi-Turn Chat using an Entity Workflow. The workflow runs forever unless expli
44

55
To run, first see `samples-python` [README.md](../../README.md), and `bedrock` [README.md](../README.md) for prerequisites specific to this sample. Once set up, run the following from this directory:
66

7-
1. Run the worker: `poetry run python run_worker.py`
7+
1. Run the worker: `uv run run_worker.py`
88
2. In another terminal run the client with a prompt.
99

10-
Example: `poetry run python send_message.py 'What animals are marsupials?'`
10+
Example: `uv run send_message.py 'What animals are marsupials?'`
1111

1212
3. View the worker's output for the response.
1313
4. Give followup prompts by signaling the workflow.
1414

15-
Example: `poetry run python send_message.py 'Do they lay eggs?'`
15+
Example: `uv run send_message.py 'Do they lay eggs?'`
1616
5. Get the conversation history summary by querying the workflow.
1717

18-
Example: `poetry run python get_history.py`
19-
6. To end the chat session, run `poetry run python end_chat.py`
18+
Example: `uv run get_history.py`
19+
6. To end the chat session, run `uv run end_chat.py`

bedrock/signals_and_queries/README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@ Adding signals & queries to the [basic Bedrock sample](../1_basic). Starts a wor
44

55
To run, first see `samples-python` [README.md](../../README.md), and `bedrock` [README.md](../README.md) for prerequisites specific to this sample. Once set up, run the following from this directory:
66

7-
1. Run the worker: `poetry run python run_worker.py`
7+
1. Run the worker: `uv run run_worker.py`
88
2. In another terminal run the client with a prompt.
99

10-
Example: `poetry run python send_message.py 'What animals are marsupials?'`
10+
Example: `uv run send_message.py 'What animals are marsupials?'`
1111

1212
3. View the worker's output for the response.
1313
4. Give followup prompts by signaling the workflow.
1414

15-
Example: `poetry run python send_message.py 'Do they lay eggs?'`
15+
Example: `uv run send_message.py 'Do they lay eggs?'`
1616
5. Get the conversation history by querying the workflow.
1717

18-
Example: `poetry run python get_history.py`
18+
Example: `uv run get_history.py`
1919
6. The workflow will timeout after inactivity.

canary/README.md

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Event Loop Canary Sample
2+
3+
This can help you determine if your event loop is clogged.
4+
5+
The idea is that you could add this canary workflow
6+
to your worker initialization, and
7+
it will log the delays in your event loop.
8+
9+
> Note: it doesn't tell you what is clogging it, but it tells you
10+
> whether something is clogging it.
11+
12+
## Example
13+
14+
In one terminal, run:
15+
16+
```txt
17+
$ poetry run python canary/your_workflows.py
18+
19+
# no output
20+
```
21+
22+
And in another, run the following:
23+
24+
```txt
25+
$ poetry run python canary/run_canary_worker.py
26+
27+
Your activity finished after 0.5 seconds
28+
Your activity finished after 1.3 seconds
29+
Your activity finished after 1.3 seconds
30+
The canary detected 1.1774 seconds of event loop delay.
31+
Your activity finished after 1.4 seconds
32+
Your activity finished after 1.1 seconds
33+
Your activity finished after 1.2 seconds
34+
The canary detected 0.7662 seconds of event loop delay.
35+
Your activity finished after 1.3 seconds
36+
Your activity finished after 0.8 seconds
37+
Your activity finished after 1.3 seconds
38+
The canary detected 0.4724 seconds of event loop delay.
39+
Your activity finished after 0.9 seconds
40+
Your activity finished after 1.3 seconds
41+
Your activity finished after 1.3 seconds
42+
The canary detected 0.6033 seconds of event loop delay.
43+
Your activity finished after 1.4 seconds
44+
Your activity finished after 1.4 seconds
45+
Your activity finished after 0.7 seconds
46+
The canary detected 0.5424 seconds of event loop delay.
47+
Your activity finished after 1.2 seconds
48+
Your activity finished after 0.7 seconds
49+
Your activity finished after 0.7 seconds
50+
Your activity finished after 0.9 seconds
51+
The canary detected 0.6584 seconds of event loop delay.
52+
...
53+
```

canary/run_canary_worker.py

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
"""
2+
In your worker initialization, you can add the canary workflow.
3+
"""
4+
5+
from datetime import timedelta
6+
import asyncio
7+
import time
8+
9+
from temporalio import activity, workflow
10+
from temporalio.client import Client
11+
from temporalio.worker import Worker
12+
13+
from canary.your_workflows import YourWorkflow, your_activity
14+
15+
_SECONDS_BETWEEN_CANARY_CHECKS = 3
16+
17+
18+
@activity.defn
19+
async def canary_activity() -> None:
20+
"""
21+
Here's the activity that can probe your worker and see if it's
22+
still responsive.
23+
"""
24+
t_prev = time.time()
25+
while True:
26+
await asyncio.sleep(_SECONDS_BETWEEN_CANARY_CHECKS)
27+
t_new = time.time()
28+
delay = t_new - (t_prev + _SECONDS_BETWEEN_CANARY_CHECKS)
29+
t_prev = t_new
30+
31+
# Log the extra time taken by the event loop to get back after the await
32+
# If you want, you can turn this into a histogram and show the distribution.
33+
# maybe you could even put it in your metrics.
34+
activity.logger.info(
35+
f"The canary detected {round(delay,4)} seconds of event loop delay."
36+
)
37+
print(f"The canary detected {round(delay,4)} seconds of event loop delay.")
38+
39+
40+
@workflow.defn
41+
class CanaryWorkflow:
42+
"""
43+
Here's the workflow that can probe your worker and see if it's
44+
still responsive.
45+
"""
46+
47+
@workflow.run
48+
async def run(self) -> str:
49+
50+
return await workflow.execute_activity(
51+
canary_activity,
52+
# these timeouts are going to be tricky because if the event loop
53+
# is indeed blocked, the heartbeats etc may not behave as expected.
54+
start_to_close_timeout=timedelta(seconds=60 * 100),
55+
)
56+
57+
58+
async def main():
59+
client = await Client.connect("localhost:7233")
60+
61+
async with Worker(
62+
client,
63+
task_queue="canary-task-queue",
64+
workflows=[CanaryWorkflow, YourWorkflow],
65+
activities=[canary_activity, your_activity],
66+
):
67+
68+
# add this to your code
69+
await client.execute_workflow(
70+
CanaryWorkflow.run,
71+
id="canary",
72+
task_queue="canary-task-queue",
73+
)
74+
75+
76+
if __name__ == "__main__":
77+
asyncio.run(main())

canary/your_workflows.py

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""
2+
This simulates your code.
3+
4+
You can actually use your own code, but if you want
5+
to use this code as a playground, you can change
6+
the amount of time in the time.sleep() call in
7+
your_activity().
8+
"""
9+
10+
import asyncio
11+
import time
12+
import random
13+
from datetime import timedelta
14+
15+
from temporalio.client import Client
16+
from temporalio import activity, workflow
17+
18+
19+
@activity.defn
20+
async def your_activity() -> None:
21+
"""
22+
Here's the activity that's in your codebase.
23+
24+
You can experiment with this one to see how it behaves.
25+
"""
26+
27+
t0 = time.time()
28+
29+
# this simulates a long-running activity. this is the piece that we don't
30+
# know if your code has it or not. This is what we're using the canary for.
31+
#
32+
# to illustrate the difference, comment out time.sleep() and uncomment
33+
# the asyncio.sleep() call.
34+
# the canary will detect very little delay.
35+
r = random.random()
36+
time.sleep(0.5 + r)
37+
# await asyncio.sleep(.5 + r)
38+
39+
print(f"Your activity finished after {round(time.time() - t0,1)} seconds")
40+
41+
42+
@workflow.defn
43+
class YourWorkflow:
44+
@workflow.run
45+
async def run(self) -> str:
46+
47+
return await workflow.execute_activity(
48+
your_activity,
49+
start_to_close_timeout=timedelta(seconds=60 * 100),
50+
)
51+
52+
53+
async def main():
54+
client = await Client.connect("localhost:7233")
55+
56+
while True:
57+
# simulate running your workflows
58+
59+
await client.execute_workflow(
60+
YourWorkflow.run,
61+
id="your-workflow",
62+
task_queue="canary-task-queue",
63+
)
64+
65+
66+
if __name__ == "__main__":
67+
asyncio.run(main())

cloud_export_to_parquet/README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@ This is an example workflow to convert exported file from proto to parquet file.
44

55
Please make sure your python is 3.9 above. For this sample, run:
66

7-
poetry install --with cloud_export_to_parquet
7+
uv sync --group=cloud-export-to-parquet
88

99
Before you start, please modify workflow input in `create_schedule.py` with your s3 bucket and namespace. Also make sure you've the right AWS permission set up in your environment to allow this workflow read and write to your s3 bucket.
1010

1111
To run, first see [README.md](../README.md) for prerequisites. Then, run the following from this directory to start the worker:
1212

1313
```bash
14-
poetry run python run_worker.py
14+
uv run run_worker.py
1515
```
1616

1717
This will start the worker. Then, in another terminal, run the following to execute the schedule:
1818

1919
```bash
20-
poetry run python create_schedule.py
20+
uv run create_schedule.py
2121
```
2222

2323
The workflow should convert exported file in your input s3 bucket to parquet in your specified location.

0 commit comments

Comments
 (0)