Skip to content

Commit b179ff0

Browse files
committed
fixes #334
1 parent 625834c commit b179ff0

File tree

3 files changed

+89
-13
lines changed

3 files changed

+89
-13
lines changed

fasthtml/_modidx.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
'fasthtml.core.cookie': ('api/core.html#cookie', 'fasthtml/core.py'),
7575
'fasthtml.core.date': ('api/core.html#date', 'fasthtml/core.py'),
7676
'fasthtml.core.decode_uri': ('api/core.html#decode_uri', 'fasthtml/core.py'),
77+
'fasthtml.core.flat_tuple': ('api/core.html#flat_tuple', 'fasthtml/core.py'),
7778
'fasthtml.core.flat_xt': ('api/core.html#flat_xt', 'fasthtml/core.py'),
7879
'fasthtml.core.form2dict': ('api/core.html#form2dict', 'fasthtml/core.py'),
7980
'fasthtml.core.get_key': ('api/core.html#get_key', 'fasthtml/core.py'),

fasthtml/core.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
# %% auto 0
44
__all__ = ['empty', 'htmx_hdrs', 'fh_cfg', 'htmxscr', 'htmxwsscr', 'surrsrc', 'scopesrc', 'viewport', 'charset', 'all_meths',
55
'date', 'snake2hyphens', 'HtmxHeaders', 'str2int', 'HttpHeader', 'form2dict', 'flat_xt', 'Beforeware',
6-
'WS_RouteX', 'uri', 'decode_uri', 'RouteX', 'RouterX', 'get_key', 'FastHTML', 'serve', 'cookie',
7-
'reg_re_param', 'MiddlewareBase']
6+
'WS_RouteX', 'uri', 'decode_uri', 'flat_tuple', 'RouteX', 'RouterX', 'get_key', 'FastHTML', 'serve',
7+
'cookie', 'reg_re_param', 'MiddlewareBase']
88

99
# %% ../nbs/api/00_core.ipynb
1010
import json,uuid,inspect,types,uvicorn
@@ -152,6 +152,7 @@ async def _find_p(req, arg:str, p:Parameter):
152152
if arg.lower()=='auth': return req.scope.get('auth', None)
153153
if arg.lower()=='htmx': return _get_htmx(req.headers)
154154
if arg.lower()=='app': return req.scope['app']
155+
if arg.lower()=='body': return (await req.body()).decode()
155156
if arg.lower() in ('hdrs','ftrs','bodykw','htmlkw'): return getattr(req, arg.lower())
156157
warn(f"`{arg} has no type annotation and is not a recognised special name, so is ignored.")
157158
return None
@@ -310,9 +311,20 @@ def _to_xml(req, resp, indent):
310311
_find_targets(req, resp)
311312
return to_xml(resp, indent)
312313

314+
# %% ../nbs/api/00_core.ipynb
315+
def flat_tuple(o):
316+
"Flatten lists"
317+
result = []
318+
if not isinstance(o,(tuple,list)): o=[o]
319+
o = list(o)
320+
for item in o:
321+
if isinstance(item, (list,tuple)): result.extend(item)
322+
else: result.append(item)
323+
return tuple(result)
324+
313325
# %% ../nbs/api/00_core.ipynb
314326
def _xt_resp(req, resp):
315-
if not isinstance(resp, tuple): resp = (resp,)
327+
resp = flat_tuple(resp)
316328
resp = resp + tuple(getattr(req, 'injects', ()))
317329
http_hdrs,resp = partition(resp, risinstance(HttpHeader))
318330
http_hdrs = {o.k:str(o.v) for o in http_hdrs}
@@ -418,7 +430,8 @@ async def _f(req, exc):
418430
def _mk_locfunc(f,p):
419431
class _lf:
420432
def __init__(self): update_wrapper(self, f)
421-
def __call__(self, **kw): return p + (f'?{urlencode(kw)}' if kw else '')
433+
def __call__(self, *args, **kw): return f(*args, **kw)
434+
def rt(self, **kw): return p + (f'?{urlencode(kw)}' if kw else '')
422435
def __str__(self): return p
423436
return _lf()
424437

