You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
-[Creating Futures and Streams](#creating-futures-and-streams)
24
27
-[Storing Data in the Spin Key-Value Store](#storing-data-in-the-spin-key-value-store)
25
28
-[Storing Data in SQLite](#storing-data-in-sqlite)
26
29
-[AI Inferencing From Python Components](#ai-inferencing-from-python-components)
@@ -481,6 +484,46 @@ redis-cli
481
484
"bar"
482
485
```
483
486
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
+
whilenot 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
+
ifisinstance(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
+
### Spawning Asynchronous Tasks
514
+
515
+
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.
516
+
517
+
### Creating Futures and Streams
518
+
519
+
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.
520
+
521
+
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).
522
+
523
+
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 goroutine 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.
524
+
525
+
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!
526
+
484
527
## Storing Data in the Spin Key-Value Store
485
528
486
529
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