Skip to content

Commit 953d1fc

Browse files
committed
fixes #555
1 parent 1011514 commit 953d1fc

File tree

2 files changed

+19
-220
lines changed

2 files changed

+19
-220
lines changed

fasthtml/core.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,18 +131,21 @@ def _is_body(anno): return issubclass(anno, (dict,ns)) or _annotations(anno)
131131
# %% ../nbs/api/00_core.ipynb
132132
def _formitem(form, k):
133133
"Return single item `k` from `form` if len 1, otherwise return list"
134+
if isinstance(form, dict): return form[k]
134135
o = form.getlist(k)
135136
return o[0] if len(o) == 1 else o if o else None
136137

137138
# %% ../nbs/api/00_core.ipynb
138139
def form2dict(form: FormData) -> dict:
139140
"Convert starlette form data to a dict"
141+
if isinstance(form, dict): return form
140142
return {k: _formitem(form, k) for k in form}
141143

142144
# %% ../nbs/api/00_core.ipynb
143145
async def parse_form(req: Request) -> FormData:
144146
"Starlette errors on empty multipart forms, so this checks for that situation"
145147
ctype = req.headers.get("Content-Type", "")
148+
if ctype=='application/json': return await req.json()
146149
if not ctype.startswith("multipart/form-data"): return await req.form()
147150
try: boundary = ctype.split("boundary=")[1].strip()
148151
except IndexError: raise HTTPException(400, "Invalid form-data: no boundary")
@@ -156,8 +159,7 @@ async def _from_body(req, p):
156159
anno = p.annotation
157160
# Get the fields and types of type `anno`, if available
158161
d = _annotations(anno)
159-
if req.headers.get('content-type', None)=='application/json': data = await req.json()
160-
else: data = form2dict(await parse_form(req))
162+
data = form2dict(await parse_form(req))
161163
if req.query_params: data = {**data, **dict(req.query_params)}
162164
cargs = {k: _form_arg(k, v, d) for k, v in data.items() if not d or k in d}
163165
return anno(**cargs)

nbs/api/00_core.ipynb

