Skip to content

Commit b5a54d5

Browse files
authored
lint all examples using MyPy in run_test.sh (#138)
This required fixing various typing and symbol resolution issues flagged by MyPy. Fixes #136 Signed-off-by: Joel Dice <joel.dice@akamai.com>
1 parent 7f14d37 commit b5a54d5

File tree

12 files changed

+77
-35
lines changed

12 files changed

+77
-35
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
run: |
2424
python -m venv venv
2525
source venv/bin/activate
26-
pip install componentize-py==0.22.0 http-router==4.1.2 build==1.4.2
26+
pip install componentize-py==0.22.0 http-router==4.1.2 build==1.4.2 mypy==1.13
2727
python -m build
2828
pip install dist/spin_sdk-4.0.0-py3-none-any.whl
2929
bash run_tests.sh

examples/external-lib-example/app.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from typing import cast
12
from spin_sdk import http
23
from spin_sdk.http import Request, Response
34
import re
@@ -47,7 +48,7 @@ async def handle_request(self, request: Request) -> Response:
4748
uri = urlparse(request.uri)
4849
try:
4950
handler = router(uri.path, request.method)
50-
return handler.target(uri, request)
51+
return cast(Response, handler.target(uri, request))
5152
except exceptions.NotFoundError:
5253
return Response(404, {}, None)
5354

examples/redis-trigger/app.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from spin_sdk.wit import exports
22

33
class SpinRedisInboundRedis300(exports.SpinRedisInboundRedis300):
4-
async def handle_message(self, message: bytes):
4+
async def handle_message(self, message: bytes) -> None:
55
print(message)

examples/spin-kv/app.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
from typing import TypeVar, Tuple, List
2+
from componentize_py_types import Result, Err
3+
from componentize_py_async_support.streams import StreamReader
4+
from componentize_py_async_support.futures import FutureReader
15
from spin_sdk import http, key_value
26
from spin_sdk.http import Request, Response
37
from spin_sdk.key_value import Store
@@ -9,7 +13,7 @@ async def handle_request(self, request: Request) -> Response:
913
print(await get_keys(a))
1014
print(await a.exists("test"))
1115
print(await a.get("test"))
12-
print(await a.delete("test"))
16+
await a.delete("test")
1317
print(await get_keys(a))
1418

1519
return Response(
@@ -18,14 +22,10 @@ async def handle_request(self, request: Request) -> Response:
1822
bytes("Hello from Python!", "utf-8")
1923
)
2024

21-
async def get_keys(Store) -> list[str]:
22-
stream, future = await Store.get_keys()
23-
keys = []
24-
25-
while True:
26-
batch = await stream.read(max_count=100)
27-
if not batch:
28-
break
29-
keys.extend(batch)
30-
31-
return keys
25+
async def get_keys(store: Store) -> list[str]:
26+
stream, future = await store.get_keys()
27+
with stream, future:
28+
keys = []
29+
while not stream.writer_dropped:
30+
keys += await stream.read(max_count=100)
31+
return keys

examples/spin-postgres/app.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
from spin_sdk import http, postgres
22
from spin_sdk.http import Request, Response
3+
from spin_sdk.postgres import RowSet, DbValue
34

45

5-
def format_value(db_value) -> str:
6+
def format_value(db_value: DbValue) -> str:
67
if hasattr(db_value, "value"):
78
return str(db_value.value)
89
return "NULL"
910

1011

11-
def format_rowset(rowset) -> str:
12+
def format_rowset(rowset: RowSet) -> str:
1213
lines = []
1314
col_names = [col.name for col in rowset.columns]
1415
lines.append(" | ".join(col_names))

run_tests.sh

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,43 @@
22

33
source venv/bin/activate
44

5+
# First, install any example-specific dependencies (common dependencies such as
6+
# `componentize-py`, `spin-sdk`, and `mypy` are assumed to have been installed
7+
# in the virtual environment).
8+
59
if [ ! -d examples/matrix-math/numpy ]
610
then
711
(cd examples/matrix-math \
812
&& curl -OL https://github.com/dicej/wasi-wheels/releases/download/v0.0.2/numpy-wasi.tar.gz \
913
&& tar xf numpy-wasi.tar.gz)
1014
fi
1115

16+
# Next, run MyPy on all the examples
17+
18+
for example in examples/*
19+
do
20+
echo "linting $example"
21+
if [ $example = "examples/matrix-math" ]
22+
then
23+
# NumPy fails linting as of this writing, so we skip it
24+
extra_option="--follow-imports silent"
25+
else
26+
unset extra_option
27+
fi
28+
export MYPYPATH=$(pwd)/src
29+
(cd $example && mypy --strict $extra_option -m app) || exit 1
30+
done
31+
32+
# Next, build all the examples
33+
1234
for example in examples/*
1335
do
1436
echo "building $example"
1537
(cd $example && spin build) || exit 1
1638
done
1739

40+
# Finally, run some of the examples and test that they behave as expected
1841

19-
# run trivial examples
2042
for example in examples/hello examples/external-lib-example examples/spin-kv examples/spin-variables
2143
do
2244
pushd $example

src/spin_sdk/http/__init__.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ async def handle(self, request: WasiRequest) -> WasiResponse:
101101
simple_response.headers['content-length'] = str(content_length)
102102

103103
tx, rx = wit.byte_stream()
104-
componentize_py_async_support.spawn(copy(simple_response.body, tx))
104+
componentize_py_async_support.spawn(_copy(simple_response.body, tx))
105105
response = WasiResponse.new(Fields.from_list(list(map(
106106
lambda pair: (pair[0], bytes(pair[1], "utf-8")),
107107
simple_response.headers.items()
@@ -159,7 +159,7 @@ async def send(request: Request) -> Response:
159159
content_length = len(request.body) if request.body is not None else 0
160160
# Make a copy rather than mutate in place, since the caller might not
161161
# expect us to mutate it:
162-
headers_dict = headers_dict.copy()
162+
headers_dict = dict(headers_dict)
163163
headers_dict['content-length'] = str(content_length)
164164

165165
headers = list(map(
@@ -168,12 +168,12 @@ async def send(request: Request) -> Response:
168168
))
169169

170170
tx, rx = wit.byte_stream()
171-
componentize_py_async_support.spawn(copy(request.body, tx))
171+
componentize_py_async_support.spawn(_copy(request.body, tx))
172172
outgoing_request = WasiRequest.new(Fields.from_list(headers), rx, _trailers_future(), None)[0]
173173
outgoing_request.set_method(method)
174174
outgoing_request.set_scheme(scheme)
175175
if url_parsed.netloc == '':
176-
if scheme == "http":
176+
if isinstance(scheme, Scheme_Http):
177177
authority = ":80"
178178
else:
179179
authority = ":443"
@@ -228,7 +228,7 @@ def strip_forbidden_headers(headers:MutableMapping[str, str]) -> MutableMapping[
228228
pass
229229
return headers
230230

231-
async def copy(bytes:bytes, tx:ByteStreamWriter):
231+
async def _copy(bytes: bytes | None, tx: ByteStreamWriter) -> None:
232232
with tx:
233233
if bytes is not None:
234234
await tx.write_all(bytes)

src/spin_sdk/key_value.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"""Module for accessing Spin key-value stores"""
22

3-
from spin_sdk.wit.imports.spin_key_value_key_value_3_0_0 import Store
3+
from spin_sdk.wit.imports import spin_key_value_key_value_3_0_0 as kv
4+
5+
Store = kv.Store
46

57
async def open(name: str) -> Store:
68
"""

src/spin_sdk/llm.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
"""Module for working with the Spin large language model API"""
22

33
from dataclasses import dataclass
4-
from typing import Optional, Sequence
4+
from typing import Optional, List
55
from spin_sdk.wit.imports import fermyon_spin_llm_2_0_0 as spin_llm
66

7+
78
@dataclass
89
class InferencingParams:
910
max_tokens: int = 100
@@ -14,7 +15,7 @@ class InferencingParams:
1415
top_p: float = 0.9
1516

1617

17-
def generate_embeddings(model: str, text: Sequence[str]) -> spin_llm.EmbeddingsResult:
18+
def generate_embeddings(model: str, text: List[str]) -> spin_llm.EmbeddingsResult:
1819
"""
1920
A `componentize_py_types.Err(spin_sdk.wit.imports.fermyon_spin_llm_2_0_0.Error_ModelNotSupported)` will be raised if the component does not have access to the specified model.
2021
@@ -32,8 +33,16 @@ def infer_with_options(model: str, prompt: str, options: Optional[InferencingPar
3233
3334
A `componentize_py_types.Err(spin_sdk.wit.imports.fermyon_spin_llm_2_0_0.Error_InvalidInput(str))` will be raised if an invalid input is provided.
3435
"""
35-
options = options or InferencingParams
36-
return spin_llm.infer(model, prompt, options)
36+
some_options = options or InferencingParams()
37+
my_options = spin_llm.InferencingParams(
38+
some_options.max_tokens,
39+
some_options.repeat_penalty,
40+
some_options.repeat_penalty_last_n_token_count,
41+
some_options.temperature,
42+
some_options.top_k,
43+
some_options.top_p,
44+
)
45+
return spin_llm.infer(model, prompt, my_options)
3746

3847
def infer(model: str, prompt: str) -> spin_llm.InferencingResult:
3948
"""
@@ -43,6 +52,5 @@ def infer(model: str, prompt: str) -> spin_llm.InferencingResult:
4352
4453
A `componentize_py_types.Err(spin_sdk.wit.imports.fermyon_spin_llm_2_0_0.Error_InvalidInput(str))` will be raised if an invalid input is provided.
4554
"""
46-
options = InferencingParams
47-
return spin_llm.infer(model, prompt, options)
55+
return infer_with_options(model, prompt, None)
4856

src/spin_sdk/postgres.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
"""Module for interacting with a Postgres database"""
22

3-
from spin_sdk.wit.imports.spin_postgres_postgres_4_2_0 import Connection
3+
from spin_sdk.wit.imports import spin_postgres_postgres_4_2_0 as pg
4+
5+
Connection = pg.Connection
6+
RowSet = pg.RowSet
7+
DbValue = pg.DbValue
48

59
async def open(connection_string: str) -> Connection:
610
"""

0 commit comments

Comments
 (0)