Skip to content

Commit f00b138

Browse files
committed
fixes #198
1 parent a75b216 commit f00b138

File tree

5 files changed

+89
-20
lines changed

5 files changed

+89
-20
lines changed

fasthtml/_modidx.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
'fasthtml.core._mk_list': ('api/core.html#_mk_list', 'fasthtml/core.py'),
5454
'fasthtml.core._resp': ('api/core.html#_resp', 'fasthtml/core.py'),
5555
'fasthtml.core._send_ws': ('api/core.html#_send_ws', 'fasthtml/core.py'),
56+
'fasthtml.core._sig': ('api/core.html#_sig', 'fasthtml/core.py'),
5657
'fasthtml.core._wrap_call': ('api/core.html#_wrap_call', 'fasthtml/core.py'),
5758
'fasthtml.core._wrap_ex': ('api/core.html#_wrap_ex', 'fasthtml/core.py'),
5859
'fasthtml.core._wrap_req': ('api/core.html#_wrap_req', 'fasthtml/core.py'),

fasthtml/core.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from datetime import datetime
1919
from dataclasses import dataclass,fields,is_dataclass,MISSING,asdict
2020
from collections import namedtuple
21-
from inspect import isfunction,ismethod,signature,Parameter,get_annotations
21+
from inspect import isfunction,ismethod,Parameter,get_annotations
2222
from functools import wraps, partialmethod
2323
from http import cookies
2424
from copy import copy,deepcopy
@@ -27,6 +27,9 @@
2727

2828
empty = Parameter.empty
2929

30+
# %% ../nbs/api/00_core.ipynb
31+
def _sig(f): return signature_ex(f, True)
32+
3033
# %% ../nbs/api/00_core.ipynb
3134
def is_typeddict(cls:type)->bool:
3235
"Check if `cls` is a `TypedDict`"
@@ -226,7 +229,7 @@ def _ws_endp(recv, conn=None, disconn=None, hdrs=None, before=None):
226229
cls = type('WS_Endp', (WebSocketEndpoint,), {"encoding":"text"})
227230

228231
async def _generic_handler(handler, ws, data=None):
229-
wd = _wrap_ws(ws, loads(data) if data else {}, signature(handler).parameters)
232+
wd = _wrap_ws(ws, loads(data) if data else {}, _sig(handler).parameters)
230233
resp = handler(*wd)
231234
if resp:
232235
if is_async_callable(handler): resp = await resp
@@ -287,7 +290,7 @@ async def _wrap_call(f, req, params):
287290
class RouteX(Route):
288291
def __init__(self, path:str, endpoint, *, methods=None, name=None, include_in_schema=True, middleware=None,
289292
hdrs=None, ftrs=None, before=None, after=None, htmlkw=None, **bodykw):
290-
self.sig = signature(endpoint)
293+
self.sig = _sig(endpoint)
291294
self.f,self.hdrs,self.ftrs,self.before,self.after,self.htmlkw,self.bodykw = endpoint,hdrs,ftrs,before,after,htmlkw,bodykw
292295
super().__init__(path, self._endp, methods=methods, name=name, include_in_schema=include_in_schema, middleware=middleware)
293296

@@ -301,10 +304,10 @@ async def _endp(self, req):
301304
if isinstance(b, Beforeware): bf,skip = b.f,b.skip
302305
else: bf,skip = b,[]
303306
if not any(re.match(r, req.url.path) for r in skip):
304-
resp = await _wrap_call(bf, req, signature(bf).parameters)
307+
resp = await _wrap_call(bf, req, _sig(bf).parameters)
305308
if not resp: resp = await _wrap_call(self.f, req, self.sig.parameters)
306309
for a in self.after:
307-
_,*wreq = await _wrap_req(req, signature(a).parameters)
310+
_,*wreq = await _wrap_req(req, _sig(a).parameters)
308311
nr = a(resp, *wreq)
309312
if nr: resp = nr
310313
return _resp(req, resp, self.sig.return_annotation)