nbs/api/00_core.ipynb

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@
126126
{
127127
"data": {
128128
"text/plain": [
129-
"datetime.datetime(2024, 8, 15, 14, 0)"
129+
"datetime.datetime(2024, 8, 19, 14, 0)"
130130
]
131131
},
132132
"execution_count": null,
@@ -528,6 +528,7 @@
528528
" if arg.lower()=='auth': return req.scope.get('auth', None)\n",
529529
" if arg.lower()=='htmx': return _get_htmx(req.headers)\n",
530530
" if arg.lower()=='app': return req.scope['app']\n",
531+
" if arg.lower()=='body': return (await req.body()).decode()\n",
531532
" if arg.lower() in ('hdrs','ftrs','bodykw','htmlkw'): return getattr(req, arg.lower())\n",
532533
" warn(f\"`{arg} has no type annotation and is not a recognised special name, so is ignored.\")\n",
533534
" return None\n",
@@ -857,6 +858,25 @@
857858
" return to_xml(resp, indent)"
858859
]
859860
},
861+
{
862+
"cell_type": "code",
863+
"execution_count": null,
864+
"id": "a407dc0e",
865+
"metadata": {},
866+
"outputs": [],
867+
"source": [
868+
"#| export\n",
869+
"def flat_tuple(o):\n",
870+
" \"Flatten lists\"\n",
871+
" result = []\n",
872+
" if not isinstance(o,(tuple,list)): o=[o]\n",
873+
" o = list(o)\n",
874+
" for item in o:\n",
875+
" if isinstance(item, (list,tuple)): result.extend(item)\n",
876+
" else: result.append(item)\n",
877+
" return tuple(result)"
878+
]
879+
},
860880
{
861881
"cell_type": "code",
862882
"execution_count": null,
@@ -866,7 +886,7 @@
866886
"source": [
867887
"#| export\n",
868888
"def _xt_resp(req, resp):\n",
869-
" if not isinstance(resp, tuple): resp = (resp,)\n",
889+
" resp = flat_tuple(resp)\n",
870890
" resp = resp + tuple(getattr(req, 'injects', ()))\n",
871891
" http_hdrs,resp = partition(resp, risinstance(HttpHeader))\n",
872892
" http_hdrs = {o.k:str(o.v) for o in http_hdrs}\n",
@@ -1015,7 +1035,7 @@
10151035
{
10161036
"data": {
10171037
"text/plain": [
1018-
"'091b529e-f0be-4107-a22e-5644344a20d3'"
1038+
"'a604e4a2-08e8-462d-aff9-15468891fe09'"
10191039
]
10201040
},
10211041
"execution_count": null,
@@ -1065,7 +1085,8 @@
10651085
"def _mk_locfunc(f,p):\n",
10661086
" class _lf:\n",
10671087
" def __init__(self): update_wrapper(self, f)\n",
1068-
" def __call__(self, **kw): return p + (f'?{urlencode(kw)}' if kw else '')\n",
1088+
" def __call__(self, *args, **kw): return f(*args, **kw)\n",
1089+
" def rt(self, **kw): return p + (f'?{urlencode(kw)}' if kw else '')\n",
10691090
" def __str__(self): return p\n",
10701091
" return _lf()"
10711092
]
@@ -1384,7 +1405,7 @@
13841405
"text": [
13851406
"<!doctype html>\n",
13861407
"\n",
1387-
"<html><div hx-post=\"/yoyo\">Text.</div>\n",
1408+
"<html><div hx-post=\"a yoyo\">Text.</div>\n",
13881409
"</html>\n",
13891410
"\n"
13901411
]
@@ -1417,7 +1438,7 @@
14171438
],
14181439
"source": [
14191440
"@app.get\n",
1420-
"def autopost2(): return Html(Body(Div('Text.', cls='px-2', hx_post=show_host(a='b'))))\n",
1441+
"def autopost2(): return Html(Body(Div('Text.', cls='px-2', hx_post=show_host.rt(a='b'))))\n",
14211442
"print(cli.get('/autopost2').text)"
14221443
]
14231444
},
@@ -1683,6 +1704,24 @@
16831704
"test_eq(r.headers['mykey'], 'myval')"
16841705
]
16851706
},
1707+
{
1708+
"cell_type": "code",
1709+
"execution_count": null,
1710+
"id": "8a91b0af",
1711+
"metadata": {},
1712+
"outputs": [],
1713+
"source": [
1714+
"@rt\n",
1715+
"def meta(): \n",
1716+
" return ((Title('hi'),H1('hi')),\n",
1717+
" (Meta(property='image'), Meta(property='site_name'))\n",
1718+
" )\n",
1719+
"\n",
1720+
"t = cli.post('/meta').text\n",
1721+
"assert re.search('<body>\\s*<h1>hi</h1>\\s*</body>', t)\n",
1722+
"assert '<meta' in t"
1723+
]
1724+
},
16861725
{
16871726
"cell_type": "code",
16881727
"execution_count": null,
@@ -1775,6 +1814,21 @@
17751814
"assert \"<title>It worked!</title>\" in response and \"<p>15, Lorem</p>\" in response"
17761815
]
17771816
},
1817+
{
1818+
"cell_type": "code",
1819+
"execution_count": null,
1820+
"id": "51cd4643",
1821+
"metadata": {},
1822+
"outputs": [],
1823+
"source": [
1824+
"# Testing POST with Content-Type: application/json\n",
1825+
"@app.post(\"/bodytext\")\n",
1826+
"def index(body): return body\n",
1827+
"\n",
1828+
"response = cli.post('/bodytext', headers={\"Content-Type\": \"application/json\"}, data=s).text\n",
1829+
"test_eq(response, '{\"b\": \"Lorem\", \"a\": 15}')"
1830+
]
1831+
},
17781832
{
17791833
"cell_type": "code",
17801834
"execution_count": null,
@@ -1807,7 +1861,7 @@
18071861
{
18081862
"data": {
18091863
"text/plain": [
1810-
"'Cookie was set at time 21:05:42.196533'"
1864+
"'Cookie was set at time 10:40:02.914012'"
18111865
]
18121866
},
18131867
"execution_count": null,
@@ -1837,13 +1891,13 @@
18371891
"name": "stdout",
18381892
"output_type": "stream",
18391893
"text": [
1840-
"Set to 2024-08-15 21:05:42.225287\n"
1894+
"Set to 2024-08-19 10:40:03.311880\n"
18411895
]
18421896
},
18431897
{
18441898
"data": {
18451899
"text/plain": [
1846-
"'Session time: 2024-08-15 21:05:42.225287'"
1900+
"'Session time: 2024-08-19 10:40:03.311880'"
18471901
]
18481902
},
18491903
"execution_count": null,
@@ -2032,6 +2086,14 @@
20322086
"#|hide\n",
20332087
"import nbdev; nbdev.nbdev_export()"
20342088
]
2089+
},
2090+
{
2091+
"cell_type": "code",
2092+
"execution_count": null,
2093+
"id": "2af5e721",
2094+
"metadata": {},
2095+
"outputs": [],
2096+
"source": []
20352097
}
20362098
],
20372099
"metadata": {

0 commit comments

Comments
 (0)