python-fasthtml 0.5.3__tar.gz → 0.6.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.5.3/python_fasthtml.egg-info → python-fasthtml-0.6.0}/PKG-INFO +2 -2
- python-fasthtml-0.6.0/fasthtml/__init__.py +2 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/fasthtml/_modidx.py +3 -1
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/fasthtml/components.py +4 -1
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/fasthtml/components.pyi +1 -1
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/fasthtml/core.py +29 -13
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/fasthtml/core.pyi +40 -4
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/fasthtml/xtend.pyi +7 -6
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0/python_fasthtml.egg-info}/PKG-INFO +2 -2
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/python_fasthtml.egg-info/requires.txt +1 -1
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/settings.ini +2 -2
- python-fasthtml-0.5.3/fasthtml/__init__.py +0 -2
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/CONTRIBUTING.md +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/LICENSE +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/MANIFEST.in +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/README.md +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/fasthtml/authmw.py +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/fasthtml/basics.py +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/fasthtml/cli.py +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/fasthtml/common.py +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/fasthtml/fastapp.py +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/fasthtml/ft.py +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/fasthtml/js.py +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/fasthtml/katex.js +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/fasthtml/live_reload.py +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/fasthtml/oauth.py +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/fasthtml/pico.py +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/fasthtml/starlette.py +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/fasthtml/svg.py +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/fasthtml/toaster.py +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/fasthtml/xtend.py +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/python_fasthtml.egg-info/SOURCES.txt +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/python_fasthtml.egg-info/dependency_links.txt +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/python_fasthtml.egg-info/entry_points.txt +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/python_fasthtml.egg-info/not-zip-safe +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/python_fasthtml.egg-info/top_level.txt +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/setup.cfg +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/setup.py +0 -0
- {python-fasthtml-0.5.3 → python-fasthtml-0.6.0}/tests/test_toaster.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-fasthtml
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: The fastest way to create an HTML app
|
|
5
5
|
Home-page: https://github.com/AnswerDotAI/fasthtml
|
|
6
6
|
Author: Jeremy Howard
|
|
@@ -15,7 +15,7 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
15
15
|
Requires-Python: >=3.10
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
|
-
Requires-Dist: fastcore>=1.7.
|
|
18
|
+
Requires-Dist: fastcore>=1.7.8
|
|
19
19
|
Requires-Dist: python-dateutil
|
|
20
20
|
Requires-Dist: starlette>0.33
|
|
21
21
|
Requires-Dist: oauthlib
|
|
@@ -89,16 +89,18 @@ d = { 'settings': { 'branch': 'main',
|
|
|
89
89
|
'fasthtml.core._xt_cts': ('api/core.html#_xt_cts', 'fasthtml/core.py'),
|
|
90
90
|
'fasthtml.core._xt_resp': ('api/core.html#_xt_resp', 'fasthtml/core.py'),
|
|
91
91
|
'fasthtml.core.cookie': ('api/core.html#cookie', 'fasthtml/core.py'),
|
|
92
|
-
'fasthtml.core.date': ('api/core.html#date', 'fasthtml/core.py'),
|
|
93
92
|
'fasthtml.core.decode_uri': ('api/core.html#decode_uri', 'fasthtml/core.py'),
|
|
94
93
|
'fasthtml.core.flat_tuple': ('api/core.html#flat_tuple', 'fasthtml/core.py'),
|
|
95
94
|
'fasthtml.core.flat_xt': ('api/core.html#flat_xt', 'fasthtml/core.py'),
|
|
96
95
|
'fasthtml.core.form2dict': ('api/core.html#form2dict', 'fasthtml/core.py'),
|
|
97
96
|
'fasthtml.core.get_key': ('api/core.html#get_key', 'fasthtml/core.py'),
|
|
97
|
+
'fasthtml.core.parse_form': ('api/core.html#parse_form', 'fasthtml/core.py'),
|
|
98
|
+
'fasthtml.core.parsed_date': ('api/core.html#parsed_date', 'fasthtml/core.py'),
|
|
98
99
|
'fasthtml.core.reg_re_param': ('api/core.html#reg_re_param', 'fasthtml/core.py'),
|
|
99
100
|
'fasthtml.core.serve': ('api/core.html#serve', 'fasthtml/core.py'),
|
|
100
101
|
'fasthtml.core.signal_shutdown': ('api/core.html#signal_shutdown', 'fasthtml/core.py'),
|
|
101
102
|
'fasthtml.core.snake2hyphens': ('api/core.html#snake2hyphens', 'fasthtml/core.py'),
|
|
103
|
+
'fasthtml.core.str2date': ('api/core.html#str2date', 'fasthtml/core.py'),
|
|
102
104
|
'fasthtml.core.str2int': ('api/core.html#str2int', 'fasthtml/core.py'),
|
|
103
105
|
'fasthtml.core.uri': ('api/core.html#uri', 'fasthtml/core.py')},
|
|
104
106
|
'fasthtml.fastapp': { 'fasthtml.fastapp.ContainerX': ('api/fastapp.html#containerx', 'fasthtml/fastapp.py'),
|
|
@@ -97,9 +97,12 @@ def _fill_item(item, obj):
|
|
|
97
97
|
val = None if name is None else obj.get(name, None)
|
|
98
98
|
if val is not None and not 'skip' in attr:
|
|
99
99
|
if tag=='input':
|
|
100
|
-
if attr.get('type', '')
|
|
100
|
+
if attr.get('type', '') == 'checkbox':
|
|
101
101
|
if val: attr['checked'] = '1'
|
|
102
102
|
else: attr.pop('checked', '')
|
|
103
|
+
elif attr.get('type', '') == 'radio':
|
|
104
|
+
if val and val == attr['value']: attr['checked'] = '1'
|
|
105
|
+
else: attr.pop('checked', '')
|
|
103
106
|
else: attr['value'] = val
|
|
104
107
|
if tag=='textarea': cs=(val,)
|
|
105
108
|
if tag == 'select':
|
|
@@ -26,7 +26,7 @@ def attrmap_x(o):
|
|
|
26
26
|
fh_cfg['attrmap'] = attrmap_x
|
|
27
27
|
fh_cfg['valmap'] = valmap
|
|
28
28
|
|
|
29
|
-
def ft_html(tag: str, *c, id=None, cls=None, title=None, style=None, attrmap=None, valmap=None, **kwargs):
|
|
29
|
+
def ft_html(tag: str, *c, id=None, cls=None, title=None, style=None, attrmap=None, valmap=None, ft_cls=FT, **kwargs):
|
|
30
30
|
...
|
|
31
31
|
|
|
32
32
|
@use_kwargs(hx_attrs, keep=True)
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
# %% auto 0
|
|
6
6
|
__all__ = ['empty', 'htmx_hdrs', 'fh_cfg', 'htmx_resps', 'htmxsrc', 'htmxwssrc', 'fhjsscr', 'htmxctsrc', 'surrsrc', 'scopesrc',
|
|
7
|
-
'viewport', 'charset', 'all_meths', '
|
|
8
|
-
'
|
|
9
|
-
'
|
|
10
|
-
'cookie', 'reg_re_param', 'MiddlewareBase', 'FtResponse']
|
|
7
|
+
'viewport', 'charset', 'all_meths', 'parsed_date', 'snake2hyphens', 'HtmxHeaders', 'str2int', 'str2date',
|
|
8
|
+
'HttpHeader', 'HtmxResponseHeaders', 'form2dict', 'parse_form', 'flat_xt', 'Beforeware', 'EventStream',
|
|
9
|
+
'signal_shutdown', 'WS_RouteX', 'uri', 'decode_uri', 'flat_tuple', 'Redirect', 'RouteX', 'RouterX',
|
|
10
|
+
'get_key', 'FastHTML', 'serve', 'Client', 'cookie', 'reg_re_param', 'MiddlewareBase', 'FtResponse']
|
|
11
11
|
|
|
12
12
|
# %% ../nbs/api/00_core.ipynb
|
|
13
13
|
import json,uuid,inspect,types,uvicorn,signal,asyncio,threading
|
|
@@ -18,7 +18,7 @@ from fastcore.meta import use_kwargs_dict
|
|
|
18
18
|
|
|
19
19
|
from types import UnionType, SimpleNamespace as ns, GenericAlias
|
|
20
20
|
from typing import Optional, get_type_hints, get_args, get_origin, Union, Mapping, TypedDict, List, Any
|
|
21
|
-
from datetime import datetime
|
|
21
|
+
from datetime import datetime,date
|
|
22
22
|
from dataclasses import dataclass,fields
|
|
23
23
|
from collections import namedtuple
|
|
24
24
|
from inspect import isfunction,ismethod,Parameter,get_annotations
|
|
@@ -39,7 +39,7 @@ empty = Parameter.empty
|
|
|
39
39
|
def _sig(f): return signature_ex(f, True)
|
|
40
40
|
|
|
41
41
|
# %% ../nbs/api/00_core.ipynb
|
|
42
|
-
def
|
|
42
|
+
def parsed_date(s:str):
|
|
43
43
|
"Convert `s` to a datetime"
|
|
44
44
|
return dtparse.parse(s)
|
|
45
45
|
|
|
@@ -84,13 +84,18 @@ def _mk_list(t, v): return [t(o) for o in v]
|
|
|
84
84
|
# %% ../nbs/api/00_core.ipynb
|
|
85
85
|
fh_cfg = AttrDict(indent=True)
|
|
86
86
|
|
|
87
|
+
# %% ../nbs/api/00_core.ipynb
|
|
88
|
+
def str2date(s:str)->date:
|
|
89
|
+
"`date.fromisoformat` with empty string handling"
|
|
90
|
+
return date.fromisoformat(s) if s else None
|
|
91
|
+
|
|
87
92
|
# %% ../nbs/api/00_core.ipynb
|
|
88
93
|
def _fix_anno(t):
|
|
89
94
|
"Create appropriate callable type for casting a `str` to type `t` (or first type in `t` if union)"
|
|
90
95
|
origin = get_origin(t)
|
|
91
96
|
if origin is Union or origin is UnionType or origin in (list,List):
|
|
92
97
|
t = first(o for o in get_args(t) if o!=type(None))
|
|
93
|
-
d = {bool: str2bool, int: str2int}
|
|
98
|
+
d = {bool: str2bool, int: str2int, date: str2date}
|
|
94
99
|
res = d.get(t, t)
|
|
95
100
|
if origin in (list,List): return partial(_mk_list, res)
|
|
96
101
|
return lambda o: res(o[-1]) if isinstance(o,(list,tuple)) else res(o)
|
|
@@ -143,13 +148,25 @@ def form2dict(form: FormData) -> dict:
|
|
|
143
148
|
"Convert starlette form data to a dict"
|
|
144
149
|
return {k: _formitem(form, k) for k in form}
|
|
145
150
|
|
|
151
|
+
# %% ../nbs/api/00_core.ipynb
|
|
152
|
+
async def parse_form(req: Request) -> FormData:
|
|
153
|
+
"Starlette errors on empty multipart forms, so this checks for that situation"
|
|
154
|
+
ctype = req.headers.get("Content-Type", "")
|
|
155
|
+
if not ctype.startswith("multipart/form-data"): return await req.form()
|
|
156
|
+
try: boundary = ctype.split("boundary=")[1].strip()
|
|
157
|
+
except IndexError: raise HTTPException(400, "Invalid form-data: no boundary")
|
|
158
|
+
min_len = len(boundary) + 6
|
|
159
|
+
clen = int(req.headers.get("Content-Length", "0"))
|
|
160
|
+
if clen <= min_len: return FormData()
|
|
161
|
+
return await req.form()
|
|
162
|
+
|
|
146
163
|
# %% ../nbs/api/00_core.ipynb
|
|
147
164
|
async def _from_body(req, p):
|
|
148
165
|
anno = p.annotation
|
|
149
166
|
# Get the fields and types of type `anno`, if available
|
|
150
167
|
d = _annotations(anno)
|
|
151
168
|
if req.headers.get('content-type', None)=='application/json': data = await req.json()
|
|
152
|
-
else: data = form2dict(await req
|
|
169
|
+
else: data = form2dict(await parse_form(req))
|
|
153
170
|
cargs = {k: _form_arg(k, v, d) for k, v in data.items() if not d or k in d}
|
|
154
171
|
return anno(**cargs)
|
|
155
172
|
|
|
@@ -179,10 +196,9 @@ async def _find_p(req, arg:str, p:Parameter):
|
|
|
179
196
|
res = req.path_params.get(arg, None)
|
|
180
197
|
if res in (empty,None): res = req.cookies.get(arg, None)
|
|
181
198
|
if res in (empty,None): res = req.headers.get(snake2hyphens(arg), None)
|
|
182
|
-
if res in (empty,None): res = req.query_params.
|
|
183
|
-
if res
|
|
184
|
-
|
|
185
|
-
res = _formitem(frm, arg)
|
|
199
|
+
if res in (empty,None): res = req.query_params.getlist(arg)
|
|
200
|
+
if res==[]: res = None
|
|
201
|
+
if res in (empty,None): res = _formitem(await parse_form(req), arg)
|
|
186
202
|
# Raise 400 error if the param does not include a default
|
|
187
203
|
if (res in (empty,None)) and p.default is empty: raise HTTPException(400, f"Missing required field: {arg}")
|
|
188
204
|
# If we have a default, return that if we have no value
|
|
@@ -417,7 +433,7 @@ class RouteX(Route):
|
|
|
417
433
|
resp = None
|
|
418
434
|
req.injects = []
|
|
419
435
|
req.hdrs,req.ftrs,req.htmlkw,req.bodykw = map(deepcopy, (self._app.hdrs,self._app.ftrs,self._app.htmlkw,self._app.bodykw))
|
|
420
|
-
req.hdrs,req.ftrs =
|
|
436
|
+
req.hdrs,req.ftrs = listify(req.hdrs),listify(req.ftrs)
|
|
421
437
|
for b in self._app.before:
|
|
422
438
|
if not resp:
|
|
423
439
|
if isinstance(b, Beforeware): bf,skip = b.f,b.skip
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"""The `FastHTML` subclass of `Starlette`, along with the `RouterX` and `RouteX` classes it automatically uses."""
|
|
2
|
-
__all__ = ['empty', 'htmx_hdrs', 'fh_cfg', 'htmx_resps', 'htmxsrc', 'htmxwssrc', 'fhjsscr', 'htmxctsrc', 'surrsrc', 'scopesrc', 'viewport', 'charset', 'all_meths', '
|
|
3
|
-
import json, uuid, inspect, types, uvicorn, signal, asyncio
|
|
2
|
+
__all__ = ['empty', 'htmx_hdrs', 'fh_cfg', 'htmx_resps', 'htmxsrc', 'htmxwssrc', 'fhjsscr', 'htmxctsrc', 'surrsrc', 'scopesrc', 'viewport', 'charset', 'all_meths', 'parsed_date', 'snake2hyphens', 'HtmxHeaders', 'str2int', 'str2date', 'HttpHeader', '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']
|
|
3
|
+
import json, uuid, inspect, types, uvicorn, signal, asyncio, threading
|
|
4
4
|
from fastcore.utils import *
|
|
5
5
|
from fastcore.xml import *
|
|
6
6
|
from fastcore.meta import use_kwargs_dict
|
|
7
7
|
from types import UnionType, SimpleNamespace as ns, GenericAlias
|
|
8
8
|
from typing import Optional, get_type_hints, get_args, get_origin, Union, Mapping, TypedDict, List, Any
|
|
9
|
-
from datetime import datetime
|
|
9
|
+
from datetime import datetime, date
|
|
10
10
|
from dataclasses import dataclass, fields
|
|
11
11
|
from collections import namedtuple
|
|
12
12
|
from inspect import isfunction, ismethod, Parameter, get_annotations
|
|
@@ -16,13 +16,15 @@ from urllib.parse import urlencode, parse_qs, quote, unquote
|
|
|
16
16
|
from copy import copy, deepcopy
|
|
17
17
|
from warnings import warn
|
|
18
18
|
from dateutil import parser as dtparse
|
|
19
|
+
from httpx import ASGITransport, AsyncClient
|
|
20
|
+
from anyio import from_thread
|
|
19
21
|
from .starlette import *
|
|
20
22
|
empty = Parameter.empty
|
|
21
23
|
|
|
22
24
|
def _sig(f):
|
|
23
25
|
...
|
|
24
26
|
|
|
25
|
-
def
|
|
27
|
+
def parsed_date(s: str):
|
|
26
28
|
"""Convert `s` to a datetime"""
|
|
27
29
|
...
|
|
28
30
|
|
|
@@ -56,6 +58,10 @@ def _mk_list(t, v):
|
|
|
56
58
|
...
|
|
57
59
|
fh_cfg = AttrDict(indent=True)
|
|
58
60
|
|
|
61
|
+
def str2date(s: str) -> date:
|
|
62
|
+
"""`date.fromisoformat` with empty string handling"""
|
|
63
|
+
...
|
|
64
|
+
|
|
59
65
|
def _fix_anno(t):
|
|
60
66
|
"""Create appropriate callable type for casting a `str` to type `t` (or first type in `t` if union)"""
|
|
61
67
|
...
|
|
@@ -93,6 +99,10 @@ def form2dict(form: FormData) -> dict:
|
|
|
93
99
|
"""Convert starlette form data to a dict"""
|
|
94
100
|
...
|
|
95
101
|
|
|
102
|
+
async def parse_form(req: Request) -> FormData:
|
|
103
|
+
"""Starlette errors on empty multipart forms, so this checks for that situation"""
|
|
104
|
+
...
|
|
105
|
+
|
|
96
106
|
async def _from_body(req, p):
|
|
97
107
|
...
|
|
98
108
|
|
|
@@ -173,9 +183,15 @@ def flat_tuple(o):
|
|
|
173
183
|
"""Flatten lists"""
|
|
174
184
|
...
|
|
175
185
|
|
|
186
|
+
def _xt_cts(req, resp):
|
|
187
|
+
...
|
|
188
|
+
|
|
176
189
|
def _xt_resp(req, resp):
|
|
177
190
|
...
|
|
178
191
|
|
|
192
|
+
def _is_ft_resp(resp):
|
|
193
|
+
...
|
|
194
|
+
|
|
179
195
|
def _resp(req, resp, cls=empty):
|
|
180
196
|
...
|
|
181
197
|
|
|
@@ -258,6 +274,17 @@ def serve(appname=None, app='app', host='0.0.0.0', port=None, reload=True, reloa
|
|
|
258
274
|
"""Run the app in an async server, with live reload set as the default."""
|
|
259
275
|
...
|
|
260
276
|
|
|
277
|
+
class Client:
|
|
278
|
+
"""A simple httpx ASGI client that doesn't require `async`"""
|
|
279
|
+
|
|
280
|
+
def __init__(self, app, url='http://testserver'):
|
|
281
|
+
...
|
|
282
|
+
|
|
283
|
+
def _sync(self, method, url, **kwargs):
|
|
284
|
+
...
|
|
285
|
+
for o in ('get', 'post', 'delete', 'put', 'patch', 'options'):
|
|
286
|
+
setattr(Client, o, partialmethod(Client._sync, o))
|
|
287
|
+
|
|
261
288
|
def cookie(key: str, value='', max_age=None, expires=None, path='/', domain=None, secure=False, httponly=False, samesite='lax'):
|
|
262
289
|
"""Create a 'set-cookie' `HttpHeader`"""
|
|
263
290
|
...
|
|
@@ -270,4 +297,13 @@ reg_re_param('static', 'ico|gif|jpg|jpeg|webm|css|js|woff|png|svg|mp4|webp|ttf|o
|
|
|
270
297
|
class MiddlewareBase:
|
|
271
298
|
|
|
272
299
|
async def __call__(self, scope, receive, send) -> None:
|
|
300
|
+
...
|
|
301
|
+
|
|
302
|
+
class FtResponse:
|
|
303
|
+
"""Wrap an FT response with any Starlette `Response`"""
|
|
304
|
+
|
|
305
|
+
def __init__(self, content, status_code: int=200, headers=None, cls=HTMLResponse, media_type: str | None=None, background=None):
|
|
306
|
+
...
|
|
307
|
+
|
|
308
|
+
def __response__(self, req):
|
|
273
309
|
...
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""Simple extensions to standard HTML components, such as adding sensible defaults"""
|
|
2
|
-
__all__ = ['A', 'Form', 'AX', 'Hidden', 'CheckboxX', 'Script', 'Style', 'double_braces', 'undouble_braces', 'loose_format', 'ScriptX', 'replace_css_vars', 'StyleX', 'On', 'Prev', 'Now', 'AnyNow', 'run_js', 'HtmxOn', 'Titled', 'Socials', 'Favicon', 'jsd', 'clear']
|
|
2
|
+
__all__ = ['sid_scr', 'A', 'Form', 'AX', 'Hidden', 'CheckboxX', 'Script', 'Style', 'double_braces', 'undouble_braces', 'loose_format', 'ScriptX', 'replace_css_vars', 'StyleX', 'On', 'Prev', 'Now', 'AnyNow', 'run_js', 'HtmxOn', 'Titled', 'Socials', 'Favicon', 'jsd', 'clear']
|
|
3
3
|
from dataclasses import dataclass, asdict
|
|
4
4
|
from typing import Any
|
|
5
5
|
from fastcore.utils import *
|
|
@@ -32,11 +32,11 @@ def CheckboxX(checked: bool=False, label=None, value='1', id=None, name=None, *,
|
|
|
32
32
|
"""A Checkbox optionally inside a Label, preceded by a `Hidden` with matching name"""
|
|
33
33
|
...
|
|
34
34
|
|
|
35
|
-
def Script(code: str='', *, id=None, cls=None, title=None, style=None, attrmap=None, valmap=None, **kwargs) -> FT:
|
|
35
|
+
def Script(code: str='', *, id=None, cls=None, title=None, style=None, attrmap=None, valmap=None, ft_cls=fastcore.xml.FT, **kwargs) -> FT:
|
|
36
36
|
"""A Script tag that doesn't escape its code"""
|
|
37
37
|
...
|
|
38
38
|
|
|
39
|
-
def Style(*c, id=None, cls=None, title=None, style=None, attrmap=None, valmap=None, **kwargs) -> FT:
|
|
39
|
+
def Style(*c, id=None, cls=None, title=None, style=None, attrmap=None, valmap=None, ft_cls=fastcore.xml.FT, **kwargs) -> FT:
|
|
40
40
|
"""A Style tag that doesn't escape its code"""
|
|
41
41
|
...
|
|
42
42
|
|
|
@@ -65,11 +65,11 @@ def StyleX(fname, **kw):
|
|
|
65
65
|
...
|
|
66
66
|
|
|
67
67
|
def On(code: str, event: str='click', sel: str='', me=True):
|
|
68
|
-
"""An async surreal.js script block event handler for `event` on selector `sel`"""
|
|
68
|
+
"""An async surreal.js script block event handler for `event` on selector `sel`, making available parent `p`, event `ev`, and target `e`"""
|
|
69
69
|
...
|
|
70
70
|
|
|
71
71
|
def Prev(code: str, event: str='click'):
|
|
72
|
-
"""An async surreal.js script block event handler for `event` on previous sibling"""
|
|
72
|
+
"""An async surreal.js script block event handler for `event` on previous sibling, with same vars as `On`"""
|
|
73
73
|
...
|
|
74
74
|
|
|
75
75
|
def Now(code: str, sel: str=''):
|
|
@@ -104,4 +104,5 @@ def jsd(org, repo, root, path, prov='gh', typ='script', ver=None, esm=False, **k
|
|
|
104
104
|
...
|
|
105
105
|
|
|
106
106
|
def clear(id):
|
|
107
|
-
...
|
|
107
|
+
...
|
|
108
|
+
sid_scr = Script('\nfunction uuid() {\n return [...crypto.getRandomValues(new Uint8Array(10))].map(b=>b.toString(36)).join(\'\');\n}\n\nsessionStorage.setItem("sid", sessionStorage.getItem("sid") || uuid());\n\nhtmx.on("htmx:configRequest", (e) => {\n const sid = sessionStorage.getItem("sid");\n if (sid) {\n const url = new URL(e.detail.path, window.location.origin);\n url.searchParams.set(\'sid\', sid);\n e.detail.path = url.pathname + url.search;\n }\n});\n')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-fasthtml
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: The fastest way to create an HTML app
|
|
5
5
|
Home-page: https://github.com/AnswerDotAI/fasthtml
|
|
6
6
|
Author: Jeremy Howard
|
|
@@ -15,7 +15,7 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
15
15
|
Requires-Python: >=3.10
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
|
-
Requires-Dist: fastcore>=1.7.
|
|
18
|
+
Requires-Dist: fastcore>=1.7.8
|
|
19
19
|
Requires-Dist: python-dateutil
|
|
20
20
|
Requires-Dist: starlette>0.33
|
|
21
21
|
Requires-Dist: oauthlib
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[DEFAULT]
|
|
2
2
|
repo = fasthtml
|
|
3
3
|
lib_name = fasthtml
|
|
4
|
-
version = 0.
|
|
4
|
+
version = 0.6.0
|
|
5
5
|
min_python = 3.10
|
|
6
6
|
license = apache2
|
|
7
|
-
requirements = fastcore>=1.7.
|
|
7
|
+
requirements = fastcore>=1.7.8 python-dateutil starlette>0.33 oauthlib itsdangerous uvicorn[standard]>=0.30 httpx fastlite>=0.0.9 python-multipart beautifulsoup4
|
|
8
8
|
dev_requirements = ipython lxml pysymbol_llm
|
|
9
9
|
black_formatting = False
|
|
10
10
|
conda_user = fastai
|
|
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.5.3 → python-fasthtml-0.6.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
|