fasthtml/fastapp.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,12 @@ def fast_app(
6060
**kwargs)->Any:
6161
h = (picolink,) if pico or (pico is None and default_hdrs) else ()
6262
if hdrs: h += tuple(hdrs)
63-
63+
6464
app = app_factory(hdrs=h, ftrs=ftrs, before=before, middleware=middleware, live=live, debug=debug, routes=routes, exception_handlers=exception_handlers,
6565
on_startup=on_startup, on_shutdown=on_shutdown, lifespan=lifespan, default_hdrs=default_hdrs, secret_key=secret_key,
6666
session_cookie=session_cookie, max_age=max_age, sess_path=sess_path, same_site=same_site, sess_https_only=sess_https_only,
6767
sess_domain=sess_domain, key_fname=key_fname, ws_hdr=ws_hdr, htmlkw=htmlkw, reload_attempts=reload_attempts, reload_interval=reload_interval, **(bodykw or {}))
68-
68+
6969
@app.route("/{fname:path}.{ext:static}")
7070
async def get(fname:str, ext:str): return FileResponse(f'{fname}.{ext}')
7171
if not db_file: return app,app.route

nbs/api/00_core.ipynb

Lines changed: 76 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
"from datetime import datetime\n",
5454
"from dataclasses import dataclass,fields,is_dataclass,MISSING,asdict\n",
5555
"from collections import namedtuple\n",
56-
"from inspect import isfunction,ismethod,signature,Parameter,get_annotations\n",
56+
"from inspect import isfunction,ismethod,Parameter,get_annotations\n",
5757
"from functools import wraps, partialmethod\n",
5858
"from http import cookies\n",
5959
"from copy import copy,deepcopy\n",
@@ -96,6 +96,17 @@
9696
"id": "19d3f2a7",
9797
"metadata": {},
9898
"outputs": [],
99+
"source": [
100+
"#| export\n",
101+
"def _sig(f): return signature_ex(f, True)"
102+
]
103+
},
104+
{
105+
"cell_type": "code",
106+
"execution_count": null,
107+
"id": "3d01cb33",
108+
"metadata": {},
109+
"outputs": [],
99110
"source": [
100111
"#| export\n",
101112
"def is_typeddict(cls:type)->bool:\n",
@@ -635,7 +646,7 @@
635646
"source": [
636647
"async def f(req):\n",
637648
" def _f(p:HttpHeader): ...\n",
638-
" p = first(signature(_f).parameters.values())\n",
649+
" p = first(_sig(_f).parameters.values())\n",
639650
" result = await _from_body(req, p)\n",
640651
" return JSONResponse(result.__dict__)\n",
641652
"\n",
@@ -715,7 +726,7 @@
715726
"def g(req, this:Starlette, a:str, b:HttpHeader): ...\n",
716727
"\n",
717728
"async def f(req):\n",
718-
" a = await _wrap_req(req, signature(g).parameters)\n",
729+
" a = await _wrap_req(req, _sig(g).parameters)\n",
719730
" return Response(str(a))\n",
720731
"\n",
721732
"app = Starlette(routes=[Route('/', f, methods=['POST'])])\n",
@@ -826,7 +837,7 @@
826837
" cls = type('WS_Endp', (WebSocketEndpoint,), {\"encoding\":\"text\"})\n",
827838
" \n",
828839
" async def _generic_handler(handler, ws, data=None):\n",
829-
" wd = _wrap_ws(ws, loads(data) if data else {}, signature(handler).parameters)\n",
840+
" wd = _wrap_ws(ws, loads(data) if data else {}, _sig(handler).parameters)\n",
830841
" resp = handler(*wd)\n",
831842
" if resp:\n",
832843
" if is_async_callable(handler): resp = await resp\n",
@@ -953,7 +964,7 @@
953964
"class RouteX(Route):\n",
954965
" def __init__(self, path:str, endpoint, *, methods=None, name=None, include_in_schema=True, middleware=None,\n",
955966
" hdrs=None, ftrs=None, before=None, after=None, htmlkw=None, **bodykw):\n",
956-
" self.sig = signature(endpoint)\n",
967+
" self.sig = _sig(endpoint)\n",
957968
" self.f,self.hdrs,self.ftrs,self.before,self.after,self.htmlkw,self.bodykw = endpoint,hdrs,ftrs,before,after,htmlkw,bodykw\n",
958969
" super().__init__(path, self._endp, methods=methods, name=name, include_in_schema=include_in_schema, middleware=middleware)\n",
959970
"\n",
@@ -967,10 +978,10 @@
967978
" if isinstance(b, Beforeware): bf,skip = b.f,b.skip\n",
968979
" else: bf,skip = b,[]\n",
969980
" if not any(re.match(r, req.url.path) for r in skip):\n",
970-
" resp = await _wrap_call(bf, req, signature(bf).parameters)\n",
981+
" resp = await _wrap_call(bf, req, _sig(bf).parameters)\n",
971982
" if not resp: resp = await _wrap_call(self.f, req, self.sig.parameters)\n",
972983
" for a in self.after:\n",
973-
" _,*wreq = await _wrap_req(req, signature(a).parameters)\n",
984+
" _,*wreq = await _wrap_req(req, _sig(a).parameters)\n",
974985
" nr = a(resp, *wreq)\n",
975986
" if nr: resp = nr\n",
976987
" return _resp(req, resp, self.sig.return_annotation)"
@@ -1598,7 +1609,7 @@
15981609
{
15991610
"data": {
16001611
"text/plain": [
1601-
"'Cookie was set at time 13:26:44.481873'"
1612+
"'Cookie was set at time 23:01:53.591425'"
16021613
]
16031614
},
16041615
"execution_count": null,
@@ -1639,13 +1650,13 @@
16391650
"name": "stdout",
16401651
"output_type": "stream",
16411652
"text": [
1642-
"Set to 2024-08-05 13:26:44.529004\n"
1653+
"Set to 2024-08-05 23:01:53.638504\n"
16431654
]
16441655
},
16451656
{
16461657
"data": {
16471658
"text/plain": [
1648-
"'Session time: 13:26:44.529004'"
1659+
"'Session time: 23:01:53.638504'"
16491660
]
16501661
},
16511662
"execution_count": null,
@@ -1673,12 +1684,43 @@
16731684
"\n",
16741685
"<!-- do not remove -->\n",
16751686
"\n",
1676-
"## 0.2.1\n",
1687+
"## 0.2.3\n",
1688+
"\n",
1689+
"### New Features\n",
1690+
"\n",
1691+
"- Add `attr\n"
1692+
]
1693+
}
1694+
],
1695+
"source": [
1696+
"@rt(\"/upload\")\n",
1697+
"async def post(uploadfile:UploadFile): return (await uploadfile.read()).decode()\n",
1698+
"\n",
1699+
"fn = '../../CHANGELOG.md'\n",
1700+
"data = {'message': 'Hello, world!'}\n",
1701+
"with open(fn, 'rb') as f:\n",
1702+
" print(cli.post('/upload', files={'uploadfile': f}, data=data).text[:80])"
1703+
]
1704+
},
1705+
{
1706+
"cell_type": "code",
1707+
"execution_count": null,
1708+
"id": "4d17ab9c",
1709+
"metadata": {},
1710+
"outputs": [
1711+
{
1712+
"name": "stdout",
1713+
"output_type": "stream",
1714+
"text": [
1715+
"# Release notes\n",
1716+
"\n",
1717+
"<!-- do not remove -->\n",
16771718
"\n",
1719+
"## 0.2.3\n",
16781720
"\n",
1679-
"### Bugs Squashed\n",
1721+
"### New Features\n",
16801722
"\n",
1681-
"- railway\n"
1723+
"- Add `attr\n"
16821724
]
16831725
}
16841726
],
@@ -1692,6 +1734,27 @@
16921734
" print(cli.post('/upload', files={'uploadfile': f}, data=data).text[:80])"
16931735
]
16941736
},
1737+
{
1738+
"cell_type": "code",
1739+
"execution_count": null,
1740+
"id": "9c1863bc",
1741+
"metadata": {},
1742+
"outputs": [],
1743+
"source": [
1744+
"@rt(\"/{fname:path}.{ext:static}\")\n",
1745+
"async def get(fname:str, ext:str): return FileResponse(f'{fname}.{ext}')"
1746+
]
1747+
},
1748+
{
1749+
"cell_type": "code",
1750+
"execution_count": null,
1751+
"id": "9281360c",
1752+
"metadata": {},
1753+
"outputs": [],
1754+
"source": [
1755+
"assert 'These are the source notebooks for FastHTML' in cli.get('/README.txt').text"
1756+
]
1757+
},
16951758
{
16961759
"cell_type": "code",
16971760
"execution_count": null,

nbs/api/README.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
These are the source notebooks for FastHTML.
2+

0 commit comments

Comments
 (0)