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
11 changes: 8 additions & 3 deletions content/v4/http-outbound.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/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
response = send(Request("GET", "https://random-data-api.fermyon.app/animals/json", {}, None))

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
```

**Notes**
Expand Down
10 changes: 6 additions & 4 deletions content/v4/http-trigger.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,23 +226,25 @@ 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 `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 IncomingHandler(http.IncomingHandler):
def handle_request(self, request: Request) -> Response:
class HttpHandler(http.Handler):
async def handle_request(self, request: Request) -> Response:
return Response(
200,
{"content-type": "text/plain"},
bytes("Hello from Python!", "utf-8")
)
```

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"}}
Expand Down
41 changes: 32 additions & 9 deletions content/v4/kv-store-api-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 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"))
val = await store.get("test")

return Response(
200,
Expand All @@ -148,8 +148,31 @@ 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`
- 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):

[`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 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 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)
- 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).

{{ blockEnd }}

Expand Down
4 changes: 2 additions & 2 deletions content/v4/language-support-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -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? |
|-----|-----|
Expand All @@ -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 |

Expand Down
19 changes: 18 additions & 1 deletion content/v4/mqtt-outbound.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 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)
```

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 }}

Expand Down
64 changes: 33 additions & 31 deletions content/v4/python-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ With <a href="https://www.python.org/" target="_blank">Python</a> 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 <a href="https://docs.python.org/3/" target="_blank">the official Python documentation</a> and comprehensive <a href="https://docs.python.org/3/reference/" target="_blank">language reference</a>.

[**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

Expand All @@ -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:

<!-- @selectiveCpy -->

Expand Down Expand Up @@ -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:

<!-- @nocpy -->

```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"},
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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"]
```

Expand Down Expand Up @@ -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:
Expand All @@ -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
Expand Down
Loading