Skip to content

Commit ee72441

Browse files
stephenhibbertStephen Hibbert
andauthored
FastHTML examples (#496)
* Add FastHTML examples with container and zip packaging plus response streaming. * Corrected README and removed print --------- Co-authored-by: Stephen Hibbert <stephibb@amazon.co.uk>
1 parent a59e6a0 commit ee72441

34 files changed

+1629
-0
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,10 @@ The Lambda Web Adapter also supports all non-HTTP event triggers, such as SQS, S
201201
- [SQS Express.js](examples/sqs-expressjs)
202202
- [Bedrock Agent FastAPI](examples/bedrock-agent-fastapi)
203203
- [Bedrock Agent FastAPI in Zip](examples/bedrock-agent-fastapi-zip)
204+
- [FastHTML](examples/fasthtml)
205+
- [FastHTML in Zip](examples/fasthtml-zip)
206+
- [FastHTML with Response Streaming](examples/fasthtml-response-streaming)
207+
- [FastHTML with Response Streaming in Zip](examples/fasthtml-response-streaming-zip)
204208

205209
## Acknowledgement
206210

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
2+
# Created by https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode
3+
4+
### Linux ###
5+
*~
6+
7+
# temporary files which can be created if a process still has a handle open of a deleted file
8+
.fuse_hidden*
9+
10+
# KDE directory preferences
11+
.directory
12+
13+
# Linux trash folder which might appear on any partition or disk
14+
.Trash-*
15+
16+
# .nfs files are created when an open file is removed but is still being accessed
17+
.nfs*
18+
19+
### OSX ###
20+
*.DS_Store
21+
.AppleDouble
22+
.LSOverride
23+
24+
# Icon must end with two \r
25+
Icon
26+
27+
# Thumbnails
28+
._*
29+
30+
# Files that might appear in the root of a volume
31+
.DocumentRevisions-V100
32+
.fseventsd
33+
.Spotlight-V100
34+
.TemporaryItems
35+
.Trashes
36+
.VolumeIcon.icns
37+
.com.apple.timemachine.donotpresent
38+
39+
# Directories potentially created on remote AFP share
40+
.AppleDB
41+
.AppleDesktop
42+
Network Trash Folder
43+
Temporary Items
44+
.apdisk
45+
46+
### PyCharm ###
47+
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
48+
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
49+
50+
# User-specific stuff:
51+
.idea/**/workspace.xml
52+
.idea/**/tasks.xml
53+
.idea/dictionaries
54+
55+
# Sensitive or high-churn files:
56+
.idea/**/dataSources/
57+
.idea/**/dataSources.ids
58+
.idea/**/dataSources.xml
59+
.idea/**/dataSources.local.xml
60+
.idea/**/sqlDataSources.xml
61+
.idea/**/dynamic.xml
62+
.idea/**/uiDesigner.xml
63+
64+
# Gradle:
65+
.idea/**/gradle.xml
66+
.idea/**/libraries
67+
68+
# CMake
69+
cmake-build-debug/
70+
71+
# Mongo Explorer plugin:
72+
.idea/**/mongoSettings.xml
73+
74+
## File-based project format:
75+
*.iws
76+
77+
## Plugin-specific files:
78+
79+
# IntelliJ
80+
/out/
81+
82+
# mpeltonen/sbt-idea plugin
83+
.idea_modules/
84+
85+
# JIRA plugin
86+
atlassian-ide-plugin.xml
87+
88+
# Cursive Clojure plugin
89+
.idea/replstate.xml
90+
91+
# Ruby plugin and RubyMine
92+
/.rakeTasks
93+
94+
# Crashlytics plugin (for Android Studio and IntelliJ)
95+
com_crashlytics_export_strings.xml
96+
crashlytics.properties
97+
crashlytics-build.properties
98+
fabric.properties
99+
100+
### PyCharm Patch ###
101+
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
102+
103+
# *.iml
104+
# modules.xml
105+
# .idea/misc.xml
106+
# *.ipr
107+
108+
# Sonarlint plugin
109+
.idea/sonarlint
110+
111+
### Python ###
112+
# Byte-compiled / optimized / DLL files
113+
__pycache__/
114+
*.py[cod]
115+
*$py.class
116+
117+
# C extensions
118+
*.so
119+
120+
# Distribution / packaging
121+
.Python
122+
build/
123+
develop-eggs/
124+
dist/
125+
downloads/
126+
eggs/
127+
.eggs/
128+
lib/
129+
lib64/
130+
parts/
131+
sdist/
132+
var/
133+
wheels/
134+
*.egg-info/
135+
.installed.cfg
136+
*.egg
137+
138+
# PyInstaller
139+
# Usually these files are written by a python script from a template
140+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
141+
*.manifest
142+
*.spec
143+
144+
# Installer logs
145+
pip-log.txt
146+
pip-delete-this-directory.txt
147+
148+
# Unit test / coverage reports
149+
htmlcov/
150+
.tox/
151+
.coverage
152+
.coverage.*
153+
.cache
154+
.pytest_cache/
155+
nosetests.xml
156+
coverage.xml
157+
*.cover
158+
.hypothesis/
159+
160+
# Translations
161+
*.mo
162+
*.pot
163+
164+
# Flask stuff:
165+
instance/
166+
.webassets-cache
167+
168+
# Scrapy stuff:
169+
.scrapy
170+
171+
# Sphinx documentation
172+
docs/_build/
173+
174+
# PyBuilder
175+
target/
176+
177+
# Jupyter Notebook
178+
.ipynb_checkpoints
179+
180+
# pyenv
181+
.python-version
182+
183+
# celery beat schedule file
184+
celerybeat-schedule.*
185+
186+
# SageMath parsed files
187+
*.sage.py
188+
189+
# Environments
190+
.env
191+
.venv
192+
env/
193+
venv/
194+
ENV/
195+
env.bak/
196+
venv.bak/
197+
198+
# Spyder project settings
199+
.spyderproject
200+
.spyproject
201+
202+
# Rope project settings
203+
.ropeproject
204+
205+
# mkdocs documentation
206+
/site
207+
208+
# mypy
209+
.mypy_cache/
210+
211+
### VisualStudioCode ###
212+
.vscode/*
213+
!.vscode/settings.json
214+
!.vscode/tasks.json
215+
!.vscode/launch.json
216+
!.vscode/extensions.json
217+
.history
218+
219+
### Windows ###
220+
# Windows thumbnail cache files
221+
Thumbs.db
222+
ehthumbs.db
223+
ehthumbs_vista.db
224+
225+
# Folder config file
226+
Desktop.ini
227+
228+
# Recycle Bin used on file shares
229+
$RECYCLE.BIN/
230+
231+
# Windows Installer files
232+
*.cab
233+
*.msi
234+
*.msm
235+
*.msp
236+
237+
# Windows shortcuts
238+
*.lnk
239+
240+
# Build folder
241+
242+
*/build/*
243+
244+
# End of https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# FastHTML Response Streaming
2+
3+
This example shows how to use Lambda Web Adapter to run a [FastHTML](https://fastht.ml/) application with response streaming via a Function URL.
4+
5+
## How does it work?
6+
7+
We add Lambda Web Adapter layer to the function and configure wrapper script.
8+
9+
1. attach Lambda Adapter layer to your function. This layer containers Lambda Adapter binary and a wrapper script.
10+
1. x86_64: `arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerX86:23`
11+
2. arm64: `arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerArm64:23`
12+
2. configure Lambda environment variable `AWS_LAMBDA_EXEC_WRAPPER` to `/opt/bootstrap`. This is a wrapper script included in the layer.
13+
3. set function handler to a startup command: `run.sh`. The wrapper script will execute this command to boot up your application.
14+
15+
To get more information of Wrapper script, please read Lambda documentation [here](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-modify.html#runtime-wrapper).
16+
17+
This is the resource for Lambda function. The function urls's invoke mode is configured as "RESPONSE_STREAM", and Lambda environment variable "AWS_LWA_INVOKE_MODE" is set to "response_stream".
18+
19+
```yaml
20+
FastHTMLFunction:
21+
Type: AWS::Serverless::Function
22+
Properties:
23+
CodeUri: app/
24+
Handler: run.sh
25+
Runtime: python3.9
26+
MemorySize: 256
27+
Environment:
28+
Variables:
29+
AWS_LAMBDA_EXEC_WRAPPER: /opt/bootstrap
30+
AWS_LWA_INVOKE_MODE: response_stream
31+
PORT: 8000
32+
Layers:
33+
- !Sub arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerX86:23
34+
FunctionUrlConfig:
35+
AuthType: NONE
36+
InvokeMode: RESPONSE_STREAM
37+
```
38+
39+
## Build and Deploy
40+
41+
Run the following commands to build and deploy the application to lambda.
42+
43+
```bash
44+
sam build --use-container
45+
sam deploy --guided
46+
```
47+
48+
When the deployment completes, take note of the FastHTMLFunctionUrl Value. It is the Lambda function URL
49+
50+
## Verify it works
51+
52+
Open the FastHTMLFunctionUrl URL in a browser, you should a button. Click to see "This is streaming from AWS Lambda! 🚀" stream back character by character.

examples/fasthtml-response-streaming-zip/__init__.py

Whitespace-only changes.

examples/fasthtml-response-streaming-zip/app/__init__.py

Whitespace-only changes.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from fasthtml.common import *
2+
from starlette.responses import StreamingResponse
3+
import asyncio
4+
import secrets
5+
6+
app, rt = fast_app(
7+
ct_hdr=True,
8+
hdrs=[Script(src="https://unpkg.com/htmx-ext-transfer-encoding-chunked@0.4.0/transfer-encoding-chunked.js")],
9+
secret_key=secrets.token_hex(32),
10+
debug=True,
11+
)
12+
13+
async def streamer():
14+
message = "This is streaming from AWS Lambda! 🚀\n"
15+
response_txt = ""
16+
for char in message:
17+
response_txt += char
18+
yield to_xml(Div(
19+
response_txt,
20+
id=f"stream-output",
21+
hx_swap_oob="outerHTML",
22+
))
23+
await asyncio.sleep(0.1)
24+
25+
@rt("/")
26+
def index():
27+
return Div(
28+
Button("Click to stream", hx_get=stream, hx_target="#stream-output", hx_ext="chunked-transfer"),
29+
P(id="stream-output")
30+
)
31+
32+
@app.get
33+
async def stream():
34+
return StreamingResponse(streamer(), media_type="text/plain",
35+
headers={"X-Transfer-Encoding": "chunked"})
36+
37+
serve(port=8000, reload=True)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
python-fasthtml==0.5.1
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/bash
2+
3+
PATH=$PATH:$LAMBDA_TASK_ROOT/bin \
4+
PYTHONPATH=$PYTHONPATH:/opt/python:$LAMBDA_RUNTIME_DIR \
5+
exec python main.py
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
AWSTemplateFormatVersion: '2010-09-09'
2+
Transform: AWS::Serverless-2016-10-31
3+
Description: >
4+
FastHTML response streaming
5+
6+
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
7+
Globals:
8+
Function:
9+
Timeout: 60
10+
11+
Resources:
12+
FastHTMLFunction:
13+
Type: AWS::Serverless::Function
14+
Properties:
15+
CodeUri: app/
16+
Handler: run.sh
17+
Runtime: python3.12
18+
MemorySize: 256
19+
Environment:
20+
Variables:
21+
AWS_LAMBDA_EXEC_WRAPPER: /opt/bootstrap
22+
AWS_LWA_INVOKE_MODE: response_stream
23+
PORT: 8000
24+
Layers:
25+
- !Sub arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerX86:23
26+
FunctionUrlConfig:
27+
AuthType: NONE
28+
InvokeMode: RESPONSE_STREAM
29+
30+
Outputs:
31+
FastHTMLFunctionUrl:
32+
Description: "Function URL for FastHTML function"
33+
Value: !GetAtt FastHTMLFunctionUrl.FunctionUrl
34+
FastHTMLFunction:
35+
Description: "FastHTML Lambda Function ARN"
36+
Value: !GetAtt FastHTMLFunction.Arn

0 commit comments

Comments
 (0)