Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
jph00 authored and alg747 committed Sep 17, 2024
1 parent 8bae9da commit 4de8f1f
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 14 deletions.
1 change: 1 addition & 0 deletions fasthtml/_modidx.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
'fasthtml.core.flat_xt': ('api/core.html#flat_xt', 'fasthtml/core.py'),
'fasthtml.core.form2dict': ('api/core.html#form2dict', 'fasthtml/core.py'),
'fasthtml.core.get_key': ('api/core.html#get_key', 'fasthtml/core.py'),
'fasthtml.core.parse_form': ('api/core.html#parse_form', 'fasthtml/core.py'),
'fasthtml.core.reg_re_param': ('api/core.html#reg_re_param', 'fasthtml/core.py'),
'fasthtml.core.serve': ('api/core.html#serve', 'fasthtml/core.py'),
'fasthtml.core.signal_shutdown': ('api/core.html#signal_shutdown', 'fasthtml/core.py'),
Expand Down
24 changes: 17 additions & 7 deletions fasthtml/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
# %% auto 0
__all__ = ['empty', 'htmx_hdrs', 'fh_cfg', 'htmx_resps', 'htmxsrc', 'htmxwssrc', 'fhjsscr', 'htmxctsrc', 'surrsrc', 'scopesrc',
'viewport', 'charset', 'all_meths', 'date', 'snake2hyphens', 'HtmxHeaders', 'str2int', 'HttpHeader',
'HtmxResponseHeaders', 'form2dict', 'flat_xt', 'Beforeware', 'EventStream', 'signal_shutdown', 'WS_RouteX',
'uri', 'decode_uri', 'flat_tuple', 'Redirect', 'RouteX', 'RouterX', 'get_key', 'FastHTML', 'serve', 'Client',
'cookie', 'reg_re_param', 'MiddlewareBase', 'FtResponse']
'HtmxResponseHeaders', 'form2dict', 'parse_form', 'flat_xt', 'Beforeware', 'EventStream', 'signal_shutdown',
'WS_RouteX', 'uri', 'decode_uri', 'flat_tuple', 'Redirect', 'RouteX', 'RouterX', 'get_key', 'FastHTML',
'serve', 'Client', 'cookie', 'reg_re_param', 'MiddlewareBase', 'FtResponse']

# %% ../nbs/api/00_core.ipynb
import json,uuid,inspect,types,uvicorn,signal,asyncio,threading
Expand Down Expand Up @@ -143,13 +143,25 @@ def form2dict(form: FormData) -> dict:
"Convert starlette form data to a dict"
return {k: _formitem(form, k) for k in form}

# %% ../nbs/api/00_core.ipynb
async def parse_form(req: Request) -> FormData:
"Starlette errors on empty multipart forms, so this checks for that situation"
ctype = req.headers.get("Content-Type", "")
if not ctype.startswith("multipart/form-data"): return await req.form()
try: boundary = ctype.split("boundary=")[1].strip()
except IndexError: raise HTTPException(400, "Invalid form-data: no boundary")
min_len = len(boundary) + 6
clen = int(req.headers.get("Content-Length", "0"))
if clen <= min_len: return FormData()
return await req.form()

# %% ../nbs/api/00_core.ipynb
async def _from_body(req, p):
anno = p.annotation
# Get the fields and types of type `anno`, if available
d = _annotations(anno)
if req.headers.get('content-type', None)=='application/json': data = await req.json()
else: data = form2dict(await req.form())
else: data = form2dict(await parse_form(req))
cargs = {k: _form_arg(k, v, d) for k, v in data.items() if not d or k in d}
return anno(**cargs)

Expand Down Expand Up @@ -181,9 +193,7 @@ async def _find_p(req, arg:str, p:Parameter):
if res in (empty,None): res = req.headers.get(snake2hyphens(arg), None)
if res in (empty,None): res = req.query_params.getlist(arg)
if res==[]: res = None
if res in (empty,None):
frm = await req.form()
res = _formitem(frm, arg)
if res in (empty,None): res = _formitem(await parse_form(req), arg)
# Raise 400 error if the param does not include a default
if (res in (empty,None)) and p.default is empty: raise HTTPException(400, f"Missing required field: {arg}")
# If we have a default, return that if we have no value
Expand Down
32 changes: 25 additions & 7 deletions nbs/api/00_core.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,26 @@
"test_eq(res['b'], 0)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "42c9cea0",
"metadata": {},
"outputs": [],
"source": [
"#| export\n",
"async def parse_form(req: Request) -> FormData:\n",
" \"Starlette errors on empty multipart forms, so this checks for that situation\"\n",
" ctype = req.headers.get(\"Content-Type\", \"\")\n",
" if not ctype.startswith(\"multipart/form-data\"): return await req.form()\n",
" try: boundary = ctype.split(\"boundary=\")[1].strip()\n",
" except IndexError: raise HTTPException(400, \"Invalid form-data: no boundary\")\n",
" min_len = len(boundary) + 6\n",
" clen = int(req.headers.get(\"Content-Length\", \"0\"))\n",
" if clen <= min_len: return FormData()\n",
" return await req.form()"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand All @@ -544,7 +564,7 @@
" # Get the fields and types of type `anno`, if available\n",
" d = _annotations(anno)\n",
" if req.headers.get('content-type', None)=='application/json': data = await req.json()\n",
" else: data = form2dict(await req.form())\n",
" else: data = form2dict(await parse_form(req))\n",
" cargs = {k: _form_arg(k, v, d) for k, v in data.items() if not d or k in d}\n",
" return anno(**cargs)"
]
Expand Down Expand Up @@ -637,9 +657,7 @@
" if res in (empty,None): res = req.headers.get(snake2hyphens(arg), None)\n",
" if res in (empty,None): res = req.query_params.getlist(arg)\n",
" if res==[]: res = None\n",
" if res in (empty,None):\n",
" frm = await req.form()\n",
" res = _formitem(frm, arg)\n",
" if res in (empty,None): res = _formitem(await parse_form(req), arg)\n",
" # Raise 400 error if the param does not include a default\n",
" if (res in (empty,None)) and p.default is empty: raise HTTPException(400, f\"Missing required field: {arg}\")\n",
" # If we have a default, return that if we have no value\n",
Expand Down Expand Up @@ -2103,13 +2121,13 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Set to 2024-09-15 06:30:32.885239\n"
"Set to 2024-09-15 07:00:36.005099\n"
]
},
{
"data": {
"text/plain": [
"'Session time: 2024-09-15 06:30:32.885239'"
"'Session time: 2024-09-15 07:00:36.005099'"
]
},
"execution_count": null,
Expand Down Expand Up @@ -2340,7 +2358,7 @@
{
"data": {
"text/plain": [
"'Cookie was set at time 06:30:34.617249'"
"'Cookie was set at time 07:00:36.291612'"
]
},
"execution_count": null,
Expand Down

0 comments on commit 4de8f1f

Please sign in to comment.