Skip to content

Commit 3b0db54

Browse files
authored
fix: can import multiple modules (#43)
* fix: can import multiple modules * feat(examples): multiple modules example * chore: forgot debug stacktrace
1 parent 9c29f68 commit 3b0db54

File tree

13 files changed

+213
-87
lines changed

13 files changed

+213
-87
lines changed

docs/source/examples.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ To showcase the framework and how it can used to deploy serverless applications,
99
:doc:`../examples/cron`
1010
Function that gets triggered periodically.
1111

12+
:doc:`../examples/multiple_modules`
13+
An application with multiple handlers organized in different modules.
14+
1215
:doc:`../examples/github_actions`
1316
A GitHub action workflow to deploy your application with `scw_serverless`.
1417

@@ -23,5 +26,6 @@ To showcase the framework and how it can used to deploy serverless applications,
2326

2427
../examples/hello_world
2528
../examples/cron
29+
../examples/multiple_modules
2630
../examples/github_actions
2731
../examples/pr_notifier
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
```{include} ../../../examples/multiple_modules/README.md
2+
```
3+
4+
## [Sources](https://github.com/scaleway/serverless-api-project/blob/main/examples/multiple_modules/app.py)
5+
6+
```{literalinclude} ../../../examples/multiple_modules/app.py
7+
```
8+
9+
```{literalinclude} ../../../examples/multiple_modules/upload.py
10+
```
11+
12+
```{literalinclude} ../../../examples/multiple_modules/query.py
13+
```

docs/source/examples/pr_notifier.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
```{include} ../../../examples/pr_notifier/README.md
22
```
33

4-
## [Source](https://github.com/scaleway/serverless-api-project/blob/main/examples/github_actions/deploy.yml)
4+
## [Source](https://github.com/scaleway/serverless-api-project/blob/main/examples/pr_notifier/notifier.py)
55

66
```{literalinclude} ../../../examples/pr_notifier/notifier.py
77
```

examples/multiple_functions/handler.py

Lines changed: 0 additions & 44 deletions
This file was deleted.

examples/multiple_modules/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# [Multiple Modules](https://github.com/scaleway/serverless-api-project/tree/main/examples/multiple_modules)
2+
3+
An app to upload and query files to S3 Glacier on Scaleway split in multiple modules.
4+
5+
The upload endpoint allows you to upload files to Glacier via the `file` form-data key:
6+
7+
```console
8+
echo -e "Hello world!\n My contents will be stored in a bunker!" > myfile.dat
9+
curl -F [email protected] <upload-function-url>
10+
```
11+
12+
This example is there to showcase how to split handlers into different Python modules.
13+
14+
## Deploying
15+
16+
Deployment can be done with `scw_serverless`:
17+
18+
```console
19+
pip install scw_serverless
20+
scw-serverless deploy app.py
21+
```
22+
23+
## Configuration
24+
25+
Here's all the environments variables that needs to be passed when deploying:
26+
27+
| Variable | Description | Required |
28+
|:----------------:|:---------------------------------------:|:------------------:|
29+
| `SCW_SECRET_KEY` | Secret key to use for S3 operations | :heavy_check_mark: |
30+
| `SCW_ACCESS_KEY` | Access key to use for S3 operations | :heavy_check_mark: |
31+
| `S3_BUCKET` | Name of the bucket to store files into. | :heavy_check_mark: |

examples/multiple_modules/app.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import logging
2+
import os
3+
4+
from scw_serverless.app import Serverless
5+
6+
logging.basicConfig(level=logging.INFO)
7+
8+
app = Serverless(
9+
"multiple-modules",
10+
secret={
11+
"SCW_ACCESS_KEY": os.environ["SCW_ACCESS_KEY"],
12+
"SCW_SECRET_KEY": os.environ["SCW_SECRET_KEY"],
13+
},
14+
env={"S3_BUCKET": os.environ["S3_BUCKET"]},
15+
)
16+
17+
import query # noqa
18+
import upload # pylint: disable=all # noqa

examples/multiple_modules/query.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import json
2+
import os
3+
from typing import Any
4+
5+
from app import app
6+
from s3 import bucket
7+
8+
9+
@app.func(
10+
description="List objects in S3 uploads.",
11+
privacy="public",
12+
env={"LIMIT": "100"},
13+
min_scale=0,
14+
max_scale=2,
15+
memory_limit=128,
16+
timeout="300s",
17+
)
18+
def query(_event: dict[str, Any], _context: dict[str, Any]) -> dict[str, Any]:
19+
"""A handler to list objects in a S3 bucket."""
20+
21+
response = []
22+
23+
for obj in bucket.objects.limit(count=int(os.environ["LIMIT"])):
24+
response.append(
25+
{
26+
"name": obj.key,
27+
"last_modified": obj.last_modified.strftime("%m/%d/%Y, %H:%M:%S"),
28+
"storage_class": obj.storage_class,
29+
}
30+
)
31+
32+
return {
33+
"statusCode": 200,
34+
"headers": {"Content-Type": "application/json"},
35+
"body": json.dumps(response),
36+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
boto3~=1.26
2+
scw-serverless~=0.0.3
3+
streaming_form_data~=1.11.0

examples/multiple_modules/s3.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import os
2+
3+
import boto3
4+
5+
s3 = boto3.resource(
6+
"s3",
7+
region_name="fr-par",
8+
use_ssl=True,
9+
endpoint_url="https://s3.fr-par.scw.cloud",
10+
aws_access_key_id=os.environ["SCW_ACCESS_KEY"],
11+
aws_secret_access_key=os.environ["SCW_SECRET_KEY"],
12+
)
13+
14+
bucket = s3.Bucket(os.environ["S3_BUCKET"])

examples/multiple_modules/upload.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import logging
2+
from typing import Any
3+
4+
from app import app
5+
from s3 import bucket
6+
from streaming_form_data import StreamingFormDataParser
7+
from streaming_form_data.targets import ValueTarget
8+
9+
10+
@app.func()
11+
def upload(event: dict[str, Any], _context: dict[str, Any]) -> dict[str, Any]:
12+
"""Upload form data to S3 Glacier."""
13+
14+
headers = event["headers"]
15+
parser = StreamingFormDataParser(headers=headers)
16+
17+
target = ValueTarget()
18+
parser.register("file", target)
19+
20+
body: str = event["body"]
21+
parser.data_received(body.encode("utf-8"))
22+
23+
if not (len(target.value) > 0 and target.multipart_filename):
24+
return {"statusCode": 400}
25+
26+
name = target.multipart_filename
27+
28+
logging.info("Uploading file %s to Glacier on %s", name, bucket.name)
29+
bucket.put_object(Key=name, Body=target.value, StorageClass="GLACIER")
30+
31+
return {"statusCode": 200}

0 commit comments

Comments
 (0)