Skip to content

Commit ad40680

Browse files
committed
feat: Add async and streaming examples
Adds two new examples to demonstrate asynchronous and streaming capabilities: - `cloud_run_async`: Shows how to create basic asynchronous HTTP and CloudEvent functions. - `cloud_run_streaming_http`: Shows how to create both synchronous and asynchronous streaming HTTP functions. The examples include instructions for running locally with the `functions-framework` CLI and deploying to Cloud Run with `gcloud`.
1 parent f1dc285 commit ad40680

File tree

8 files changed

+258
-1
lines changed

8 files changed

+258
-1
lines changed

examples/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
## Deployment targets
44
### Cloud Run
5-
* [`cloud_run_http`](./cloud_run_http/) - Deploying an HTTP function to [Cloud Run](http://cloud.google.com/run) with the Functions Framework
5+
* [`cloud_run_http`](./cloud_run_http/) - Deploying an HTTP function to [Cloud Run](http.cloud.google.com/run) with the Functions Framework
66
* [`cloud_run_event`](./cloud_run_event/) - Deploying a CloudEvent function to [Cloud Run](http://cloud.google.com/run) with the Functions Framework
77
* [`cloud_run_cloud_events`](cloud_run_cloud_events/) - Deploying a [CloudEvent](https://github.com/cloudevents/sdk-python) function to [Cloud Run](http://cloud.google.com/run) with the Functions Framework
8+
* [`cloud_run_async`](./cloud_run_async/) - Deploying asynchronous HTTP and CloudEvent functions to [Cloud Run](http://cloud.google.com/run) with the Functions Framework
9+
* [`cloud_run_streaming_http`](./cloud_run_streaming_http/) - Deploying streaming HTTP functions to [Cloud Run](http://cloud.google.com/run) with the Functions Framework
810

911
## Development Tools
1012
* [`docker-compose`](./docker-compose) -

examples/cloud_run_async/README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Deploying async functions to Cloud Run
2+
3+
This sample shows how to deploy asynchronous functions to [Cloud Run](http://cloud.google.com/run) with the Functions Framework. It includes examples for both HTTP and CloudEvent functions, which can be found in the `main.py` file.
4+
5+
## Dependencies
6+
Install the dependencies for this example:
7+
```sh
8+
pip install -r requirements.txt
9+
```
10+
11+
## Running locally
12+
13+
### HTTP Function
14+
To run the HTTP function locally, use the `functions-framework` command:
15+
```sh
16+
functions-framework --target=hello_async_http
17+
```
18+
Then, send a request to it from another terminal:
19+
```sh
20+
curl localhost:8080
21+
# Output: Hello, async world!
22+
```
23+
24+
### CloudEvent Function
25+
To run the CloudEvent function, specify the target and set the signature type:
26+
```sh
27+
functions-framework --target=hello_async_cloudevent --signature-type=cloudevent
28+
```
29+
Then, in another terminal, send a sample CloudEvent using the provided script:
30+
```sh
31+
python send_cloud_event.py
32+
```
33+
34+
## Deploying to Cloud Run
35+
You can deploy these functions to Cloud Run using the `gcloud` CLI.
36+
37+
### HTTP Function
38+
```sh
39+
gcloud run deploy async-http-function \
40+
--source . \
41+
--function hello_async_http \
42+
--base-image python312 \
43+
--region <YOUR_REGION> \
44+
--allow-unauthenticated
45+
```
46+
47+
### CloudEvent Function
48+
```sh
49+
gcloud run deploy async-cloudevent-function \
50+
--source . \
51+
--function hello_async_cloudevent \
52+
--base-image python312 \
53+
--region <YOUR_REGION> \
54+
--set-env-vars=FUNCTION_SIGNATURE_TYPE=cloudevent \
55+
--allow-unauthenticated
56+
```
57+
After deploying, you can invoke the CloudEvent function by sending an HTTP POST request with a CloudEvent payload to its URL.
58+

examples/cloud_run_async/main.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import functions_framework.aio
16+
17+
@functions_framework.aio.http
18+
async def hello_async_http(request):
19+
"""
20+
An async HTTP function.
21+
Args:
22+
request (starlette.requests.Request): The request object.
23+
Returns:
24+
The response text, or an instance of any Starlette response class
25+
(e.g. `starlette.responses.Response`).
26+
"""
27+
return "Hello, async world!"
28+
29+
@functions_framework.aio.cloud_event
30+
async def hello_async_cloudevent(cloud_event):
31+
"""
32+
An async CloudEvent function.
33+
Args:
34+
cloud_event (cloudevents.http.CloudEvent): The CloudEvent object.
35+
"""
36+
print(f"Received event with ID: {cloud_event['id']} and data {cloud_event.data}")
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Function dependencies
2+
functions-framework
3+
4+
# For testing
5+
httpx
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/usr/local/bin/python
2+
3+
# Copyright 2025 Google LLC
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
import asyncio
17+
import httpx
18+
19+
from cloudevents.http import CloudEvent, to_structured
20+
21+
async def main():
22+
# Create a cloudevent
23+
attributes = {
24+
"Content-Type": "application/json",
25+
"source": "from-galaxy-far-far-away",
26+
"type": "cloudevent.greet.you",
27+
}
28+
data = {"name": "john"}
29+
30+
event = CloudEvent(attributes, data)
31+
32+
# Send the event to the local functions-framework server
33+
headers, data = to_structured(event)
34+
35+
async with httpx.AsyncClient() as client:
36+
await client.post("http://localhost:8080/", headers=headers, data=data)
37+
38+
if __name__ == "__main__":
39+
asyncio.run(main())
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Deploying streaming functions to Cloud Run
2+
3+
This sample shows how to deploy streaming functions to [Cloud Run](http://cloud.google.com/run) with the Functions Framework. The `main.py` file contains examples for both synchronous and asynchronous streaming.
4+
5+
## Dependencies
6+
Install the dependencies for this example:
7+
```sh
8+
pip install -r requirements.txt
9+
```
10+
11+
## Running locally
12+
13+
### Synchronous Streaming
14+
To run the synchronous streaming function locally:
15+
```sh
16+
functions-framework --target=hello_stream
17+
```
18+
Then, send a request to it from another terminal:
19+
```sh
20+
curl localhost:8080
21+
```
22+
23+
### Asynchronous Streaming
24+
To run the asynchronous streaming function locally:
25+
```sh
26+
functions-framework --target=hello_stream_async
27+
```
28+
Then, send a request to it from another terminal:
29+
```sh
30+
curl localhost:8080
31+
```
32+
33+
## Deploying to Cloud Run
34+
You can deploy these functions to Cloud Run using the `gcloud` CLI.
35+
36+
### Synchronous Streaming
37+
```sh
38+
gcloud run deploy streaming-function \
39+
--source . \
40+
--function hello_stream \
41+
--base-image python312 \
42+
--region <YOUR_REGION> \
43+
--allow-unauthenticated
44+
```
45+
46+
### Asynchronous Streaming
47+
```sh
48+
gcloud run deploy streaming-async-function \
49+
--source . \
50+
--function hello_stream_async \
51+
--base-image python312 \
52+
--region <YOUR_REGION> \
53+
--allow-unauthenticated
54+
```
55+
56+
```
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import time
16+
import asyncio
17+
import functions_framework
18+
import functions_framework.aio
19+
from starlette.responses import StreamingResponse
20+
21+
# Helper function for the synchronous streaming example.
22+
def slow_numbers(minimum, maximum):
23+
yield '<html><body><ul>'
24+
for number in range(minimum, maximum + 1):
25+
yield '<li>%d</li>' % number
26+
time.sleep(0.5)
27+
yield '</ul></body></html>'
28+
29+
@functions_framework.http
30+
def hello_stream(request):
31+
"""
32+
A synchronous HTTP function that streams a response.
33+
Args:
34+
request (flask.Request): The request object.
35+
Returns:
36+
A generator, which will be streamed as the response.
37+
"""
38+
generator = slow_numbers(1, 10)
39+
return generator, {'Content-Type': 'text/html'}
40+
41+
# Helper function for the asynchronous streaming example.
42+
async def slow_numbers_async(minimum, maximum):
43+
yield '<html><body><ul>'
44+
for number in range(minimum, maximum + 1):
45+
yield '<li>%d</li>' % number
46+
await asyncio.sleep(0.5)
47+
yield '</ul></body></html>'
48+
49+
@functions_framework.aio.http
50+
async def hello_stream_async(request):
51+
"""
52+
An asynchronous HTTP function that streams a response.
53+
Args:
54+
request (starlette.requests.Request): The request object.
55+
Returns:
56+
A starlette.responses.StreamingResponse.
57+
"""
58+
generator = slow_numbers_async(1, 10)
59+
return StreamingResponse(generator, media_type='text/html')
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Function dependencies
2+
functions-framework

0 commit comments

Comments
 (0)