Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/v4/index.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

103 changes: 103 additions & 0 deletions docs/v4/util.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 3 additions & 15 deletions examples/spin-kv/app.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,19 @@
from typing import TypeVar, Tuple, List
from componentize_py_types import Result, Err
from componentize_py_async_support.streams import StreamReader
from componentize_py_async_support.futures import FutureReader
from spin_sdk import http, key_value
from spin_sdk import http, key_value, util
from spin_sdk.http import Request, Response
from spin_sdk.key_value import Store

class WasiHttpHandler030Rc20260315(http.Handler):
async def handle_request(self, request: Request) -> Response:
with await key_value.open_default() as a:
await a.set("test", bytes("hello world!", "utf-8"))
print(await get_keys(a))
print(await util.collect(await a.get_keys()))
print(await a.exists("test"))
print(await a.get("test"))
await a.delete("test")
print(await get_keys(a))
print(await util.collect(await a.get_keys()))

return Response(
200,
{"content-type": "text/plain"},
bytes("Hello from Python!", "utf-8")
)

async def get_keys(store: Store) -> list[str]:
stream, future = await store.get_keys()
with stream, future:
keys = []
while not stream.writer_dropped:
keys += await stream.read(max_count=100)
return keys
26 changes: 26 additions & 0 deletions src/spin_sdk/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Module for general-purpose utility functions"""

from typing import TypeVar, Tuple, List
from componentize_py_types import Result, Err
from componentize_py_async_support.streams import StreamReader
from componentize_py_async_support.futures import FutureReader

T = TypeVar('T')
E = TypeVar('E')

async def collect(tuple: Tuple[StreamReader[T], FutureReader[Result[None, E]]]) -> List[T]:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this be usable for PostgreSQL and SQLite rows? The APIs there return (list<column>, stream<row>, future<result>) rather than the mere 2-tuple of get-keys - can users still call this as collect(stream, future) or would they need to re-tuple the stream and future?

Wow I explained that badly but hopefully makes sense.

Copy link
Copy Markdown
Collaborator

@dicej dicej Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could do something like this:

columns, stream, result = connection.execute_async(statement, parameters)
rows = collect((stream, result))

Copy link
Copy Markdown
Collaborator

@dicej dicej Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So yes, you'd need to re-tuple, but the syntax for that is pretty simple.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@itowlson merging this to get some docs URLs to point to for the new util module and collect helper, but please do file a follow-up if I or @dicej can attend to improvements here.

"""
Collect all items from the StreamReader portion of the provided Tuple and return them in a List,
verifying the FutureReader result upon stream completion and, if it is error, raising it as an exception.
"""
stream = tuple[0]
future = tuple[1]
collected = []
with stream, future:
while not stream.writer_dropped:
collected += await stream.read(128)
result = await future.read()
if isinstance(result, Err):
raise result
else:
return collected
Loading