Lines changed: 15 additions & 218 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@
131131
{
132132
"data": {
133133
"text/plain": [
134-
"datetime.datetime(2024, 10, 26, 14, 0)"
134+
"datetime.datetime(2024, 10, 28, 14, 0)"
135135
]
136136
},
137137
"execution_count": null,
@@ -487,6 +487,7 @@
487487
"#| export\n",
488488
"def _formitem(form, k):\n",
489489
" \"Return single item `k` from `form` if len 1, otherwise return list\"\n",
490+
" if isinstance(form, dict): return form[k]\n",
490491
" o = form.getlist(k)\n",
491492
" return o[0] if len(o) == 1 else o if o else None"
492493
]
@@ -501,6 +502,7 @@
501502
"#| export\n",
502503
"def form2dict(form: FormData) -> dict:\n",
503504
" \"Convert starlette form data to a dict\"\n",
505+
" if isinstance(form, dict): return form\n",
504506
" return {k: _formitem(form, k) for k in form}"
505507
]
506508
},
@@ -529,6 +531,7 @@
529531
"async def parse_form(req: Request) -> FormData:\n",
530532
" \"Starlette errors on empty multipart forms, so this checks for that situation\"\n",
531533
" ctype = req.headers.get(\"Content-Type\", \"\")\n",
534+
" if ctype=='application/json': return await req.json()\n",
532535
" if not ctype.startswith(\"multipart/form-data\"): return await req.form()\n",
533536
" try: boundary = ctype.split(\"boundary=\")[1].strip()\n",
534537
" except IndexError: raise HTTPException(400, \"Invalid form-data: no boundary\")\n",
@@ -550,8 +553,7 @@
550553
" anno = p.annotation\n",
551554
" # Get the fields and types of type `anno`, if available\n",
552555
" d = _annotations(anno)\n",
553-
" if req.headers.get('content-type', None)=='application/json': data = await req.json()\n",
554-
" else: data = form2dict(await parse_form(req))\n",
556+
" data = form2dict(await parse_form(req))\n",
555557
" if req.query_params: data = {**data, **dict(req.query_params)}\n",
556558
" cargs = {k: _form_arg(k, v, d) for k, v in data.items() if not d or k in d}\n",
557559
" return anno(**cargs)"
@@ -1541,28 +1543,6 @@
15411543
"id": "b163c933",
15421544
"metadata": {},
15431545
"outputs": [
1544-
{
1545-
"data": {
1546-
"text/html": [
1547-
"<meta charset=\"utf-8\">\n",
1548-
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, viewport-fit=cover\">\n",
1549-
"<script src=\"https://unpkg.com/htmx.org@next/dist/htmx.min.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/answerdotai/fasthtml-js@1.0.4/fasthtml.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/answerdotai/surreal@main/surreal.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/gnat/css-scope-inline@main/script.js\"></script><script>\n",
1550-
" function sendmsg() {\n",
1551-
" window.parent.postMessage({height: document.documentElement.offsetHeight}, '*');\n",
1552-
" }\n",
1553-
" window.onload = function() {\n",
1554-
" sendmsg();\n",
1555-
" document.body.addEventListener('htmx:afterSettle', sendmsg);\n",
1556-
" document.body.addEventListener('htmx:wsAfterMessage', sendmsg);\n",
1557-
" };</script>"
1558-
],
1559-
"text/plain": [
1560-
"<IPython.core.display.HTML object>"
1561-
]
1562-
},
1563-
"metadata": {},
1564-
"output_type": "display_data"
1565-
},
15661546
{
15671547
"data": {
15681548
"text/plain": [
@@ -1612,30 +1592,7 @@
16121592
"execution_count": null,
16131593
"id": "645d8d95",
16141594
"metadata": {},
1615-
"outputs": [
1616-
{
1617-
"data": {
1618-
"text/html": [
1619-
"<meta charset=\"utf-8\">\n",
1620-
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, viewport-fit=cover\">\n",
1621-
"<script src=\"https://unpkg.com/htmx.org@next/dist/htmx.min.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/answerdotai/fasthtml-js@1.0.4/fasthtml.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/answerdotai/surreal@main/surreal.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/gnat/css-scope-inline@main/script.js\"></script><script>\n",
1622-
" function sendmsg() {\n",
1623-
" window.parent.postMessage({height: document.documentElement.offsetHeight}, '*');\n",
1624-
" }\n",
1625-
" window.onload = function() {\n",
1626-
" sendmsg();\n",
1627-
" document.body.addEventListener('htmx:afterSettle', sendmsg);\n",
1628-
" document.body.addEventListener('htmx:wsAfterMessage', sendmsg);\n",
1629-
" };</script>"
1630-
],
1631-
"text/plain": [
1632-
"<IPython.core.display.HTML object>"
1633-
]
1634-
},
1635-
"metadata": {},
1636-
"output_type": "display_data"
1637-
}
1638-
],
1595+
"outputs": [],
16391596
"source": [
16401597
"app,cli,rt = get_cli(FastHTML(secret_key='soopersecret'))"
16411598
]
@@ -2054,28 +2011,6 @@
20542011
"id": "bb8154cc",
20552012
"metadata": {},
20562013
"outputs": [
2057-
{
2058-
"data": {
2059-
"text/html": [
2060-
"<meta charset=\"utf-8\">\n",
2061-
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, viewport-fit=cover\">\n",
2062-
"<script src=\"https://unpkg.com/htmx.org@next/dist/htmx.min.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/answerdotai/fasthtml-js@1.0.4/fasthtml.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/answerdotai/surreal@main/surreal.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/gnat/css-scope-inline@main/script.js\"></script><script>\n",
2063-
" function sendmsg() {\n",
2064-
" window.parent.postMessage({height: document.documentElement.offsetHeight}, '*');\n",
2065-
" }\n",
2066-
" window.onload = function() {\n",
2067-
" sendmsg();\n",
2068-
" document.body.addEventListener('htmx:afterSettle', sendmsg);\n",
2069-
" document.body.addEventListener('htmx:wsAfterMessage', sendmsg);\n",
2070-
" };</script>"
2071-
],
2072-
"text/plain": [
2073-
"<IPython.core.display.HTML object>"
2074-
]
2075-
},
2076-
"metadata": {},
2077-
"output_type": "display_data"
2078-
},
20792014
{
20802015
"data": {
20812016
"text/plain": [
@@ -2412,13 +2347,13 @@
24122347
"name": "stdout",
24132348
"output_type": "stream",
24142349
"text": [
2415-
"Set to 2024-10-26 07:52:13.419267\n"
2350+
"Set to 2024-10-28 20:22:34.772989\n"
24162351
]
24172352
},
24182353
{
24192354
"data": {
24202355
"text/plain": [
2421-
"'Session time: 2024-10-26 07:52:13.419267'"
2356+
"'Session time: 2024-10-28 20:22:34.772989'"
24222357
]
24232358
},
24242359
"execution_count": null,
@@ -2527,30 +2462,7 @@
25272462
"execution_count": null,
25282463
"id": "1f11eab1",
25292464
"metadata": {},
2530-
"outputs": [
2531-
{
2532-
"data": {
2533-
"text/html": [
2534-
"<meta charset=\"utf-8\">\n",
2535-
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, viewport-fit=cover\">\n",
2536-
"<script src=\"https://unpkg.com/htmx.org@next/dist/htmx.min.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/answerdotai/fasthtml-js@1.0.4/fasthtml.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/answerdotai/surreal@main/surreal.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/gnat/css-scope-inline@main/script.js\"></script><script>\n",
2537-
" function sendmsg() {\n",
2538-
" window.parent.postMessage({height: document.documentElement.offsetHeight}, '*');\n",
2539-
" }\n",
2540-
" window.onload = function() {\n",
2541-
" sendmsg();\n",
2542-
" document.body.addEventListener('htmx:afterSettle', sendmsg);\n",
2543-
" document.body.addEventListener('htmx:wsAfterMessage', sendmsg);\n",
2544-
" };</script>"
2545-
],
2546-
"text/plain": [
2547-
"<IPython.core.display.HTML object>"
2548-
]
2549-
},
2550-
"metadata": {},
2551-
"output_type": "display_data"
2552-
}
2553-
],
2465+
"outputs": [],
25542466
"source": [
25552467
"def _not_found(req, exc): return Div('nope')\n",
25562468
"\n",
@@ -2566,30 +2478,7 @@
25662478
"execution_count": null,
25672479
"id": "063b7f43",
25682480
"metadata": {},
2569-
"outputs": [
2570-
{
2571-
"data": {
2572-
"text/html": [
2573-
"<meta charset=\"utf-8\">\n",
2574-
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, viewport-fit=cover\">\n",
2575-
"<script src=\"https://unpkg.com/htmx.org@next/dist/htmx.min.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/answerdotai/fasthtml-js@1.0.4/fasthtml.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/answerdotai/surreal@main/surreal.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/gnat/css-scope-inline@main/script.js\"></script><script>\n",
2576-
" function sendmsg() {\n",
2577-
" window.parent.postMessage({height: document.documentElement.offsetHeight}, '*');\n",
2578-
" }\n",
2579-
" window.onload = function() {\n",
2580-
" sendmsg();\n",
2581-
" document.body.addEventListener('htmx:afterSettle', sendmsg);\n",
2582-
" document.body.addEventListener('htmx:wsAfterMessage', sendmsg);\n",
2583-
" };</script>"
2584-
],
2585-
"text/plain": [
2586-
"<IPython.core.display.HTML object>"
2587-
]
2588-
},
2589-
"metadata": {},
2590-
"output_type": "display_data"
2591-
}
2592-
],
2481+
"outputs": [],
25932482
"source": [
25942483
"app,cli,rt = get_cli(FastHTML())\n",
25952484
"\n",
@@ -2606,30 +2495,7 @@
26062495
"execution_count": null,
26072496
"id": "efd6fc6c",
26082497
"metadata": {},
2609-
"outputs": [
2610-
{
2611-
"data": {
2612-
"text/html": [
2613-
"<meta charset=\"utf-8\">\n",
2614-
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, viewport-fit=cover\">\n",
2615-
"<script src=\"https://unpkg.com/htmx.org@next/dist/htmx.min.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/answerdotai/fasthtml-js@1.0.4/fasthtml.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/answerdotai/surreal@main/surreal.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/gnat/css-scope-inline@main/script.js\"></script><script>\n",
2616-
" function sendmsg() {\n",
2617-
" window.parent.postMessage({height: document.documentElement.offsetHeight}, '*');\n",
2618-
" }\n",
2619-
" window.onload = function() {\n",
2620-
" sendmsg();\n",
2621-
" document.body.addEventListener('htmx:afterSettle', sendmsg);\n",
2622-
" document.body.addEventListener('htmx:wsAfterMessage', sendmsg);\n",
2623-
" };</script>"
2624-
],
2625-
"text/plain": [
2626-
"<IPython.core.display.HTML object>"
2627-
]
2628-
},
2629-
"metadata": {},
2630-
"output_type": "display_data"
2631-
}
2632-
],
2498+
"outputs": [],
26332499
"source": [
26342500
"auth = user_pwd_auth(testuser='spycraft')\n",
26352501
"app,cli,rt = get_cli(FastHTML(middleware=[auth]))\n",
@@ -2646,30 +2512,7 @@
26462512
"execution_count": null,
26472513
"id": "32520197",
26482514
"metadata": {},
2649-
"outputs": [
2650-
{
2651-
"data": {
2652-
"text/html": [
2653-
"<meta charset=\"utf-8\">\n",
2654-
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, viewport-fit=cover\">\n",
2655-
"<script src=\"https://unpkg.com/htmx.org@next/dist/htmx.min.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/answerdotai/fasthtml-js@1.0.4/fasthtml.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/answerdotai/surreal@main/surreal.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/gnat/css-scope-inline@main/script.js\"></script><script>\n",
2656-
" function sendmsg() {\n",
2657-
" window.parent.postMessage({height: document.documentElement.offsetHeight}, '*');\n",
2658-
" }\n",
2659-
" window.onload = function() {\n",
2660-
" sendmsg();\n",
2661-
" document.body.addEventListener('htmx:afterSettle', sendmsg);\n",
2662-
" document.body.addEventListener('htmx:wsAfterMessage', sendmsg);\n",
2663-
" };</script>"
2664-
],
2665-
"text/plain": [
2666-
"<IPython.core.display.HTML object>"
2667-
]
2668-
},
2669-
"metadata": {},
2670-
"output_type": "display_data"
2671-
}
2672-
],
2515+
"outputs": [],
26732516
"source": [
26742517
"auth = user_pwd_auth(testuser='spycraft')\n",
26752518
"app,cli,rt = get_cli(FastHTML(middleware=[auth]))\n",
@@ -2754,30 +2597,7 @@
27542597
"execution_count": null,
27552598
"id": "cd413b0d",
27562599
"metadata": {},
2757-
"outputs": [
2758-
{
2759-
"data": {
2760-
"text/html": [
2761-
"<meta charset=\"utf-8\">\n",
2762-
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, viewport-fit=cover\">\n",
2763-
"<script src=\"https://unpkg.com/htmx.org@next/dist/htmx.min.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/answerdotai/fasthtml-js@1.0.4/fasthtml.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/answerdotai/surreal@main/surreal.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/gnat/css-scope-inline@main/script.js\"></script><script>\n",
2764-
" function sendmsg() {\n",
2765-
" window.parent.postMessage({height: document.documentElement.offsetHeight}, '*');\n",
2766-
" }\n",
2767-
" window.onload = function() {\n",
2768-
" sendmsg();\n",
2769-
" document.body.addEventListener('htmx:afterSettle', sendmsg);\n",
2770-
" document.body.addEventListener('htmx:wsAfterMessage', sendmsg);\n",
2771-
" };</script>"
2772-
],
2773-
"text/plain": [
2774-
"<IPython.core.display.HTML object>"
2775-
]
2776-
},
2777-
"metadata": {},
2778-
"output_type": "display_data"
2779-
}
2780-
],
2600+
"outputs": [],
27812601
"source": [
27822602
"app,cli,_ = get_cli(FastHTML())\n",
27832603
"ar.to_app(app)"
@@ -2851,30 +2671,7 @@
28512671
"execution_count": null,
28522672
"id": "7438435e",
28532673
"metadata": {},
2854-
"outputs": [
2855-
{
2856-
"data": {
2857-
"text/html": [
2858-
"<meta charset=\"utf-8\">\n",
2859-
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, viewport-fit=cover\">\n",
2860-
"<script src=\"https://unpkg.com/htmx.org@next/dist/htmx.min.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/answerdotai/fasthtml-js@1.0.4/fasthtml.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/answerdotai/surreal@main/surreal.js\"></script><script src=\"https://cdn.jsdelivr.net/gh/gnat/css-scope-inline@main/script.js\"></script><script>\n",
2861-
" function sendmsg() {\n",
2862-
" window.parent.postMessage({height: document.documentElement.offsetHeight}, '*');\n",
2863-
" }\n",
2864-
" window.onload = function() {\n",
2865-
" sendmsg();\n",
2866-
" document.body.addEventListener('htmx:afterSettle', sendmsg);\n",
2867-
" document.body.addEventListener('htmx:wsAfterMessage', sendmsg);\n",
2868-
" };</script>"
2869-
],
2870-
"text/plain": [
2871-
"<IPython.core.display.HTML object>"
2872-
]
2873-
},
2874-
"metadata": {},
2875-
"output_type": "display_data"
2876-
}
2877-
],
2674+
"outputs": [],
28782675
"source": [
28792676
"app,cli,rt = get_cli(FastHTML(secret_key='soopersecret'))"
28802677
]
@@ -2921,7 +2718,7 @@
29212718
{
29222719
"data": {
29232720
"text/plain": [
2924-
"'Cookie was set at time 07:52:14.289735'"
2721+
"'Cookie was set at time 20:22:35.467691'"
29252722
]
29262723
},
29272724
"execution_count": null,

0 commit comments

Comments
 (0)