From 343cbf3b7f591fb69ca82ba51774e92174bdb030 Mon Sep 17 00:00:00 2001 From: Vaughn Dice Date: Wed, 8 Apr 2026 12:55:11 -0600 Subject: [PATCH 1/4] docs(v4): update Python code snippets/links/details Signed-off-by: Vaughn Dice --- content/v4/http-outbound.md | 11 +++++-- content/v4/http-trigger.md | 10 ++++--- content/v4/kv-store-api-guide.md | 40 +++++++++++++++++++------ content/v4/language-support-overview.md | 2 +- content/v4/mqtt-outbound.md | 19 +++++++++++- content/v4/quickstart.md | 30 ++++++++++--------- content/v4/rdbms-storage.md | 9 +++--- content/v4/redis-outbound.md | 19 +++++++----- content/v4/redis-trigger.md | 9 ++++-- content/v4/serverless-ai-api-guide.md | 27 +++++++++-------- content/v4/sqlite-api-guide.md | 20 +++++++------ content/v4/variables.md | 24 ++++++++------- content/v4/writing-apps.md | 2 +- 13 files changed, 142 insertions(+), 80 deletions(-) diff --git a/content/v4/http-outbound.md b/content/v4/http-outbound.md index 39e663a2..b02b608a 100644 --- a/content/v4/http-outbound.md +++ b/content/v4/http-outbound.md @@ -176,13 +176,18 @@ You can find a complete example of using outbound HTTP in the JavaScript SDK rep {{ startTab "Python"}} -> [**Want to go straight to the reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v3/http/index.html) +> [**Want to go straight to the reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v4/http/index.html) -HTTP functions and classes are available in the `http` module. The function name is [`send`](https://spinframework.github.io/spin-python-sdk/v3/http/index.html#spin_sdk.http.send). The [request type](https://spinframework.github.io/spin-python-sdk/http/index.html#spin_sdk.http.Request) is `Request`, and the [response type](https://spinframework.github.io/spin-python-sdk/v3/http/index.html#spin_sdk.http.Response) is `Response`. For example: +HTTP functions and classes are available in the `http` module. The function name is [`send`](https://spinframework.github.io/spin-python-sdk/v4/http/index.html#spin_sdk.http.send). The [request type](https://spinframework.github.io/spin-python-sdk/http/index.html#spin_sdk.http.Request) is `Request`, and the [response type](https://spinframework.github.io/spin-python-sdk/v4/http/index.html#spin_sdk.http.Response) is `Response`. For example: ```python +from spin_sdk import http from spin_sdk.http import Request, Response, send -response = send(Request("GET", "https://random-data-api.fermyon.app/animals/json", {}, None)) + +class WasiHttpHandler030Rc20260315(http.Handler): + async def handle_request(self, request: Request) -> Response: + response = await send(Request("GET", "https://random-data-api.fermyon.app/animals/json", {}, None)) + return response ``` **Notes** diff --git a/content/v4/http-trigger.md b/content/v4/http-trigger.md index af9eb6c3..d0992cb6 100644 --- a/content/v4/http-trigger.md +++ b/content/v4/http-trigger.md @@ -226,16 +226,16 @@ addEventListener('fetch', async (event: FetchEvent) => { {{ startTab "Python"}} -> [**Want to go straight to the reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v3/) +> [**Want to go straight to the reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v4/) -In Python, the application must define a top-level class named IncomingHandler which inherits from [IncomingHandler](https://spinframework.github.io/spin-python-sdk/v3/http/index.html#spin_sdk.http.IncomingHandler), overriding the `handle_request` method. +In Python, the application must define a top-level class named `WasiHttpHandler030Rc20260315` which inherits from [http.Handler](https://spinframework.github.io/spin-python-sdk/v4/http/index.html#spin_sdk.http.Handler), overriding the `handle_request` method. ```python from spin_sdk import http from spin_sdk.http import Request, Response -class IncomingHandler(http.IncomingHandler): - def handle_request(self, request: Request) -> Response: +class WasiHttpHandler030Rc20260315(http.Handler): + async def handle_request(self, request: Request) -> Response: return Response( 200, {"content-type": "text/plain"}, @@ -243,6 +243,8 @@ class IncomingHandler(http.IncomingHandler): ) ``` +You can find a complete example for handling a HTTP request in the [Python SDK repository on GitHub](https://github.com/spinframework/spin-python-sdk/tree/main/examples/hello). + {{ blockEnd }} {{ startTab "TinyGo"}} diff --git a/content/v4/kv-store-api-guide.md b/content/v4/kv-store-api-guide.md index aa81dd7d..ce5e9eb4 100644 --- a/content/v4/kv-store-api-guide.md +++ b/content/v4/kv-store-api-guide.md @@ -123,19 +123,19 @@ addEventListener('fetch', async (event: FetchEvent) => { {{ startTab "Python"}} -> [**Want to go straight to the reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v3/key_value.html) +> [**Want to go straight to the reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v4/key_value.html) -The key value functions are provided through the `spin_key_value` module in the Python SDK. For example: +The key value functions are provided through the `key_value` module in the Python SDK. For example: ```python from spin_sdk import http, key_value from spin_sdk.http import Request, Response -class IncomingHandler(http.IncomingHandler): - def handle_request(self, request: Request) -> Response: - with key_value.open_default() as store: - store.set("test", bytes("hello world!", "utf-8")) - val = store.get("test") +class WasiHttpHandler030Rc20260315(http.Handler): + async def handle_request(self, request: Request) -> Response: + with await key_value.open_default() as store: + await store.set("test", bytes("hello world!", "utf-8")) + val = await store.get("test") return Response( 200, @@ -148,8 +148,30 @@ class IncomingHandler(http.IncomingHandler): **General Notes** - The Python SDK doesn't surface the `close` operation. It automatically closes all stores at the end of the request; there's no way to close them early. -[`get` **Operation**](https://spinframework.github.io/spin-python-sdk/v3/wit/imports/key_value.html#spin_sdk.wit.imports.key_value.Store.get) -- If a key does not exist, it returns `None` +- As in previous versions of the SDK, top-level [`open`](https://spinframework.github.io/spin-python-sdk/v4/key_value.html#spin_sdk.key_value.open) and [`open_default`](https://spinframework.github.io/spin-python-sdk/v4/key_value.html#spin_sdk.key_value.open_default) convenience helpers exist for opening a store by label, or opening the default store, respectively. + +- Below is a breakdown of the methods surfaced directly from the underlying [spin-key-value-3.0.0 WIT definition](https://spinframework.github.io/spin-python-sdk/v4/wit/imports/spin_key_value_key_value_3_0_0.html): + + [`open` **Operation**](https://spinframework.github.io/spin-python-sdk/v4/wit/imports/spin_key_value_key_value_3_0_0.html#spin_sdk.wit.imports.spin_key_value_key_value_3_0_0.Store.open) + - Open the store with the specified label + + [`get` **Operation**](https://spinframework.github.io/spin-python-sdk/v4/wit/imports/spin_key_value_key_value_3_0_0.html#spin_sdk.wit.imports.spin_key_value_key_value_3_0_0.Store.get) + - If a key does not exist, it returns `None` + + [`set` **Operation**](https://spinframework.github.io/spin-python-sdk/v4/wit/imports/spin_key_value_key_value_3_0_0.html#spin_sdk.wit.imports.spin_key_value_key_value_3_0_0.Store.set) + - Sets a value associated with the specified key, overwriting any existing value. + + [`delete` **Operation**](https://spinframework.github.io/spin-python-sdk/v4/wit/imports/spin_key_value_key_value_3_0_0.html#spin_sdk.wit.imports.spin_key_value_key_value_3_0_0.Store.delete) + - Deletes the tuple with the specified key + + [`exists` **Operation**](https://spinframework.github.io/spin-python-sdk/v4/wit/imports/spin_key_value_key_value_3_0_0.html#spin_sdk.wit.imports.spin_key_value_key_value_3_0_0.Store.exists) + - Return whether a tuple exists for the specified key + + [`get_keys` **Operation**](https://spinframework.github.io/spin-python-sdk/v4/wit/imports/spin_key_value_key_value_3_0_0.html#spin_sdk.wit.imports.spin_key_value_key_value_3_0_0.Store.get_keys) + - The underlying `get_keys` implementation no longer returns a list of strings in v4 of the SDK. Rather, due to the asynchronous nature of this method, it now returns a `Tuple` containing a [StreamReader](https://github.com/bytecodealliance/componentize-py/blob/1b3d2e936868307a48fb70941dcad71b54e844f8/bundled/componentize_py_async_support/streams.py#L101) and a [FutureReader](https://github.com/bytecodealliance/componentize-py/blob/1b3d2e936868307a48fb70941dcad71b54e844f8/bundled/componentize_py_async_support/futures.py#L11). The future _must_ be checked when the stream ends, to determine if the stream ended normally, or was terminated prematurely due to an error. + - However, a new [util](https://spinframework.github.io/spin-python-sdk/v4/util.html) module offers a convenience method called [collect](https://spinframework.github.io/spin-python-sdk/v4/util.html#spin_sdk.util.collect), which does return the list of keys, after reading them from the stream and handling the future, raising an exception if an error results. (Note: This helper can be used to collect values from any `Tuple[StreamReader, FutureReader]` and isn't specific to KV.) + +You can find a complete Python code example using the Key Value store in the [Spin Python SDK repository on GitHub](https://github.com/spinframework/spin-python-sdk/tree/main/examples/spin-kv). {{ blockEnd }} diff --git a/content/v4/language-support-overview.md b/content/v4/language-support-overview.md index b175845c..64d9860b 100644 --- a/content/v4/language-support-overview.md +++ b/content/v4/language-support-overview.md @@ -77,7 +77,7 @@ This page contains information about language support for Spin features: | PostgreSQL | Supported | | [Outbound Redis](./python-components#an-outbound-redis-example) | Supported | | [Serverless AI](./serverless-ai-api-guide) | Supported | -| [MQTT Messaging](./mqtt-outbound) | Not Supported | +| [MQTT Messaging](./mqtt-outbound) | Supported | | **Extensibility** | | Authoring Custom Triggers | Not Supported | diff --git a/content/v4/mqtt-outbound.md b/content/v4/mqtt-outbound.md index fe7cdb81..2396213b 100644 --- a/content/v4/mqtt-outbound.md +++ b/content/v4/mqtt-outbound.md @@ -82,7 +82,24 @@ You can find a complete Rust code example for using outbound MQTT from an HTTP c {{ startTab "Python"}} -MQTT is not available in the current version of the Python SDK. +> [**Want to go straight to the reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v4/mqtt.html) + +To access an MQTT server, use the `open` function. You can then call the `publish` method on the connection to send MQTT messages: + +```python +from spin_sdk import http, mqtt +from spin_sdk.mqtt import Qos +from spin_sdk.http import Request, Response + +class WasiHttpHandler030Rc20260315(http.Handler): + async def handle_request(self, request: Request) -> Response: + with await mqtt.open("mqtt://localhost:1883?client_id=client001", "user", "password", 30) as conn: + await conn.publish("telemetry", bytes("Eureka!", "utf-8"), Qos.AT_LEAST_ONCE) +``` + +For full details of the MQTT API, see the [Spin SDK reference documentation](https://spinframework.github.io/spin-python-sdk/v4/mqtt.html) + +You can find a complete Python code example for using outbound MQTT from an HTTP component in the [Spin Python SDK repository on GitHub](https://github.com/spinframework/spin-python-sdk/tree/main/examples/spin-outbound-mqtt). {{ blockEnd }} diff --git a/content/v4/quickstart.md b/content/v4/quickstart.md index 13df9495..ce034dea 100644 --- a/content/v4/quickstart.md +++ b/content/v4/quickstart.md @@ -498,14 +498,14 @@ The `requirements.txt`, by default, contains the references to the `spin-sdk` an ```bash $ pip3 install -r requirements.txt -Collecting spin-sdk==3.1.0 (from -r requirements.txt (line 1)) - Using cached spin_sdk-3.1.0-py3-none-any.whl.metadata (16 kB) -Collecting componentize-py==0.13.3 (from -r requirements.txt (line 2)) - Using cached componentize_py-0.13.3-cp37-abi3-macosx_10_12_x86_64.whl.metadata (3.4 kB) -Using cached spin_sdk-3.1.0-py3-none-any.whl (94 kB) -Using cached componentize_py-0.13.3-cp37-abi3-macosx_10_12_x86_64.whl (38.8 MB) +Collecting spin-sdk==4.0.0 (from -r requirements.txt (line 1)) + Using cached spin_sdk-4.0.0-py3-none-any.whl.metadata (16 kB) +Collecting componentize-py==0.22.0 (from -r requirements.txt (line 2)) + Using cached componentize_py-0.22.0-cp37-abi3-macosx_10_12_x86_64.whl.metadata (3.4 kB) +Using cached spin_sdk-4.0.0-py3-none-any.whl (94 kB) +Using cached componentize_py-0.22.0-cp37-abi3-macosx_10_12_x86_64.whl (38.8 MB) Installing collected packages: spin-sdk, componentize-py -Successfully installed componentize-py-0.13.3 spin-sdk-3.1.0 +Successfully installed componentize-py-0.22.0 spin-sdk-4.0.0 ``` ## Structure of a Python Component @@ -540,7 +540,8 @@ component = "hello-python" [component.hello-python] source = "app.wasm" [component.hello-python.build] -command = "componentize-py -w spin-http componentize app -o app.wasm" +command = "componentize-py -w spin:up/http-trigger@4.0.0 componentize app -o app.wasm" +watch = ["*.py", "requirements.txt"] ``` This represents a simple Spin HTTP application (triggered by an HTTP request). It has: @@ -556,14 +557,14 @@ takes an HTTP request as a parameter and returns an HTTP response. ```python -from spin_sdk.http import IncomingHandler, Request, Response +from spin_sdk.http import Handler, Request, Response -class IncomingHandler(IncomingHandler): - def handle_request(self, request: Request) -> Response: +class WasiHttpHandler030Rc20260315(Handler): + async def handle_request(self, request: Request) -> Response: return Response( 200, {"content-type": "text/plain"}, - bytes("Hello from the Python SDK!", "utf-8") + bytes("Hello from Python!", "utf-8") ) ``` @@ -782,7 +783,8 @@ Then run: ```bash $ spin build -Executing the build command for component hello-python: "componentize-py -w spin-http componentize app -o app.wasm" +Building component hello-python with `componentize-py -w spin:up/http-trigger@4.0.0 componentize app -o app.wasm +Component built successfully Finished building all Spin components ``` @@ -796,7 +798,7 @@ If you would like to know what build command Spin runs for a component, you can ```toml [component.hello-python.build] -command = "componentize-py -w spin-http componentize app -o app.wasm" +command = "componentize-py -w spin:up/http-trigger@4.0.0 componentize app -o app.wasm" ``` You can always run this command manually; `spin build` is a shortcut. diff --git a/content/v4/rdbms-storage.md b/content/v4/rdbms-storage.md index 96d2d9a3..57e1360f 100644 --- a/content/v4/rdbms-storage.md +++ b/content/v4/rdbms-storage.md @@ -144,17 +144,16 @@ addEventListener('fetch', async (event: FetchEvent) => { {{ startTab "Python"}} -> [**Want to go straight to the reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v3/) +> [**Want to go straight to the reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v4/) The code below is an [Outbound MySQL example](https://github.com/spinframework/spin-python-sdk/tree/main/examples/spin-mysql). There is also an outbound [PostgreSQL example](https://github.com/spinframework/spin-python-sdk/tree/main/examples/spin-postgres) available. ```python -from spin_sdk import http +from spin_sdk import http, mysql from spin_sdk.http import Request, Response -from spin_sdk import mysql -class IncomingHandler(http.IncomingHandler): - def handle_request(self, request: Request) -> Response: +class WasiHttpHandler030Rc20260315(http.Handler): + async def handle_request(self, request: Request) -> Response: with mysql.open("mysql://root:@127.0.0.1/spin_dev") as db: print(db.query("select * from test", [])) diff --git a/content/v4/redis-outbound.md b/content/v4/redis-outbound.md index fe73b1cb..6c214b94 100644 --- a/content/v4/redis-outbound.md +++ b/content/v4/redis-outbound.md @@ -115,23 +115,28 @@ You can find a complete TypeScript example for using outbound Redis from an HTTP {{ startTab "Python"}} -> [**Want to go straight to the reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v3/redis.html) +> [**Want to go straight to the reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v4/redis.html) -Redis functions are available in [the `redis` module](https://spinframework.github.io/spin-python-sdk/v3/redis.html). The function names are prefixed `redis_`. You must pass the Redis instance address to _each_ operation as its first parameter. For example: +Redis functions are available in [the `redis` module](https://spinframework.github.io/spin-python-sdk/v4/redis.html). You must pass the Redis instance address to _each_ operation as its first parameter. For example: ```python -from spin_sdk import redis -with redis.open("redis://localhost:6379") as db: - val = db.get("test") +from spin_sdk import http, redis +from spin_sdk.http import Request, Response + +class WasiHttpHandler030Rc20260315(http.Handler): + async def handle_request(self, request: Request) -> Response: + with await redis.open("redis://localhost:6379") as db: + print(await db.get("test")) ``` **General Notes** * Address and key parameters are strings (`str`). -* Bytes parameters and return values are `bytes`. (You can pass literal strings using the `b` prefix, e.g. `redis_set(address, key, b"hello")`.) -* Numeric return values are of type `int64`. +* Bytes parameters and return values are `bytes`. (You can pass literal strings using the `b` prefix, e.g. `set(address, key, b"hello")`.) +* Numeric return values are of type `int`. * Lists are passed and returned as Python lists. * Errors are signalled through exceptions. +* For a deep dive into the all of the methods available from this module, see the [spin_redis_3.0.0 WIT documentation](https://spinframework.github.io/spin-python-sdk/v4/wit/imports/spin_redis_redis_3_0_0.html). You can find a complete Python code example for using outbound Redis from an HTTP component in the [Python SDK repository on GitHub](https://github.com/spinframework/spin-python-sdk/tree/main/examples/spin-redis). Please also see this, related, [outbound Redis (using Python) section](./python-components#an-outbound-redis-example). diff --git a/content/v4/redis-trigger.md b/content/v4/redis-trigger.md index 1975b388..f925ab6e 100644 --- a/content/v4/redis-trigger.md +++ b/content/v4/redis-trigger.md @@ -94,15 +94,18 @@ The JavaScript/TypeScript SDK doesn't currently support Redis components. Pleas {{ startTab "Python"}} -In Python, the handler needs to implement the [`InboundRedis`](https://spinframework.github.io/spin-python-sdk/v3/wit/exports/index.html#spin_sdk.wit.exports.InboundRedis) class, and override the `handle_message` method: +In Python, the handler needs to implement the [`SpinRedisInboundRedis300`](https://spinframework.github.io/spin-python-sdk/v4/wit/exports/index.html#spin_sdk.wit.exports.SpinRedisInboundRedis300) class, and override the `handle_message` method: ```python from spin_sdk.wit import exports -class InboundRedis(exports.InboundRedis): - def handle_message(self, message: bytes): + +class SpinRedisInboundRedis300(exports.SpinRedisInboundRedis300): + async def handle_message(self, message: bytes) -> None: print(message) ``` +You can find a complete Python code example using the Redis trigger in the [Spin Python SDK repository on GitHub](https://github.com/spinframework/spin-python-sdk/tree/main/examples/redis-trigger). + {{ blockEnd }} {{ startTab "TinyGo"}} diff --git a/content/v4/serverless-ai-api-guide.md b/content/v4/serverless-ai-api-guide.md index 3812e970..52efbc04 100644 --- a/content/v4/serverless-ai-api-guide.md +++ b/content/v4/serverless-ai-api-guide.md @@ -158,30 +158,33 @@ addEventListener('fetch', async (event: FetchEvent) => { {{ startTab "Python"}} -> [**Want to go straight to the reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v3/llm.html) +> [**Want to go straight to the reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v4/llm.html) ```python -from spin_sdk import http +from spin_sdk import http, llm from spin_sdk.http import Request, Response -from spin_sdk import llm -class IncomingHandler(http.IncomingHandler): - def handle_request(self, request: Request) -> Response: +class WasiHttpHandler030Rc20260315(http.Handler): + async def handle_request(self, request: Request) -> Response: prompt="You are a stand up comedy writer. Tell me a joke." result = llm.infer("llama2-chat", prompt) - return Response(200, - {"content-type": "application/json"}, - bytes(result.text, "utf-8")) + + return Response( + 200, + {"content-type": "text/plain"}, + bytes(result.text, "utf-8") + ) ``` **General Notes** -[`infer` operation](https://spinframework.github.io/spin-python-sdk/v3/llm.html#spin_sdk.llm.infer): - +[`infer` operation](https://spinframework.github.io/spin-python-sdk/v4/llm.html#spin_sdk.llm.infer): - The model name is passed in as a string (as shown above; `"llama2-chat"`). -[`infer_with_options` operation](https://spinframework.github.io/spin-python-sdk/v3/llm.html#spin_sdk.llm.infer_with_options): -- It takes in a model name, prompt text, and optionally a [parameter object](https://spinframework.github.io/spin-python-sdk/v3/llm.html#spin_sdk.llm.InferencingParams) to control the inferencing. +[`infer_with_options` operation](https://spinframework.github.io/spin-python-sdk/v4/llm.html#spin_sdk.llm.infer_with_options): +- It takes in a model name, prompt text, and optionally a [parameter object](https://spinframework.github.io/spin-python-sdk/v4/llm.html#spin_sdk.llm.InferencingParams) to control the inferencing. + +You can find a complete Python code example using the LLM module in the [Spin Python SDK repository on GitHub](https://github.com/spinframework/spin-python-sdk/tree/main/examples/spin-llm). {{ blockEnd }} diff --git a/content/v4/sqlite-api-guide.md b/content/v4/sqlite-api-guide.md index a4c59ff5..e7c6b6b1 100644 --- a/content/v4/sqlite-api-guide.md +++ b/content/v4/sqlite-api-guide.md @@ -156,19 +156,19 @@ addEventListener('fetch', async (event: FetchEvent) => { {{ startTab "Python"}} -> [**Want to go straight to the reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v3/sqlite.html) +> [**Want to go straight to the reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v4/sqlite.html) -To use SQLite functions, use the `sqlite` module in the Python SDK. The [`sqlite_open`](https://spinframework.github.io/spin-python-sdk/v3/sqlite.html#spin_sdk.sqlite.open) and [`sqlite_open_default`](https://spinframework.github.io/spin-python-sdk/v3/sqlite.html#spin_sdk.sqlite.open_default) functions return a [connection object](https://spinframework.github.io/spin-python-sdk/v3/wit/imports/sqlite.html#spin_sdk.wit.imports.sqlite.Connection). The connection object provides the [`execute` method](https://spinframework.github.io/spin-python-sdk/v3/wit/imports/sqlite.html#spin_sdk.wit.imports.sqlite.Connection.execute) as described above. For example: +To use SQLite functions, use the `sqlite` module in the Python SDK. The [`open`](https://spinframework.github.io/spin-python-sdk/v4/sqlite.html#spin_sdk.sqlite.open) and [`open_default`](https://spinframework.github.io/spin-python-sdk/v4/sqlite.html#spin_sdk.sqlite.open_default) functions return a [`Connection` object](https://spinframework.github.io/spin-python-sdk/v4/wit/imports/spin_sqlite_sqlite_3_1_0.html#spin_sdk.wit.imports.spin_sqlite_sqlite_3_1_0.Connection). The `Connection` object provides the [`execute` method](https://spinframework.github.io/spin-python-sdk/v4/wit/imports/spin_sqlite_sqlite_3_1_0.html#spin_sdk.wit.imports.spin_sqlite_sqlite_3_1_0.Connection.execute) as described above. For example: ```python from spin_sdk import http, sqlite from spin_sdk.http import Request, Response -from spin_sdk.sqlite import ValueInteger +from spin_sdk.sqlite import Value_Integer -class IncomingHandler(http.IncomingHandler): - def handle_request(self, request: Request) -> Response: - with sqlite.open_default() as db: - result = db.execute("SELECT * FROM todos WHERE id > (?);", [ValueInteger(1)]) +class WasiHttpHandler030Rc20260315(http.Handler): + async def handle_request(self, request: Request) -> Response: + with await sqlite.open_default() as db: + result = db.execute("SELECT * FROM todos WHERE id > (?);", [Value_Integer(1)]) rows = result.rows return Response( @@ -179,10 +179,12 @@ class IncomingHandler(http.IncomingHandler): ``` **General Notes** -* The `execute` method returns [a `QueryResult` object](https://spinframework.github.io/spin-python-sdk/v3/wit/imports/sqlite.html#spin_sdk.wit.imports.sqlite.QueryResult) with `rows` and `columns` methods. `columns` returns a list of strings representing column names. `rows` is an array of rows, each of which is an array of [`RowResult`](https://spinframework.github.io/spin-python-sdk/v3/wit/imports/sqlite.html#spin_sdk.wit.imports.sqlite.RowResult) in the same order as `columns`. -* The connection object doesn't surface the `close` function. +* The `execute` method returns [a `QueryResult` object](https://spinframework.github.io/spin-python-sdk/v4/wit/imports/spin_sqlite_sqlite_3_1_0.html#spin_sdk.wit.imports.spin_sqlite_sqlite_3_1_0.QueryResult) with `rows` and `columns` methods. `columns` returns a list of strings representing column names. `rows` is an array of rows, each of which is an array of [`RowResult`](https://spinframework.github.io/spin-python-sdk/v4/wit/imports/spin_sqlite_sqlite_3_1_0.html#spin_sdk.wit.imports.spin_sqlite_sqlite_3_1_0.RowResult) in the same order as `columns`. +* The `Connection` object doesn't surface the `close` function. * Errors are surfaced as exceptions. +You can find a complete Python code example using SQLite storage in the [Spin Python SDK repository on GitHub](https://github.com/spinframework/spin-python-sdk/tree/main/examples/spin-sqlite). + {{ blockEnd }} {{ startTab "TinyGo"}} diff --git a/content/v4/variables.md b/content/v4/variables.md index ccc9ab67..86433c0b 100644 --- a/content/v4/variables.md +++ b/content/v4/variables.md @@ -173,24 +173,24 @@ addEventListener('fetch', async (event: FetchEvent) => { {{ startTab "Python"}} -> [**Want to go straight to the reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v3/variables.html) +> [**Want to go straight to the reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v4/variables.html) -The `variables` module has a function called `get`(https://spinframework.github.io/spin-python-sdk/v3/variables.html#spin_sdk.variables.get). +The `variables` module has a function called `get`(https://spinframework.github.io/spin-python-sdk/v4/variables.html#spin_sdk.variables.get). ```py -from spin_sdk.http import IncomingHandler, Request, Response, send -from spin_sdk import variables - -class IncomingHandler(IncomingHandler): - def handle_request(self, request: Request) -> Response: - token = variables.get("token") - api_uri = variables.get("api_uri") - version = variables.get("version") +from spin_sdk import http, variables +from spin_sdk.http import Request, Response + +class WasiHttpHandler030Rc20260315(http.Handler): + async def handle_request(self, request: Request) -> Response: + token = await variables.get("token") + api_uri = await variables.get("api_uri") + version = await variables.get("version") versioned_api_uri = f"{api_uri}/{version}" headers = { "Authorization": f"Bearer {token}" } - response = send(Request("GET", versioned_api_uri, headers, None)) + response = await send(Request("GET", versioned_api_uri, headers, None)) # Do something with the response ... return Response( 200, @@ -199,6 +199,8 @@ class IncomingHandler(IncomingHandler): ) ``` +You can find a complete Python code example using Variables in the [Spin Python SDK repository on GitHub](https://github.com/spinframework/spin-python-sdk/tree/main/examples/spin-variables). + {{ blockEnd }} {{ startTab "TinyGo"}} diff --git a/content/v4/writing-apps.md b/content/v4/writing-apps.md index a0bde37a..109dcc82 100644 --- a/content/v4/writing-apps.md +++ b/content/v4/writing-apps.md @@ -217,7 +217,7 @@ HTTP path: /... Choose the `http-py` template to create a new HTTP application. -> The Python development kit doesn't yet support Redis applications. +> The Python development kit doesn't yet offer a Redis application template. From ac9a62ddb102f6723a67c08103c4d8637eed5fd5 Mon Sep 17 00:00:00 2001 From: Vaughn Dice Date: Thu, 9 Apr 2026 11:31:01 -0600 Subject: [PATCH 2/4] Apply suggestions from code review Co-authored-by: itowlson Signed-off-by: Vaughn Dice --- content/v4/kv-store-api-guide.md | 10 +++++----- content/v4/redis-outbound.md | 6 ++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/content/v4/kv-store-api-guide.md b/content/v4/kv-store-api-guide.md index ce5e9eb4..435709f3 100644 --- a/content/v4/kv-store-api-guide.md +++ b/content/v4/kv-store-api-guide.md @@ -148,7 +148,7 @@ class WasiHttpHandler030Rc20260315(http.Handler): **General Notes** - The Python SDK doesn't surface the `close` operation. It automatically closes all stores at the end of the request; there's no way to close them early. -- As in previous versions of the SDK, top-level [`open`](https://spinframework.github.io/spin-python-sdk/v4/key_value.html#spin_sdk.key_value.open) and [`open_default`](https://spinframework.github.io/spin-python-sdk/v4/key_value.html#spin_sdk.key_value.open_default) convenience helpers exist for opening a store by label, or opening the default store, respectively. +- To open the default key-value store, you can use the [`key_value.open_default`](https://spinframework.github.io/spin-python-sdk/v4/key_value.html#spin_sdk.key_value.open_default) function. You can use [`key_value.open`](https://spinframework.github.io/spin-python-sdk/v4/key_value.html#spin_sdk.key_value.open) to open any store by label. - Below is a breakdown of the methods surfaced directly from the underlying [spin-key-value-3.0.0 WIT definition](https://spinframework.github.io/spin-python-sdk/v4/wit/imports/spin_key_value_key_value_3_0_0.html): @@ -162,14 +162,14 @@ class WasiHttpHandler030Rc20260315(http.Handler): - Sets a value associated with the specified key, overwriting any existing value. [`delete` **Operation**](https://spinframework.github.io/spin-python-sdk/v4/wit/imports/spin_key_value_key_value_3_0_0.html#spin_sdk.wit.imports.spin_key_value_key_value_3_0_0.Store.delete) - - Deletes the tuple with the specified key + - Deletes the specified item from the store [`exists` **Operation**](https://spinframework.github.io/spin-python-sdk/v4/wit/imports/spin_key_value_key_value_3_0_0.html#spin_sdk.wit.imports.spin_key_value_key_value_3_0_0.Store.exists) - - Return whether a tuple exists for the specified key + - Return whether the specified key is present in the store [`get_keys` **Operation**](https://spinframework.github.io/spin-python-sdk/v4/wit/imports/spin_key_value_key_value_3_0_0.html#spin_sdk.wit.imports.spin_key_value_key_value_3_0_0.Store.get_keys) - - The underlying `get_keys` implementation no longer returns a list of strings in v4 of the SDK. Rather, due to the asynchronous nature of this method, it now returns a `Tuple` containing a [StreamReader](https://github.com/bytecodealliance/componentize-py/blob/1b3d2e936868307a48fb70941dcad71b54e844f8/bundled/componentize_py_async_support/streams.py#L101) and a [FutureReader](https://github.com/bytecodealliance/componentize-py/blob/1b3d2e936868307a48fb70941dcad71b54e844f8/bundled/componentize_py_async_support/futures.py#L11). The future _must_ be checked when the stream ends, to determine if the stream ended normally, or was terminated prematurely due to an error. - - However, a new [util](https://spinframework.github.io/spin-python-sdk/v4/util.html) module offers a convenience method called [collect](https://spinframework.github.io/spin-python-sdk/v4/util.html#spin_sdk.util.collect), which does return the list of keys, after reading them from the stream and handling the future, raising an exception if an error results. (Note: This helper can be used to collect values from any `Tuple[StreamReader, FutureReader]` and isn't specific to KV.) + - The underlying `get_keys` implementation no longer returns a list of strings in v4 of the SDK. Rather, due to the asynchronous nature of this method, it now returns a `Tuple` containing a [StreamReader](https://github.com/bytecodealliance/componentize-py/blob/1b3d2e936868307a48fb70941dcad71b54e844f8/bundled/componentize_py_async_support/streams.py#L101) and a [FutureReader](https://github.com/bytecodealliance/componentize-py/blob/1b3d2e936868307a48fb70941dcad71b54e844f8/bundled/componentize_py_async_support/futures.py#L11). You _must_ check when the stream ends, to determine if the stream ended normally, or was terminated prematurely due to an error. + - However, a new [util](https://spinframework.github.io/spin-python-sdk/v4/util.html) module offers a convenience method called [collect](https://spinframework.github.io/spin-python-sdk/v4/util.html#spin_sdk.util.collect), which does return the list of keys, after reading them from the stream and handling the future, raising an exception if an error results. (You can use this helper to collect values from any `Tuple[StreamReader, FutureReader]` - it isn't specific to KV.) You can find a complete Python code example using the Key Value store in the [Spin Python SDK repository on GitHub](https://github.com/spinframework/spin-python-sdk/tree/main/examples/spin-kv). diff --git a/content/v4/redis-outbound.md b/content/v4/redis-outbound.md index 6c214b94..612f8910 100644 --- a/content/v4/redis-outbound.md +++ b/content/v4/redis-outbound.md @@ -117,7 +117,9 @@ You can find a complete TypeScript example for using outbound Redis from an HTTP > [**Want to go straight to the reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v4/redis.html) -Redis functions are available in [the `redis` module](https://spinframework.github.io/spin-python-sdk/v4/redis.html). You must pass the Redis instance address to _each_ operation as its first parameter. For example: +Redis functions are available in [the `redis` module](https://spinframework.github.io/spin-python-sdk/v4/redis.html). + +To open a connection to a Redis instance, use the `redis.open` function. You can then call methods on the connection object to work with the Redis instance. For example: ```python from spin_sdk import http, redis @@ -132,7 +134,7 @@ class WasiHttpHandler030Rc20260315(http.Handler): **General Notes** * Address and key parameters are strings (`str`). -* Bytes parameters and return values are `bytes`. (You can pass literal strings using the `b` prefix, e.g. `set(address, key, b"hello")`.) +* Bytes parameters and return values are `bytes`. (You can pass literal strings using the `b` prefix, e.g. `set(key, b"hello")`.) * Numeric return values are of type `int`. * Lists are passed and returned as Python lists. * Errors are signalled through exceptions. From 8e2bf4c85e847428aeed41c506e12384a3906842 Mon Sep 17 00:00:00 2001 From: Vaughn Dice Date: Thu, 9 Apr 2026 11:33:19 -0600 Subject: [PATCH 3/4] docs(content/v4): further Python SDK/example amendments Signed-off-by: Vaughn Dice --- content/v4/http-outbound.md | 4 ++-- content/v4/http-trigger.md | 4 ++-- content/v4/kv-store-api-guide.md | 7 ++++--- content/v4/mqtt-outbound.md | 2 +- content/v4/quickstart.md | 2 +- content/v4/rdbms-storage.md | 14 ++++++++------ content/v4/redis-outbound.md | 2 +- content/v4/redis-trigger.md | 4 ++-- content/v4/serverless-ai-api-guide.md | 2 +- content/v4/sqlite-api-guide.md | 2 +- content/v4/variables.md | 2 +- 11 files changed, 24 insertions(+), 21 deletions(-) diff --git a/content/v4/http-outbound.md b/content/v4/http-outbound.md index b02b608a..284f5c4f 100644 --- a/content/v4/http-outbound.md +++ b/content/v4/http-outbound.md @@ -178,13 +178,13 @@ You can find a complete example of using outbound HTTP in the JavaScript SDK rep > [**Want to go straight to the reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v4/http/index.html) -HTTP functions and classes are available in the `http` module. The function name is [`send`](https://spinframework.github.io/spin-python-sdk/v4/http/index.html#spin_sdk.http.send). The [request type](https://spinframework.github.io/spin-python-sdk/http/index.html#spin_sdk.http.Request) is `Request`, and the [response type](https://spinframework.github.io/spin-python-sdk/v4/http/index.html#spin_sdk.http.Response) is `Response`. For example: +HTTP functions and classes are available in the `http` module. The function name is [`send`](https://spinframework.github.io/spin-python-sdk/v4/http/index.html#spin_sdk.http.send). The [request type](https://spinframework.github.io/spin-python-sdk/v4/http/index.html#spin_sdk.http.Request) is `Request`, and the [response type](https://spinframework.github.io/spin-python-sdk/v4/http/index.html#spin_sdk.http.Response) is `Response`. For example: ```python from spin_sdk import http from spin_sdk.http import Request, Response, send -class WasiHttpHandler030Rc20260315(http.Handler): +class HttpHandler(http.Handler): async def handle_request(self, request: Request) -> Response: response = await send(Request("GET", "https://random-data-api.fermyon.app/animals/json", {}, None)) return response diff --git a/content/v4/http-trigger.md b/content/v4/http-trigger.md index d0992cb6..83137f33 100644 --- a/content/v4/http-trigger.md +++ b/content/v4/http-trigger.md @@ -228,13 +228,13 @@ addEventListener('fetch', async (event: FetchEvent) => { > [**Want to go straight to the reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v4/) -In Python, the application must define a top-level class named `WasiHttpHandler030Rc20260315` which inherits from [http.Handler](https://spinframework.github.io/spin-python-sdk/v4/http/index.html#spin_sdk.http.Handler), overriding the `handle_request` method. +In Python, the application must define a top-level class named `HttpHandler` which inherits from [http.Handler](https://spinframework.github.io/spin-python-sdk/v4/http/index.html#spin_sdk.http.Handler), overriding the `handle_request` method. ```python from spin_sdk import http from spin_sdk.http import Request, Response -class WasiHttpHandler030Rc20260315(http.Handler): +class HttpHandler(http.Handler): async def handle_request(self, request: Request) -> Response: return Response( 200, diff --git a/content/v4/kv-store-api-guide.md b/content/v4/kv-store-api-guide.md index 435709f3..0d7e8f45 100644 --- a/content/v4/kv-store-api-guide.md +++ b/content/v4/kv-store-api-guide.md @@ -131,7 +131,7 @@ The key value functions are provided through the `key_value` module in the Pytho from spin_sdk import http, key_value from spin_sdk.http import Request, Response -class WasiHttpHandler030Rc20260315(http.Handler): +class HttpHandler(http.Handler): async def handle_request(self, request: Request) -> Response: with await key_value.open_default() as store: await store.set("test", bytes("hello world!", "utf-8")) @@ -168,8 +168,9 @@ class WasiHttpHandler030Rc20260315(http.Handler): - Return whether the specified key is present in the store [`get_keys` **Operation**](https://spinframework.github.io/spin-python-sdk/v4/wit/imports/spin_key_value_key_value_3_0_0.html#spin_sdk.wit.imports.spin_key_value_key_value_3_0_0.Store.get_keys) - - The underlying `get_keys` implementation no longer returns a list of strings in v4 of the SDK. Rather, due to the asynchronous nature of this method, it now returns a `Tuple` containing a [StreamReader](https://github.com/bytecodealliance/componentize-py/blob/1b3d2e936868307a48fb70941dcad71b54e844f8/bundled/componentize_py_async_support/streams.py#L101) and a [FutureReader](https://github.com/bytecodealliance/componentize-py/blob/1b3d2e936868307a48fb70941dcad71b54e844f8/bundled/componentize_py_async_support/futures.py#L11). You _must_ check when the stream ends, to determine if the stream ended normally, or was terminated prematurely due to an error. - - However, a new [util](https://spinframework.github.io/spin-python-sdk/v4/util.html) module offers a convenience method called [collect](https://spinframework.github.io/spin-python-sdk/v4/util.html#spin_sdk.util.collect), which does return the list of keys, after reading them from the stream and handling the future, raising an exception if an error results. (You can use this helper to collect values from any `Tuple[StreamReader, FutureReader]` - it isn't specific to KV.) + - Returns a `Tuple` containing a [StreamReader](https://github.com/bytecodealliance/componentize-py/blob/1b3d2e936868307a48fb70941dcad71b54e844f8/bundled/componentize_py_async_support/streams.py#L101) and a [FutureReader](https://github.com/bytecodealliance/componentize-py/blob/1b3d2e936868307a48fb70941dcad71b54e844f8/bundled/componentize_py_async_support/futures.py#L11). You _must_ check when the stream ends, to determine if the stream ended normally, or was terminated prematurely due to an error. + + > If you're familiar with previous versions of the Python SDK, note that `get_keys` no longer returns a list. To get the keys as a list, use `await util.collect(await store.get_keys())`. See [collect](https://spinframework.github.io/spin-python-sdk/v4/util.html#spin_sdk.util.collect) for more details. You can find a complete Python code example using the Key Value store in the [Spin Python SDK repository on GitHub](https://github.com/spinframework/spin-python-sdk/tree/main/examples/spin-kv). diff --git a/content/v4/mqtt-outbound.md b/content/v4/mqtt-outbound.md index 2396213b..0bd78b88 100644 --- a/content/v4/mqtt-outbound.md +++ b/content/v4/mqtt-outbound.md @@ -91,7 +91,7 @@ from spin_sdk import http, mqtt from spin_sdk.mqtt import Qos from spin_sdk.http import Request, Response -class WasiHttpHandler030Rc20260315(http.Handler): +class HttpHandler(http.Handler): async def handle_request(self, request: Request) -> Response: with await mqtt.open("mqtt://localhost:1883?client_id=client001", "user", "password", 30) as conn: await conn.publish("telemetry", bytes("Eureka!", "utf-8"), Qos.AT_LEAST_ONCE) diff --git a/content/v4/quickstart.md b/content/v4/quickstart.md index ce034dea..1dfaf4fa 100644 --- a/content/v4/quickstart.md +++ b/content/v4/quickstart.md @@ -559,7 +559,7 @@ takes an HTTP request as a parameter and returns an HTTP response. ```python from spin_sdk.http import Handler, Request, Response -class WasiHttpHandler030Rc20260315(Handler): +class HttpHandler(Handler): async def handle_request(self, request: Request) -> Response: return Response( 200, diff --git a/content/v4/rdbms-storage.md b/content/v4/rdbms-storage.md index 57e1360f..5ebf276d 100644 --- a/content/v4/rdbms-storage.md +++ b/content/v4/rdbms-storage.md @@ -146,17 +146,17 @@ addEventListener('fetch', async (event: FetchEvent) => { > [**Want to go straight to the reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v4/) -The code below is an [Outbound MySQL example](https://github.com/spinframework/spin-python-sdk/tree/main/examples/spin-mysql). There is also an outbound [PostgreSQL example](https://github.com/spinframework/spin-python-sdk/tree/main/examples/spin-postgres) available. +The code below shows use of the [Postgres](https://spinframework.github.io/spin-python-sdk/v4/postgres.html) module and its [open](https://spinframework.github.io/spin-python-sdk/v4/postgres.html#spin_sdk.postgres.open) function for opening a connection to the database: ```python -from spin_sdk import http, mysql +from spin_sdk import http, postgres from spin_sdk.http import Request, Response -class WasiHttpHandler030Rc20260315(http.Handler): +class HttpHandler(http.Handler): async def handle_request(self, request: Request) -> Response: - with mysql.open("mysql://root:@127.0.0.1/spin_dev") as db: - print(db.query("select * from test", [])) - + with await postgres.open("user=postgres dbname=spin_dev host=localhost sslmode=disable password=password") as db: + print(db.query("SELECT * FROM test", [])) + return Response( 200, {"content-type": "text/plain"}, @@ -164,6 +164,8 @@ class WasiHttpHandler030Rc20260315(http.Handler): ) ``` +You can find a complete outbound PostgreSQL example in the [Spin Python SDK repository on GitHub](https://github.com/spinframework/spin-python-sdk/tree/main/examples/spin-postgres). There is also an [Outbound MySQL example](https://github.com/spinframework/spin-python-sdk/tree/main/examples/spin-mysql) available. + {{ blockEnd }} {{ startTab "TinyGo"}} diff --git a/content/v4/redis-outbound.md b/content/v4/redis-outbound.md index 612f8910..f74f295d 100644 --- a/content/v4/redis-outbound.md +++ b/content/v4/redis-outbound.md @@ -125,7 +125,7 @@ To open a connection to a Redis instance, use the `redis.open` function. You can from spin_sdk import http, redis from spin_sdk.http import Request, Response -class WasiHttpHandler030Rc20260315(http.Handler): +class HttpHandler(http.Handler): async def handle_request(self, request: Request) -> Response: with await redis.open("redis://localhost:6379") as db: print(await db.get("test")) diff --git a/content/v4/redis-trigger.md b/content/v4/redis-trigger.md index f925ab6e..d7ddf05d 100644 --- a/content/v4/redis-trigger.md +++ b/content/v4/redis-trigger.md @@ -94,12 +94,12 @@ The JavaScript/TypeScript SDK doesn't currently support Redis components. Pleas {{ startTab "Python"}} -In Python, the handler needs to implement the [`SpinRedisInboundRedis300`](https://spinframework.github.io/spin-python-sdk/v4/wit/exports/index.html#spin_sdk.wit.exports.SpinRedisInboundRedis300) class, and override the `handle_message` method: +In Python, the handler needs to implement the [`RedisHandler`](https://spinframework.github.io/spin-python-sdk/v4/wit/exports/index.html#spin_sdk.wit.exports.RedisHandler) class, and override the `handle_message` method: ```python from spin_sdk.wit import exports -class SpinRedisInboundRedis300(exports.SpinRedisInboundRedis300): +class RedisHandler(exports.RedisHandler): async def handle_message(self, message: bytes) -> None: print(message) ``` diff --git a/content/v4/serverless-ai-api-guide.md b/content/v4/serverless-ai-api-guide.md index 52efbc04..b8a23700 100644 --- a/content/v4/serverless-ai-api-guide.md +++ b/content/v4/serverless-ai-api-guide.md @@ -164,7 +164,7 @@ addEventListener('fetch', async (event: FetchEvent) => { from spin_sdk import http, llm from spin_sdk.http import Request, Response -class WasiHttpHandler030Rc20260315(http.Handler): +class HttpHandler(http.Handler): async def handle_request(self, request: Request) -> Response: prompt="You are a stand up comedy writer. Tell me a joke." result = llm.infer("llama2-chat", prompt) diff --git a/content/v4/sqlite-api-guide.md b/content/v4/sqlite-api-guide.md index e7c6b6b1..0b81396e 100644 --- a/content/v4/sqlite-api-guide.md +++ b/content/v4/sqlite-api-guide.md @@ -165,7 +165,7 @@ from spin_sdk import http, sqlite from spin_sdk.http import Request, Response from spin_sdk.sqlite import Value_Integer -class WasiHttpHandler030Rc20260315(http.Handler): +class HttpHandler(http.Handler): async def handle_request(self, request: Request) -> Response: with await sqlite.open_default() as db: result = db.execute("SELECT * FROM todos WHERE id > (?);", [Value_Integer(1)]) diff --git a/content/v4/variables.md b/content/v4/variables.md index 86433c0b..44d7f1bd 100644 --- a/content/v4/variables.md +++ b/content/v4/variables.md @@ -181,7 +181,7 @@ The `variables` module has a function called `get`(https://spinframework.github. from spin_sdk import http, variables from spin_sdk.http import Request, Response -class WasiHttpHandler030Rc20260315(http.Handler): +class HttpHandler(http.Handler): async def handle_request(self, request: Request) -> Response: token = await variables.get("token") api_uri = await variables.get("api_uri") From 35340dbfd598156b702f146a865ff85ec97e1f78 Mon Sep 17 00:00:00 2001 From: Vaughn Dice Date: Thu, 9 Apr 2026 16:05:07 -0600 Subject: [PATCH 4/4] docs(content/v4): python-components.md snippets/links updates Signed-off-by: Vaughn Dice --- content/v4/language-support-overview.md | 2 +- content/v4/python-components.md | 64 +++++++++++++------------ 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/content/v4/language-support-overview.md b/content/v4/language-support-overview.md index 64d9860b..847b16a9 100644 --- a/content/v4/language-support-overview.md +++ b/content/v4/language-support-overview.md @@ -61,7 +61,7 @@ This page contains information about language support for Spin features: {{ startTab "Python"}} -**[📄 Visit the Python Spin SDK reference documentation](https://spinframework.github.io/spin-python-sdk/v3) to see specific modules, functions, variables and syntax relating to the following Python SDK.** +**[📄 Visit the Python Spin SDK reference documentation](https://spinframework.github.io/spin-python-sdk/v4) to see specific modules, functions, variables and syntax relating to the following Python SDK.** | Feature | SDK Supported? | |-----|-----| diff --git a/content/v4/python-components.md b/content/v4/python-components.md index a73fd65b..fd9f1ad7 100644 --- a/content/v4/python-components.md +++ b/content/v4/python-components.md @@ -35,7 +35,7 @@ With Python being a very p > This guide assumes you are familiar with the Python programming language, but if you are just getting started, be sure to check out the official Python documentation and comprehensive language reference. -[**Want to go straight to the Spin SDK reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v3) +[**Want to go straight to the Spin SDK reference documentation?** Find it here.](https://spinframework.github.io/spin-python-sdk/v4) ## Prerequisite @@ -49,7 +49,7 @@ If you do not have Python 3.10 or later, you can install it by following the ins ## Spin's Python HTTP Request Handler Template -Spin's Python HTTP Request Handler Template can be installed from [spin-python-sdk repository](https://github.com/spinframework/spin-python-sdk/tree/main/) using the following command: +Spin's Python HTTP Request Handler Template can be installed from [spin-python-sdk repository](https://github.com/spinframework/spin-python-sdk) using the following command: @@ -170,22 +170,22 @@ component = "hello-world" [component.hello-world] source = "app.wasm" [component.hello-world.build] -command = "componentize-py -w spin-http componentize app -o app.wasm" +command = "componentize-py -w spin:up/http-trigger@4.0.0 componentize app -o app.wasm" ``` ## A Simple HTTP Components Example In Spin, HTTP components are triggered by the occurrence of an HTTP request and must return an HTTP response at the end of their execution. Components can be built in any language that compiles to WASI. If you would like additional information about building HTTP applications you may find [the HTTP trigger page](./http-trigger.md) useful. -Building a Spin HTTP component using the Python SDK means defining a top-level class named IncomingHandler which inherits from [`IncomingHandler`](https://spinframework.github.io/spin-python-sdk/v3/wit/exports/index.html#spin_sdk.wit.exports.IncomingHandler), overriding the `handle_request` method. Here is an example of the default Python code which the previous `spin new` created for us; a simple example of a request/response: +Building a Spin HTTP component using the Python SDK means defining a top-level class named HttpHandler which inherits from [`HttpHandler`](https://spinframework.github.io/spin-python-sdk/v4/wit/exports/index.html#spin_sdk.wit.exports.HttpHandler), overriding the `handle_request` method. Here is an example of the default Python code which the previous `spin new` created for us; a simple example of a request/response: ```python -from spin_sdk.http import IncomingHandler, Request, Response +from spin_sdk.http import Handler, Request, Response -class IncomingHandler(IncomingHandler): - def handle_request(self, request: Request) -> Response: +class HttpHandler(Handler): + async def handle_request(self, request: Request) -> Response: return Response( 200, {"content-type": "text/plain"}, @@ -241,8 +241,8 @@ import json from spin_sdk import http from spin_sdk.http import Request, Response -class IncomingHandler(http.IncomingHandler): - def handle_request(self, request: Request) -> Response: +class HttpHandler(http.Handler): + async def handle_request(self, request: Request) -> Response: # Access the request.method if request.method == 'POST': # Read the request.body as a string @@ -330,14 +330,15 @@ This next example will create an outbound request, to obtain a random fact about from spin_sdk import http from spin_sdk.http import Request, Response, send -class IncomingHandler(http.IncomingHandler): - def handle_request(self, request: Request) -> Response: - resp = send(Request("GET", "https://random-data-api.fermyon.app/animals/json", {}, None)) - - return Response(200, - {"content-type": "text/plain"}, - bytes(f"Here is an animal fact: {str(resp.body, 'utf-8')}", "utf-8")) +class HttpHandler(http.Handler): + async def handle_request(self, request: Request) -> Response: + resp = await send(Request("GET", "https://random-data-api.fermyon.app/animals/json", {}, None)) + return Response( + 200, + {"content-type": "text/plain"}, + bytes(f"Here is an animal fact: {str(resp.body, 'utf-8')}", "utf-8") + ) ``` ### Configuring Outbound Requests @@ -363,7 +364,7 @@ component = "hello-world" source = "app.wasm" allowed_outbound_hosts = ["https://random-data-api.fermyon.app"] [component.hello-world.build] -command = "componentize-py -w spin-http componentize app -o app.wasm" +command = "componentize-py -w spin:up/http-trigger@4.0.0 componentize app -o app.wasm" watch = ["*.py", "requirements.txt"] ``` @@ -418,12 +419,11 @@ route = "/..." component = "hello-world" [component.hello-world] -id = "hello-world" source = "app.wasm" variables = { redis_address = "redis://127.0.0.1:6379" } allowed_outbound_hosts = ["redis://127.0.0.1:6379"] [component.hello-world.build] -command = "spin py2wasm app -o app.wasm" +command = "componentize-py -w spin:up/http-trigger@4.0.0 componentize app -o app.wasm" ``` If you are still following along, please go ahead and update your `app.py` file one more time, as follows: @@ -434,20 +434,22 @@ If you are still following along, please go ahead and update your `app.py` file from spin_sdk import http, redis, variables from spin_sdk.http import Request, Response -class IncomingHandler(http.IncomingHandler): - def handle_request(self, request: Request) -> Response: - with redis.open(variables.get("redis_address")) as db: - db.set("foo", b"bar") - value = db.get("foo") - db.incr("testIncr") - db.sadd("testSets", ["hello", "world"]) - content = db.smembers("testSets") - db.srem("testSets", ["hello"]) +class HttpHandler(http.Handler): + async def handle_request(self, request: Request) -> Response: + with await redis.open(await variables.get("redis_address")) as db: + await db.set("foo", b"bar") + value = await db.get("foo") + await db.incr("testIncr") + await db.sadd("testSets", ["hello", "world"]) + content = await db.smembers("testSets") + await db.srem("testSets", ["hello"]) assert value == b"bar", f"expected \"bar\", got \"{str(value, 'utf-8')}\"" - return Response(200, - {"content-type": "text/plain"}, - bytes(f"Executed outbound Redis commands: {request.uri}", "utf-8")) + return Response( + 200, + {"content-type": "text/plain"}, + bytes(f"Executed outbound Redis commands: {request.uri}", "utf-8") + ) ``` ### Building and Running the Application