python-fasthtml 0.7.0__tar.gz → 0.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python-fasthtml-0.7.0/python_fasthtml.egg-info → python-fasthtml-0.8.0}/PKG-INFO +1 -1
- python-fasthtml-0.8.0/fasthtml/__init__.py +2 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/_modidx.py +13 -18
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/core.py +108 -82
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/fastapp.py +1 -9
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/jupyter.py +1 -32
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/starlette.py +1 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0/python_fasthtml.egg-info}/PKG-INFO +1 -1
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/settings.ini +1 -1
- python-fasthtml-0.7.0/fasthtml/__init__.py +0 -2
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/CONTRIBUTING.md +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/LICENSE +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/MANIFEST.in +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/README.md +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/authmw.py +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/basics.py +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/cli.py +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/common.py +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/components.py +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/components.pyi +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/core.pyi +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/ft.py +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/js.py +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/katex.js +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/live_reload.py +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/oauth.py +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/pico.py +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/svg.py +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/toaster.py +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/xtend.py +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/fasthtml/xtend.pyi +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/pyproject.toml +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/python_fasthtml.egg-info/SOURCES.txt +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/python_fasthtml.egg-info/dependency_links.txt +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/python_fasthtml.egg-info/entry_points.txt +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/python_fasthtml.egg-info/not-zip-safe +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/python_fasthtml.egg-info/requires.txt +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/python_fasthtml.egg-info/top_level.txt +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/setup.cfg +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/setup.py +0 -0
- {python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/tests/test_toaster.py +0 -0
|
@@ -23,7 +23,12 @@ d = { 'settings': { 'branch': 'main',
|
|
|
23
23
|
'fasthtml.components.html2ft': ('api/components.html#html2ft', 'fasthtml/components.py'),
|
|
24
24
|
'fasthtml.components.show': ('api/components.html#show', 'fasthtml/components.py'),
|
|
25
25
|
'fasthtml.components.sse_message': ('api/components.html#sse_message', 'fasthtml/components.py')},
|
|
26
|
-
'fasthtml.core': { 'fasthtml.core.
|
|
26
|
+
'fasthtml.core': { 'fasthtml.core.APIRouter': ('api/core.html#apirouter', 'fasthtml/core.py'),
|
|
27
|
+
'fasthtml.core.APIRouter.__call__': ('api/core.html#apirouter.__call__', 'fasthtml/core.py'),
|
|
28
|
+
'fasthtml.core.APIRouter.__init__': ('api/core.html#apirouter.__init__', 'fasthtml/core.py'),
|
|
29
|
+
'fasthtml.core.APIRouter.to_app': ('api/core.html#apirouter.to_app', 'fasthtml/core.py'),
|
|
30
|
+
'fasthtml.core.APIRouter.ws': ('api/core.html#apirouter.ws', 'fasthtml/core.py'),
|
|
31
|
+
'fasthtml.core.Beforeware': ('api/core.html#beforeware', 'fasthtml/core.py'),
|
|
27
32
|
'fasthtml.core.Beforeware.__init__': ('api/core.html#beforeware.__init__', 'fasthtml/core.py'),
|
|
28
33
|
'fasthtml.core.Client': ('api/core.html#client', 'fasthtml/core.py'),
|
|
29
34
|
'fasthtml.core.Client.__init__': ('api/core.html#client.__init__', 'fasthtml/core.py'),
|
|
@@ -31,6 +36,10 @@ d = { 'settings': { 'branch': 'main',
|
|
|
31
36
|
'fasthtml.core.EventStream': ('api/core.html#eventstream', 'fasthtml/core.py'),
|
|
32
37
|
'fasthtml.core.FastHTML': ('api/core.html#fasthtml', 'fasthtml/core.py'),
|
|
33
38
|
'fasthtml.core.FastHTML.__init__': ('api/core.html#fasthtml.__init__', 'fasthtml/core.py'),
|
|
39
|
+
'fasthtml.core.FastHTML._add_route': ('api/core.html#fasthtml._add_route', 'fasthtml/core.py'),
|
|
40
|
+
'fasthtml.core.FastHTML._add_ws': ('api/core.html#fasthtml._add_ws', 'fasthtml/core.py'),
|
|
41
|
+
'fasthtml.core.FastHTML._endp': ('api/core.html#fasthtml._endp', 'fasthtml/core.py'),
|
|
42
|
+
'fasthtml.core.FastHTML.add_route': ('api/core.html#fasthtml.add_route', 'fasthtml/core.py'),
|
|
34
43
|
'fasthtml.core.FastHTML.route': ('api/core.html#fasthtml.route', 'fasthtml/core.py'),
|
|
35
44
|
'fasthtml.core.FastHTML.static_route': ('api/core.html#fasthtml.static_route', 'fasthtml/core.py'),
|
|
36
45
|
'fasthtml.core.FastHTML.static_route_exts': ('api/core.html#fasthtml.static_route_exts', 'fasthtml/core.py'),
|
|
@@ -49,17 +58,7 @@ d = { 'settings': { 'branch': 'main',
|
|
|
49
58
|
'fasthtml.core.Redirect': ('api/core.html#redirect', 'fasthtml/core.py'),
|
|
50
59
|
'fasthtml.core.Redirect.__init__': ('api/core.html#redirect.__init__', 'fasthtml/core.py'),
|
|
51
60
|
'fasthtml.core.Redirect.__response__': ('api/core.html#redirect.__response__', 'fasthtml/core.py'),
|
|
52
|
-
'fasthtml.core.RouteX': ('api/core.html#routex', 'fasthtml/core.py'),
|
|
53
|
-
'fasthtml.core.RouteX.__init__': ('api/core.html#routex.__init__', 'fasthtml/core.py'),
|
|
54
|
-
'fasthtml.core.RouteX._endp': ('api/core.html#routex._endp', 'fasthtml/core.py'),
|
|
55
|
-
'fasthtml.core.RouterX': ('api/core.html#routerx', 'fasthtml/core.py'),
|
|
56
|
-
'fasthtml.core.RouterX.__init__': ('api/core.html#routerx.__init__', 'fasthtml/core.py'),
|
|
57
|
-
'fasthtml.core.RouterX._add_route': ('api/core.html#routerx._add_route', 'fasthtml/core.py'),
|
|
58
|
-
'fasthtml.core.RouterX.add_route': ('api/core.html#routerx.add_route', 'fasthtml/core.py'),
|
|
59
|
-
'fasthtml.core.RouterX.add_ws': ('api/core.html#routerx.add_ws', 'fasthtml/core.py'),
|
|
60
61
|
'fasthtml.core.StringConvertor.to_string': ('api/core.html#stringconvertor.to_string', 'fasthtml/core.py'),
|
|
61
|
-
'fasthtml.core.WS_RouteX': ('api/core.html#ws_routex', 'fasthtml/core.py'),
|
|
62
|
-
'fasthtml.core.WS_RouteX.__init__': ('api/core.html#ws_routex.__init__', 'fasthtml/core.py'),
|
|
63
62
|
'fasthtml.core._add_ids': ('api/core.html#_add_ids', 'fasthtml/core.py'),
|
|
64
63
|
'fasthtml.core._annotations': ('api/core.html#_annotations', 'fasthtml/core.py'),
|
|
65
64
|
'fasthtml.core._apply_ft': ('api/core.html#_apply_ft', 'fasthtml/core.py'),
|
|
@@ -77,9 +76,9 @@ d = { 'settings': { 'branch': 'main',
|
|
|
77
76
|
'fasthtml.core._list': ('api/core.html#_list', 'fasthtml/core.py'),
|
|
78
77
|
'fasthtml.core._mk_list': ('api/core.html#_mk_list', 'fasthtml/core.py'),
|
|
79
78
|
'fasthtml.core._mk_locfunc': ('api/core.html#_mk_locfunc', 'fasthtml/core.py'),
|
|
79
|
+
'fasthtml.core._params': ('api/core.html#_params', 'fasthtml/core.py'),
|
|
80
80
|
'fasthtml.core._resp': ('api/core.html#_resp', 'fasthtml/core.py'),
|
|
81
81
|
'fasthtml.core._send_ws': ('api/core.html#_send_ws', 'fasthtml/core.py'),
|
|
82
|
-
'fasthtml.core._sig': ('api/core.html#_sig', 'fasthtml/core.py'),
|
|
83
82
|
'fasthtml.core._to_htmx_header': ('api/core.html#_to_htmx_header', 'fasthtml/core.py'),
|
|
84
83
|
'fasthtml.core._to_xml': ('api/core.html#_to_xml', 'fasthtml/core.py'),
|
|
85
84
|
'fasthtml.core._url_for': ('api/core.html#_url_for', 'fasthtml/core.py'),
|
|
@@ -106,9 +105,7 @@ d = { 'settings': { 'branch': 'main',
|
|
|
106
105
|
'fasthtml.core.snake2hyphens': ('api/core.html#snake2hyphens', 'fasthtml/core.py'),
|
|
107
106
|
'fasthtml.core.unqid': ('api/core.html#unqid', 'fasthtml/core.py'),
|
|
108
107
|
'fasthtml.core.uri': ('api/core.html#uri', 'fasthtml/core.py')},
|
|
109
|
-
'fasthtml.fastapp': {
|
|
110
|
-
'fasthtml.fastapp._get_tbl': ('api/fastapp.html#_get_tbl', 'fasthtml/fastapp.py'),
|
|
111
|
-
'fasthtml.fastapp.fast_app': ('api/fastapp.html#fast_app', 'fasthtml/fastapp.py')},
|
|
108
|
+
'fasthtml.fastapp': {},
|
|
112
109
|
'fasthtml.ft': {},
|
|
113
110
|
'fasthtml.js': { 'fasthtml.js.HighlightJS': ('api/js.html#highlightjs', 'fasthtml/js.py'),
|
|
114
111
|
'fasthtml.js.KatexMarkdownJS': ('api/js.html#katexmarkdownjs', 'fasthtml/js.py'),
|
|
@@ -117,14 +114,12 @@ d = { 'settings': { 'branch': 'main',
|
|
|
117
114
|
'fasthtml.js.SortableJS': ('api/js.html#sortablejs', 'fasthtml/js.py'),
|
|
118
115
|
'fasthtml.js.dark_media': ('api/js.html#dark_media', 'fasthtml/js.py'),
|
|
119
116
|
'fasthtml.js.light_media': ('api/js.html#light_media', 'fasthtml/js.py')},
|
|
120
|
-
'fasthtml.jupyter': { 'fasthtml.jupyter.
|
|
121
|
-
'fasthtml.jupyter.HTMX': ('api/jupyter.html#htmx', 'fasthtml/jupyter.py'),
|
|
117
|
+
'fasthtml.jupyter': { 'fasthtml.jupyter.HTMX': ('api/jupyter.html#htmx', 'fasthtml/jupyter.py'),
|
|
122
118
|
'fasthtml.jupyter.JupyUvi': ('api/jupyter.html#jupyuvi', 'fasthtml/jupyter.py'),
|
|
123
119
|
'fasthtml.jupyter.JupyUvi.__init__': ('api/jupyter.html#jupyuvi.__init__', 'fasthtml/jupyter.py'),
|
|
124
120
|
'fasthtml.jupyter.JupyUvi.start': ('api/jupyter.html#jupyuvi.start', 'fasthtml/jupyter.py'),
|
|
125
121
|
'fasthtml.jupyter.JupyUvi.stop': ('api/jupyter.html#jupyuvi.stop', 'fasthtml/jupyter.py'),
|
|
126
122
|
'fasthtml.jupyter.is_port_free': ('api/jupyter.html#is_port_free', 'fasthtml/jupyter.py'),
|
|
127
|
-
'fasthtml.jupyter.jupy_app': ('api/jupyter.html#jupy_app', 'fasthtml/jupyter.py'),
|
|
128
123
|
'fasthtml.jupyter.nb_serve': ('api/jupyter.html#nb_serve', 'fasthtml/jupyter.py'),
|
|
129
124
|
'fasthtml.jupyter.nb_serve_async': ('api/jupyter.html#nb_serve_async', 'fasthtml/jupyter.py'),
|
|
130
125
|
'fasthtml.jupyter.wait_port_free': ('api/jupyter.html#wait_port_free', 'fasthtml/jupyter.py'),
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
# %% auto 0
|
|
6
6
|
__all__ = ['empty', 'htmx_hdrs', 'fh_cfg', 'htmx_resps', 'htmx_exts', 'htmxsrc', 'fhjsscr', 'surrsrc', 'scopesrc', 'viewport',
|
|
7
|
-
'charset', '
|
|
8
|
-
'
|
|
9
|
-
'
|
|
10
|
-
'Client', 'cookie', 'reg_re_param', 'MiddlewareBase', 'FtResponse', 'unqid', 'setup_ws']
|
|
7
|
+
'charset', 'cors_allow', 'iframe_scr', 'all_meths', 'parsed_date', 'snake2hyphens', 'HtmxHeaders',
|
|
8
|
+
'HttpHeader', 'HtmxResponseHeaders', 'form2dict', 'parse_form', 'flat_xt', 'Beforeware', 'EventStream',
|
|
9
|
+
'signal_shutdown', 'uri', 'decode_uri', 'flat_tuple', 'Redirect', 'get_key', 'def_hdrs', 'FastHTML', 'serve',
|
|
10
|
+
'Client', 'APIRouter', 'cookie', 'reg_re_param', 'MiddlewareBase', 'FtResponse', 'unqid', 'setup_ws']
|
|
11
11
|
|
|
12
12
|
# %% ../nbs/api/00_core.ipynb
|
|
13
13
|
import json,uuid,inspect,types,uvicorn,signal,asyncio,threading
|
|
@@ -35,10 +35,10 @@ from base64 import b85encode,b64encode
|
|
|
35
35
|
|
|
36
36
|
from .starlette import *
|
|
37
37
|
|
|
38
|
-
empty = Parameter.empty
|
|
39
|
-
|
|
40
38
|
# %% ../nbs/api/00_core.ipynb
|
|
41
|
-
def
|
|
39
|
+
def _params(f): return signature_ex(f, True).parameters
|
|
40
|
+
|
|
41
|
+
empty = Parameter.empty
|
|
42
42
|
|
|
43
43
|
# %% ../nbs/api/00_core.ipynb
|
|
44
44
|
def parsed_date(s:str):
|
|
@@ -257,7 +257,7 @@ def _ws_endp(recv, conn=None, disconn=None):
|
|
|
257
257
|
cls = type('WS_Endp', (WebSocketEndpoint,), {"encoding":"text"})
|
|
258
258
|
|
|
259
259
|
async def _generic_handler(handler, ws, data=None):
|
|
260
|
-
wd = _wrap_ws(ws, loads(data) if data else {},
|
|
260
|
+
wd = _wrap_ws(ws, loads(data) if data else {}, _params(handler))
|
|
261
261
|
resp = await _handle(handler, wd)
|
|
262
262
|
if resp: await _send_ws(ws, resp)
|
|
263
263
|
|
|
@@ -289,13 +289,6 @@ def signal_shutdown():
|
|
|
289
289
|
for sig in (signal.SIGINT, signal.SIGTERM): signal.signal(sig, signal_handler)
|
|
290
290
|
return event
|
|
291
291
|
|
|
292
|
-
# %% ../nbs/api/00_core.ipynb
|
|
293
|
-
class WS_RouteX(WebSocketRoute):
|
|
294
|
-
def __init__(self, app, path:str, recv, conn:callable=None, disconn:callable=None, *,
|
|
295
|
-
name=None, middleware=None):
|
|
296
|
-
super().__init__(path, _ws_endp(recv, conn, disconn), name=name, middleware=middleware)
|
|
297
|
-
self.methods = ['WS']
|
|
298
|
-
|
|
299
292
|
# %% ../nbs/api/00_core.ipynb
|
|
300
293
|
def uri(_arg, **kwargs):
|
|
301
294
|
return f"{quote(_arg)}/{urlencode(kwargs, doseq=True)}"
|
|
@@ -418,52 +411,6 @@ async def _wrap_call(f, req, params):
|
|
|
418
411
|
wreq = await _wrap_req(req, params)
|
|
419
412
|
return await _handle(f, wreq)
|
|
420
413
|
|
|
421
|
-
# %% ../nbs/api/00_core.ipynb
|
|
422
|
-
class RouteX(Route):
|
|
423
|
-
def __init__(self, app, path:str, endpoint, *, methods=None, name=None, include_in_schema=True, middleware=None):
|
|
424
|
-
self.f,self.sig,self._app = endpoint,_sig(endpoint),app
|
|
425
|
-
super().__init__(path, self._endp, methods=methods, name=name, include_in_schema=include_in_schema, middleware=middleware)
|
|
426
|
-
|
|
427
|
-
async def _endp(self, req):
|
|
428
|
-
resp = None
|
|
429
|
-
req.injects = []
|
|
430
|
-
req.hdrs,req.ftrs,req.htmlkw,req.bodykw = map(deepcopy, (self._app.hdrs,self._app.ftrs,self._app.htmlkw,self._app.bodykw))
|
|
431
|
-
req.hdrs,req.ftrs = listify(req.hdrs),listify(req.ftrs)
|
|
432
|
-
for b in self._app.before:
|
|
433
|
-
if not resp:
|
|
434
|
-
if isinstance(b, Beforeware): bf,skip = b.f,b.skip
|
|
435
|
-
else: bf,skip = b,[]
|
|
436
|
-
if not any(re.fullmatch(r, req.url.path) for r in skip):
|
|
437
|
-
resp = await _wrap_call(bf, req, _sig(bf).parameters)
|
|
438
|
-
if not resp: resp = await _wrap_call(self.f, req, self.sig.parameters)
|
|
439
|
-
for a in self._app.after:
|
|
440
|
-
_,*wreq = await _wrap_req(req, _sig(a).parameters)
|
|
441
|
-
nr = a(resp, *wreq)
|
|
442
|
-
if nr: resp = nr
|
|
443
|
-
return _resp(req, resp, self.sig.return_annotation)
|
|
444
|
-
|
|
445
|
-
# %% ../nbs/api/00_core.ipynb
|
|
446
|
-
class RouterX(Router):
|
|
447
|
-
def __init__(self, app, routes=None, redirect_slashes=True, default=None, *, middleware=None):
|
|
448
|
-
self._app = app
|
|
449
|
-
super().__init__(routes, redirect_slashes, default, app.on_startup, app.on_shutdown,
|
|
450
|
-
lifespan=app.lifespan, middleware=middleware)
|
|
451
|
-
|
|
452
|
-
def _add_route(self, route):
|
|
453
|
-
route.methods = [m.upper() for m in listify(route.methods)]
|
|
454
|
-
self.routes = [r for r in self.routes if not
|
|
455
|
-
(r.path==route.path and r.name == route.name and
|
|
456
|
-
((route.methods is None) or (set(r.methods) == set(route.methods))))]
|
|
457
|
-
self.routes.append(route)
|
|
458
|
-
|
|
459
|
-
def add_route(self, path: str, endpoint: callable, methods=None, name=None, include_in_schema=True):
|
|
460
|
-
route = RouteX(self._app, path, endpoint=endpoint, methods=methods, name=name, include_in_schema=include_in_schema)
|
|
461
|
-
self._add_route(route)
|
|
462
|
-
|
|
463
|
-
def add_ws(self, path: str, recv: callable, conn:callable=None, disconn:callable=None, name=None):
|
|
464
|
-
route = WS_RouteX(self._app, path, recv=recv, conn=conn, disconn=disconn, name=name)
|
|
465
|
-
self._add_route(route)
|
|
466
|
-
|
|
467
414
|
# %% ../nbs/api/00_core.ipynb
|
|
468
415
|
htmx_exts = {
|
|
469
416
|
"head-support": "https://unpkg.com/htmx-ext-head-support@2.0.1/head-support.js",
|
|
@@ -522,6 +469,20 @@ def def_hdrs(htmx=True, surreal=True):
|
|
|
522
469
|
if htmx: hdrs = [htmxsrc,fhjsscr] + hdrs
|
|
523
470
|
return [charset, viewport] + hdrs
|
|
524
471
|
|
|
472
|
+
# %% ../nbs/api/00_core.ipynb
|
|
473
|
+
cors_allow = Middleware(CORSMiddleware, allow_credentials=True,
|
|
474
|
+
allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])
|
|
475
|
+
|
|
476
|
+
iframe_scr = Script("""
|
|
477
|
+
function sendmsg() {
|
|
478
|
+
window.parent.postMessage({height: document.documentElement.offsetHeight}, '*');
|
|
479
|
+
}
|
|
480
|
+
window.onload = function() {
|
|
481
|
+
sendmsg();
|
|
482
|
+
document.body.addEventListener('htmx:afterSettle', sendmsg);
|
|
483
|
+
document.body.addEventListener('htmx:wsAfterMessage', sendmsg);
|
|
484
|
+
};""")
|
|
485
|
+
|
|
525
486
|
# %% ../nbs/api/00_core.ipynb
|
|
526
487
|
class FastHTML(Starlette):
|
|
527
488
|
def __init__(self, debug=False, routes=None, middleware=None, exception_handlers=None,
|
|
@@ -536,6 +497,9 @@ class FastHTML(Starlette):
|
|
|
536
497
|
htmlkw = htmlkw or {}
|
|
537
498
|
if default_hdrs: hdrs = def_hdrs(htmx, surreal=surreal) + hdrs
|
|
538
499
|
hdrs += [Script(src=ext) for ext in exts.values()]
|
|
500
|
+
if IN_NOTEBOOK:
|
|
501
|
+
hdrs.append(iframe_scr)
|
|
502
|
+
middleware.append(cors_allow)
|
|
539
503
|
self.on_startup,self.on_shutdown,self.lifespan,self.hdrs,self.ftrs = on_startup,on_shutdown,lifespan,hdrs,ftrs
|
|
540
504
|
self.before,self.after,self.htmlkw,self.bodykw = before,after,htmlkw,bodykw
|
|
541
505
|
secret_key = get_key(secret_key, key_fname)
|
|
@@ -550,36 +514,78 @@ class FastHTML(Starlette):
|
|
|
550
514
|
exception_handlers[404] = _not_found
|
|
551
515
|
excs = {k:_wrap_ex(v, hdrs, ftrs, htmlkw, bodykw) for k,v in exception_handlers.items()}
|
|
552
516
|
super().__init__(debug, routes, middleware=middleware, exception_handlers=excs, on_startup=on_startup, on_shutdown=on_shutdown, lifespan=lifespan)
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
return f
|
|
517
|
+
|
|
518
|
+
def add_route(self, route):
|
|
519
|
+
route.methods = [m.upper() for m in listify(route.methods)]
|
|
520
|
+
self.router.routes = [r for r in self.router.routes if not
|
|
521
|
+
(r.path==route.path and r.name == route.name and
|
|
522
|
+
((route.methods is None) or (set(r.methods) == set(route.methods))))]
|
|
523
|
+
self.router.routes.append(route)
|
|
561
524
|
|
|
562
525
|
# %% ../nbs/api/00_core.ipynb
|
|
563
526
|
all_meths = 'get post put delete patch head trace options'.split()
|
|
564
527
|
|
|
528
|
+
# %% ../nbs/api/00_core.ipynb
|
|
529
|
+
@patch
|
|
530
|
+
def _endp(self:FastHTML, f):
|
|
531
|
+
sig = signature_ex(f, True)
|
|
532
|
+
async def _f(req):
|
|
533
|
+
resp = None
|
|
534
|
+
req.injects = []
|
|
535
|
+
req.hdrs,req.ftrs,req.htmlkw,req.bodykw = map(deepcopy, (self.hdrs,self.ftrs,self.htmlkw,self.bodykw))
|
|
536
|
+
req.hdrs,req.ftrs = listify(req.hdrs),listify(req.ftrs)
|
|
537
|
+
for b in self.before:
|
|
538
|
+
if not resp:
|
|
539
|
+
if isinstance(b, Beforeware): bf,skip = b.f,b.skip
|
|
540
|
+
else: bf,skip = b,[]
|
|
541
|
+
if not any(re.fullmatch(r, req.url.path) for r in skip):
|
|
542
|
+
resp = await _wrap_call(bf, req, _params(bf))
|
|
543
|
+
if not resp: resp = await _wrap_call(f, req, sig.parameters)
|
|
544
|
+
for a in self.after:
|
|
545
|
+
_,*wreq = await _wrap_req(req, _params(a))
|
|
546
|
+
nr = a(resp, *wreq)
|
|
547
|
+
if nr: resp = nr
|
|
548
|
+
return _resp(req, resp, sig.return_annotation)
|
|
549
|
+
return _f
|
|
550
|
+
|
|
551
|
+
# %% ../nbs/api/00_core.ipynb
|
|
552
|
+
@patch
|
|
553
|
+
def _add_ws(self:FastHTML, func, path, conn, disconn, name, middleware):
|
|
554
|
+
endp = _ws_endp(func, conn, disconn)
|
|
555
|
+
route = WebSocketRoute(path, endpoint=endp, name=name, middleware=middleware)
|
|
556
|
+
route.methods = ['ws']
|
|
557
|
+
self.add_route(route)
|
|
558
|
+
return func
|
|
559
|
+
|
|
560
|
+
# %% ../nbs/api/00_core.ipynb
|
|
561
|
+
@patch
|
|
562
|
+
def ws(self:FastHTML, path:str, conn=None, disconn=None, name=None, middleware=None):
|
|
563
|
+
"Add a websocket route at `path`"
|
|
564
|
+
def f(func=noop): return self._add_ws(func, path, conn, disconn, name=name, middleware=middleware)
|
|
565
|
+
return f
|
|
566
|
+
|
|
567
|
+
# %% ../nbs/api/00_core.ipynb
|
|
568
|
+
@patch
|
|
569
|
+
def _add_route(self:FastHTML, func, path, methods, name, include_in_schema):
|
|
570
|
+
n,fn,p = name,func.__name__,None if callable(path) else path
|
|
571
|
+
if methods: m = [methods] if isinstance(methods,str) else methods
|
|
572
|
+
elif fn in all_meths and p is not None: m = [fn]
|
|
573
|
+
else: m = ['get','post']
|
|
574
|
+
if not n: n = fn
|
|
575
|
+
if not p: p = '/'+('' if fn=='index' else fn)
|
|
576
|
+
route = Route(p, endpoint=self._endp(func), methods=m, name=n, include_in_schema=include_in_schema)
|
|
577
|
+
self.add_route(route)
|
|
578
|
+
lf = _mk_locfunc(func, p)
|
|
579
|
+
lf.__routename__ = n
|
|
580
|
+
return lf
|
|
581
|
+
|
|
582
|
+
# %% ../nbs/api/00_core.ipynb
|
|
565
583
|
@patch
|
|
566
584
|
def route(self:FastHTML, path:str=None, methods=None, name=None, include_in_schema=True):
|
|
567
585
|
"Add a route at `path`"
|
|
568
|
-
|
|
569
|
-
def f(func):
|
|
570
|
-
n,fn,p = name,func.__name__,pathstr
|
|
571
|
-
if methods: m = [methods] if isinstance(methods,str) else methods
|
|
572
|
-
elif fn in all_meths and p is not None: m = [fn]
|
|
573
|
-
else: m = ['get','post']
|
|
574
|
-
if not n: n = fn
|
|
575
|
-
if not p: p = '/'+('' if fn=='index' else fn)
|
|
576
|
-
self.router.add_route(p, func, methods=m, name=n, include_in_schema=include_in_schema)
|
|
577
|
-
lf = _mk_locfunc(func, p)
|
|
578
|
-
lf.__routename__ = n
|
|
579
|
-
return lf
|
|
586
|
+
def f(func): return self._add_route(func, path, methods, name=name, include_in_schema=include_in_schema)
|
|
580
587
|
return f(path) if callable(path) else f
|
|
581
588
|
|
|
582
|
-
# %% ../nbs/api/00_core.ipynb
|
|
583
589
|
for o in all_meths: setattr(FastHTML, o, partialmethod(FastHTML.route, methods=o))
|
|
584
590
|
|
|
585
591
|
# %% ../nbs/api/00_core.ipynb
|
|
@@ -616,6 +622,26 @@ class Client:
|
|
|
616
622
|
|
|
617
623
|
for o in ('get', 'post', 'delete', 'put', 'patch', 'options'): setattr(Client, o, partialmethod(Client._sync, o))
|
|
618
624
|
|
|
625
|
+
# %% ../nbs/api/00_core.ipynb
|
|
626
|
+
class APIRouter:
|
|
627
|
+
"Add routes to an app"
|
|
628
|
+
def __init__(self): self.routes,self.wss = [],[]
|
|
629
|
+
|
|
630
|
+
def __call__(self:FastHTML, path:str=None, methods=None, name=None, include_in_schema=True):
|
|
631
|
+
"Add a route at `path`"
|
|
632
|
+
def f(func): return self.routes.append((func, path,methods,name,include_in_schema))
|
|
633
|
+
return f(path) if callable(path) else f
|
|
634
|
+
|
|
635
|
+
def to_app(self, app):
|
|
636
|
+
"Add routes to `app`"
|
|
637
|
+
for args in self.routes: app._add_route(*args)
|
|
638
|
+
for args in self.wss : app._add_ws (*args)
|
|
639
|
+
|
|
640
|
+
def ws(self:FastHTML, path:str, conn=None, disconn=None, name=None, middleware=None):
|
|
641
|
+
"Add a websocket route at `path`"
|
|
642
|
+
def f(func=noop): return self.wss.append((func, path, conn, disconn, name, middleware))
|
|
643
|
+
return f
|
|
644
|
+
|
|
619
645
|
# %% ../nbs/api/00_core.ipynb
|
|
620
646
|
def cookie(key: str, value="", max_age=None, expires=None, path="/", domain=None, secure=False, httponly=False, samesite="lax",):
|
|
621
647
|
"Create a 'set-cookie' `HttpHeader`"
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
"""The `fast_app` convenience wrapper"""
|
|
2
2
|
|
|
3
|
-
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/10_fastapp.ipynb.
|
|
4
|
-
|
|
5
|
-
# %% ../nbs/api/10_fastapp.ipynb 3
|
|
6
|
-
from __future__ import annotations
|
|
7
|
-
|
|
8
3
|
import inspect,uvicorn
|
|
9
4
|
from fastcore.utils import *
|
|
10
5
|
from fastlite import *
|
|
@@ -13,10 +8,8 @@ from .pico import *
|
|
|
13
8
|
from .starlette import *
|
|
14
9
|
from .live_reload import FastHTMLWithLiveReload
|
|
15
10
|
|
|
16
|
-
# %% auto 0
|
|
17
11
|
__all__ = ['fast_app']
|
|
18
12
|
|
|
19
|
-
# %% ../nbs/api/10_fastapp.ipynb
|
|
20
13
|
def _get_tbl(dt, nm, schema):
|
|
21
14
|
render = schema.pop('render', None)
|
|
22
15
|
tbl = dt[nm]
|
|
@@ -26,7 +19,6 @@ def _get_tbl(dt, nm, schema):
|
|
|
26
19
|
if render: dc.__ft__ = render
|
|
27
20
|
return tbl,dc
|
|
28
21
|
|
|
29
|
-
# %% ../nbs/api/10_fastapp.ipynb
|
|
30
22
|
def _app_factory(*args, **kwargs) -> FastHTML | FastHTMLWithLiveReload:
|
|
31
23
|
"Creates a FastHTML or FastHTMLWithLiveReload app instance"
|
|
32
24
|
if kwargs.pop('live', False): return FastHTMLWithLiveReload(*args, **kwargs)
|
|
@@ -34,7 +26,6 @@ def _app_factory(*args, **kwargs) -> FastHTML | FastHTMLWithLiveReload:
|
|
|
34
26
|
kwargs.pop('reload_interval', None)
|
|
35
27
|
return FastHTML(*args, **kwargs)
|
|
36
28
|
|
|
37
|
-
# %% ../nbs/api/10_fastapp.ipynb
|
|
38
29
|
def fast_app(
|
|
39
30
|
db_file:Optional[str]=None, # Database file name, if needed
|
|
40
31
|
render:Optional[callable]=None, # Function used to render default database class
|
|
@@ -91,3 +82,4 @@ def fast_app(
|
|
|
91
82
|
dbtbls = [_get_tbl(db.t, k, v) for k,v in tbls.items()]
|
|
92
83
|
if len(dbtbls)==1: dbtbls=dbtbls[0]
|
|
93
84
|
return app,app.route,*dbtbls
|
|
85
|
+
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/06_jupyter.ipynb.
|
|
2
2
|
|
|
3
3
|
# %% auto 0
|
|
4
|
-
__all__ = ['
|
|
5
|
-
'ws_client', 'jupy_app']
|
|
4
|
+
__all__ = ['nb_serve', 'nb_serve_async', 'is_port_free', 'wait_port_free', 'JupyUvi', 'HTMX', 'ws_client']
|
|
6
5
|
|
|
7
6
|
# %% ../nbs/api/06_jupyter.ipynb
|
|
8
7
|
import asyncio, socket, time, uvicorn
|
|
@@ -51,10 +50,6 @@ def wait_port_free(port, host='localhost', max_wait=3):
|
|
|
51
50
|
if time.time() - start_time>max_wait: return print(f"Timeout")
|
|
52
51
|
time.sleep(0.1)
|
|
53
52
|
|
|
54
|
-
# %% ../nbs/api/06_jupyter.ipynb
|
|
55
|
-
cors_allow = Middleware(CORSMiddleware, allow_credentials=True,
|
|
56
|
-
allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])
|
|
57
|
-
|
|
58
53
|
# %% ../nbs/api/06_jupyter.ipynb
|
|
59
54
|
class JupyUvi:
|
|
60
55
|
"Start and stop a Jupyter compatible uvicorn server with ASGI `app` on `port` with `log_level`"
|
|
@@ -71,25 +66,6 @@ class JupyUvi:
|
|
|
71
66
|
self.server.should_exit = True
|
|
72
67
|
wait_port_free(self.port)
|
|
73
68
|
|
|
74
|
-
# %% ../nbs/api/06_jupyter.ipynb
|
|
75
|
-
# The script lets an iframe parent know of changes so that it can resize automatically.
|
|
76
|
-
_iframe_scr = Script("""
|
|
77
|
-
function sendmsg() {
|
|
78
|
-
window.parent.postMessage({height: document.documentElement.offsetHeight}, '*');
|
|
79
|
-
}
|
|
80
|
-
window.onload = function() {
|
|
81
|
-
sendmsg();
|
|
82
|
-
document.body.addEventListener('htmx:afterSettle', sendmsg);
|
|
83
|
-
document.body.addEventListener('htmx:wsAfterMessage', sendmsg);
|
|
84
|
-
};""")
|
|
85
|
-
|
|
86
|
-
# %% ../nbs/api/06_jupyter.ipynb
|
|
87
|
-
def FastJupy(hdrs=None, middleware=None, **kwargs):
|
|
88
|
-
"Same as FastHTML, but with Jupyter compatible middleware and headers added"
|
|
89
|
-
hdrs = listify(hdrs)+[_iframe_scr]
|
|
90
|
-
middleware = listify(middleware)+[cors_allow]
|
|
91
|
-
return FastHTML(hdrs=hdrs, middleware=middleware, **kwargs)
|
|
92
|
-
|
|
93
69
|
# %% ../nbs/api/06_jupyter.ipynb
|
|
94
70
|
def HTMX(path="", host='localhost', port=8000, iframe_height="auto"):
|
|
95
71
|
"An iframe which displays the HTMX application in a notebook."
|
|
@@ -114,10 +90,3 @@ def ws_client(app, nm='', host='localhost', port=8000, ws_connect='/ws', frame=T
|
|
|
114
90
|
def send(o): asyncio.create_task(app._send(o))
|
|
115
91
|
c.on(send)
|
|
116
92
|
return c
|
|
117
|
-
|
|
118
|
-
# %% ../nbs/api/06_jupyter.ipynb
|
|
119
|
-
def jupy_app(pico=False, hdrs=None, middleware=None, **kwargs):
|
|
120
|
-
"Same as `fast_app` but for Jupyter notebooks"
|
|
121
|
-
hdrs = listify(hdrs)+[_iframe_scr]
|
|
122
|
-
middleware = listify(middleware)+[cors_allow]
|
|
123
|
-
return fast_app(pico=pico, hdrs=hdrs, middleware=middleware, **kwargs)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from starlette.applications import Starlette
|
|
2
2
|
from starlette.middleware import Middleware
|
|
3
3
|
from starlette.middleware.sessions import SessionMiddleware
|
|
4
|
+
from starlette.middleware.cors import CORSMiddleware
|
|
4
5
|
from starlette.middleware.authentication import AuthenticationMiddleware
|
|
5
6
|
from starlette.authentication import AuthCredentials, AuthenticationBackend, AuthenticationError, SimpleUser, requires
|
|
6
7
|
from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[DEFAULT]
|
|
2
2
|
repo = fasthtml
|
|
3
3
|
lib_name = fasthtml
|
|
4
|
-
version = 0.
|
|
4
|
+
version = 0.8.0
|
|
5
5
|
min_python = 3.10
|
|
6
6
|
license = apache2
|
|
7
7
|
requirements = fastcore>=1.7.18 python-dateutil starlette>0.33 oauthlib itsdangerous uvicorn[standard]>=0.30 httpx fastlite>=0.0.9 python-multipart beautifulsoup4
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python-fasthtml-0.7.0 → python-fasthtml-0.8.0}/python_fasthtml.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|