Skip to content

Commit 470d64c

Browse files
committed
Python async
Signed-off-by: itowlson <ivan.towlson@fermyon.com>
1 parent b9c43ba commit 470d64c

1 file changed

Lines changed: 45 additions & 0 deletions

File tree

content/v4/python-components.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ url = "https://github.com/spinframework/spin-docs/blob/main/content/v4/python-co
2121
- [An Outbound Redis Example](#an-outbound-redis-example)
2222
- [Configuring Outbound Redis](#configuring-outbound-redis)
2323
- [Building and Running the Application](#building-and-running-the-application-3)
24+
- [Async and Streaming Idioms in Python](#async-and-streaming-idioms-in-python)
25+
- [Spawning Asynchronous Tasks](#spawning-asynchronous-tasks)
26+
- [Creating Futures and Streams](#creating-futures-and-streams)
2427
- [Storing Data in the Spin Key-Value Store](#storing-data-in-the-spin-key-value-store)
2528
- [Storing Data in SQLite](#storing-data-in-sqlite)
2629
- [AI Inferencing From Python Components](#ai-inferencing-from-python-components)
@@ -481,6 +484,48 @@ redis-cli
481484
"bar"
482485
```
483486

487+
## Async and Streaming Idioms in Python
488+
489+
When a Spin API returns a potentially large number of values, such as database query APIs, the convention is to return the values as a asynchronous iterator (`componentize_py_async_support.streams.StreamReader`), plus a future (`componentize_py_async_support.futures.FutureReader`) containing the result of the operation. For example, the key-value `Store::get_keys` function returns a stream of strings and a future of 'either OK or error'. This signature is likely to be unfamiliar. The way to read it is:
490+
491+
- Spin will stream values to you until either there are no more values, or an error occurs.
492+
- When that happens, you must `await` the future to find out which one it was.
493+
494+
For example, here's how you might use `Store::get_keys`:
495+
496+
```python
497+
stream, future = await store.get_keys()
498+
499+
with stream, future:
500+
while not stream.writer_dropped: # check if at the end of the stream
501+
batch = await stream.read(max_count=100)
502+
# do something with `batch`
503+
504+
result = await result.read() # check if the key stream hit an error
505+
if isinstance(result, Err):
506+
raise result
507+
else:
508+
pass
509+
```
510+
511+
The future does not resolve until the stream ends, so be sure not to await it until you've finished with the stream.
512+
513+
> If the data set is small enough to fit in memory and you are happy to wait for the last item, use `spin_sdk.util.collect` function that collects all the streamed values into a list, and checks for an error. For example, `all_keys = spin_sdk.util.collect(await store.get_keys())`.
514+
515+
### Spawning Asynchronous Tasks
516+
517+
You can spawn an asynchronous task in a component using the `componentize_py_async_support.spawn()` function, passing it a future. The future is then run to completion in the background. The task may outlive the entry point of your component - this is crucial in, for example, the HTTP trigger, where your handler function doesn't necessarily want to wait for all response data to be available before it starts sending.
518+
519+
### Creating Futures and Streams
520+
521+
The Python SDK provides `spin_sdk.wit.*` functions for creating Wasm Component Model futures and streams. The bindings contain a corresponding function for each concrete future or stream type mentioned in the Spin and WASI APIs.
522+
523+
To create a future, call `spin_sdk.wit.<type>_future()` - for example, `spin_sdk.wit.fields_future()`. This returns a writer (which you can use later to complete the future) and a reader (representing the future which will eventually resolve to a value).
524+
525+
To create a stream, call `spin_sdk.wit.<type>_stream()` - for example, `spin_sdk.wit.byte_stream()` is a byte stream. Again, this returns a writer and a reader. The writer is typically handed to a background task (created using `spawn`) to asynchronously send values into the stream. The reader is typically passed to an API that takes a stream parameter, for example acting as the body in an HTTP response.
526+
527+
For generic types, the type name in the function is formed by concatenation, so you may see things like `result_option_wasi_http_types_fields_wasi_http_types_error_code_future` at the bindings level. You shouldn't normally have to deal with these in application code though!
528+
484529
## Storing Data in the Spin Key-Value Store
485530

486531
Spin has a key-value store built in. For information about using it from Python, see [the key-value store API guide](kv-store-api-guide).

0 commit comments

Comments
 (0)