python-fasthtml 0.11.0__tar.gz → 0.12.1__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.11.0/python_fasthtml.egg-info → python_fasthtml-0.12.1}/PKG-INFO +50 -2
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/README.md +48 -0
- python_fasthtml-0.12.1/fasthtml/__init__.py +2 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/_modidx.py +7 -2
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/components.py +5 -5
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/components.pyi +17 -5
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/core.py +41 -33
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/core.pyi +44 -15
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/jupyter.py +14 -1
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/xtend.pyi +2 -2
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1/python_fasthtml.egg-info}/PKG-INFO +50 -2
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/python_fasthtml.egg-info/requires.txt +1 -1
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/settings.ini +4 -4
- python_fasthtml-0.11.0/fasthtml/__init__.py +0 -2
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/CONTRIBUTING.md +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/LICENSE +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/MANIFEST.in +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/authmw.py +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/basics.py +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/cli.py +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/common.py +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/fastapp.py +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/ft.py +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/js.py +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/katex.js +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/live_reload.py +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/oauth.py +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/pico.py +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/starlette.py +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/svg.py +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/toaster.py +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/fasthtml/xtend.py +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/pyproject.toml +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/python_fasthtml.egg-info/SOURCES.txt +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/python_fasthtml.egg-info/dependency_links.txt +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/python_fasthtml.egg-info/entry_points.txt +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/python_fasthtml.egg-info/not-zip-safe +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/python_fasthtml.egg-info/top_level.txt +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/setup.cfg +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/setup.py +0 -0
- {python_fasthtml-0.11.0 → python_fasthtml-0.12.1}/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.12.1
|
|
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 and contributors
|
|
@@ -22,7 +22,7 @@ Requires-Dist: oauthlib
|
|
|
22
22
|
Requires-Dist: itsdangerous
|
|
23
23
|
Requires-Dist: uvicorn[standard]>=0.30
|
|
24
24
|
Requires-Dist: httpx
|
|
25
|
-
Requires-Dist: fastlite>=0.
|
|
25
|
+
Requires-Dist: fastlite>=0.1.1
|
|
26
26
|
Requires-Dist: python-multipart
|
|
27
27
|
Requires-Dist: beautifulsoup4
|
|
28
28
|
Provides-Extra: dev
|
|
@@ -185,3 +185,51 @@ Finally, join the FastHTML community to ask questions, share your work,
|
|
|
185
185
|
and learn from others:
|
|
186
186
|
|
|
187
187
|
- [Discord](https://discord.gg/qcXvcxMhdP)
|
|
188
|
+
|
|
189
|
+
## Other languages and related projects
|
|
190
|
+
|
|
191
|
+
If you’re not a Python user, or are keen to try out a new language,
|
|
192
|
+
we’ll list here other projects that have a similar approach to FastHTML.
|
|
193
|
+
(Please reach out if you know of any other projects that you’d like to
|
|
194
|
+
see added.)
|
|
195
|
+
|
|
196
|
+
- [htmgo](https://htmgo.dev/) (Go): “*htmgo is a lightweight pure go way
|
|
197
|
+
to build interactive websites / web applications using go & htmx. By
|
|
198
|
+
combining the speed & simplicity of go + hypermedia attributes (htmx)
|
|
199
|
+
to add interactivity to websites, all conveniently wrapped in pure go,
|
|
200
|
+
you can build simple, fast, interactive websites without touching
|
|
201
|
+
javascript. All compiled to a single deployable binary*”
|
|
202
|
+
|
|
203
|
+
If you’re just interested in functional HTML components, rather than a
|
|
204
|
+
full HTMX server solution, consider:
|
|
205
|
+
|
|
206
|
+
- [fastcore.xml.FT](https://fastcore.fast.ai/xml.html): This is actually
|
|
207
|
+
what FastHTML uses behind the scenes
|
|
208
|
+
- [htpy](https://htpy.dev/): Similar to
|
|
209
|
+
[`fastcore.xml.FT`](https://fastcore.fast.ai/xml.html#ft), but with a
|
|
210
|
+
somewhat different syntax
|
|
211
|
+
- [elm-html](https://package.elm-lang.org/packages/elm/html/latest/):
|
|
212
|
+
Elm’s built-in HTML library with a type-safe functional approach
|
|
213
|
+
- [hiccup](https://github.com/weavejester/hiccup): Popular library for
|
|
214
|
+
representing HTML in Clojure using vectors
|
|
215
|
+
- [hiccl](https://github.com/garlic0x1/hiccl): HTML generation library
|
|
216
|
+
for Common Lisp inspired by Clojure’s Hiccup
|
|
217
|
+
- [Falco.Markup](https://github.com/pimbrouwers/Falco): F# HTML DSL and
|
|
218
|
+
web framework with type-safe HTML generation
|
|
219
|
+
- [Lucid](https://github.com/chrisdone/lucid): Type-safe HTML generation
|
|
220
|
+
for Haskell using monad transformers
|
|
221
|
+
- [dream-html](https://github.com/aantron/dream): Part of the Dream web
|
|
222
|
+
framework for OCaml, provides type-safe HTML templating
|
|
223
|
+
|
|
224
|
+
For other hypermedia application platforms, not based on HTMX, take a
|
|
225
|
+
look at:
|
|
226
|
+
|
|
227
|
+
- [Hotwire/Turbo](https://turbo.hotwired.dev/): Rails-oriented framework
|
|
228
|
+
that similarly uses HTML-over-the-wire
|
|
229
|
+
- [LiveView](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html):
|
|
230
|
+
Phoenix framework’s solution for building interactive web apps with
|
|
231
|
+
minimal JavaScript
|
|
232
|
+
- [Unpoly](https://unpoly.com/): Another HTML-over-the-wire framework
|
|
233
|
+
with progressive enhancement
|
|
234
|
+
- [Livewire](https://laravel-livewire.com/): Laravel’s take on building
|
|
235
|
+
dynamic interfaces with minimal JavaScript
|
|
@@ -153,3 +153,51 @@ Finally, join the FastHTML community to ask questions, share your work,
|
|
|
153
153
|
and learn from others:
|
|
154
154
|
|
|
155
155
|
- [Discord](https://discord.gg/qcXvcxMhdP)
|
|
156
|
+
|
|
157
|
+
## Other languages and related projects
|
|
158
|
+
|
|
159
|
+
If you’re not a Python user, or are keen to try out a new language,
|
|
160
|
+
we’ll list here other projects that have a similar approach to FastHTML.
|
|
161
|
+
(Please reach out if you know of any other projects that you’d like to
|
|
162
|
+
see added.)
|
|
163
|
+
|
|
164
|
+
- [htmgo](https://htmgo.dev/) (Go): “*htmgo is a lightweight pure go way
|
|
165
|
+
to build interactive websites / web applications using go & htmx. By
|
|
166
|
+
combining the speed & simplicity of go + hypermedia attributes (htmx)
|
|
167
|
+
to add interactivity to websites, all conveniently wrapped in pure go,
|
|
168
|
+
you can build simple, fast, interactive websites without touching
|
|
169
|
+
javascript. All compiled to a single deployable binary*”
|
|
170
|
+
|
|
171
|
+
If you’re just interested in functional HTML components, rather than a
|
|
172
|
+
full HTMX server solution, consider:
|
|
173
|
+
|
|
174
|
+
- [fastcore.xml.FT](https://fastcore.fast.ai/xml.html): This is actually
|
|
175
|
+
what FastHTML uses behind the scenes
|
|
176
|
+
- [htpy](https://htpy.dev/): Similar to
|
|
177
|
+
[`fastcore.xml.FT`](https://fastcore.fast.ai/xml.html#ft), but with a
|
|
178
|
+
somewhat different syntax
|
|
179
|
+
- [elm-html](https://package.elm-lang.org/packages/elm/html/latest/):
|
|
180
|
+
Elm’s built-in HTML library with a type-safe functional approach
|
|
181
|
+
- [hiccup](https://github.com/weavejester/hiccup): Popular library for
|
|
182
|
+
representing HTML in Clojure using vectors
|
|
183
|
+
- [hiccl](https://github.com/garlic0x1/hiccl): HTML generation library
|
|
184
|
+
for Common Lisp inspired by Clojure’s Hiccup
|
|
185
|
+
- [Falco.Markup](https://github.com/pimbrouwers/Falco): F# HTML DSL and
|
|
186
|
+
web framework with type-safe HTML generation
|
|
187
|
+
- [Lucid](https://github.com/chrisdone/lucid): Type-safe HTML generation
|
|
188
|
+
for Haskell using monad transformers
|
|
189
|
+
- [dream-html](https://github.com/aantron/dream): Part of the Dream web
|
|
190
|
+
framework for OCaml, provides type-safe HTML templating
|
|
191
|
+
|
|
192
|
+
For other hypermedia application platforms, not based on HTMX, take a
|
|
193
|
+
look at:
|
|
194
|
+
|
|
195
|
+
- [Hotwire/Turbo](https://turbo.hotwired.dev/): Rails-oriented framework
|
|
196
|
+
that similarly uses HTML-over-the-wire
|
|
197
|
+
- [LiveView](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html):
|
|
198
|
+
Phoenix framework’s solution for building interactive web apps with
|
|
199
|
+
minimal JavaScript
|
|
200
|
+
- [Unpoly](https://unpoly.com/): Another HTML-over-the-wire framework
|
|
201
|
+
with progressive enhancement
|
|
202
|
+
- [Livewire](https://laravel-livewire.com/): Laravel’s take on building
|
|
203
|
+
dynamic interfaces with minimal JavaScript
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Autogenerated by nbdev
|
|
2
2
|
|
|
3
3
|
d = { 'settings': { 'branch': 'main',
|
|
4
|
-
'doc_baseurl': '/
|
|
5
|
-
'doc_host': 'https://
|
|
4
|
+
'doc_baseurl': '/',
|
|
5
|
+
'doc_host': 'https://docs.fastht.ml',
|
|
6
6
|
'git_url': 'https://github.com/AnswerDotAI/fasthtml',
|
|
7
7
|
'lib_path': 'fasthtml'},
|
|
8
8
|
'syms': { 'fasthtml.authmw': {},
|
|
@@ -133,6 +133,11 @@ d = { 'settings': { 'branch': 'main',
|
|
|
133
133
|
'fasthtml.jupyter.JupyUvi.__init__': ('api/jupyter.html#jupyuvi.__init__', 'fasthtml/jupyter.py'),
|
|
134
134
|
'fasthtml.jupyter.JupyUvi.start': ('api/jupyter.html#jupyuvi.start', 'fasthtml/jupyter.py'),
|
|
135
135
|
'fasthtml.jupyter.JupyUvi.stop': ('api/jupyter.html#jupyuvi.stop', 'fasthtml/jupyter.py'),
|
|
136
|
+
'fasthtml.jupyter.JupyUviAsync': ('api/jupyter.html#jupyuviasync', 'fasthtml/jupyter.py'),
|
|
137
|
+
'fasthtml.jupyter.JupyUviAsync.__init__': ( 'api/jupyter.html#jupyuviasync.__init__',
|
|
138
|
+
'fasthtml/jupyter.py'),
|
|
139
|
+
'fasthtml.jupyter.JupyUviAsync.start': ('api/jupyter.html#jupyuviasync.start', 'fasthtml/jupyter.py'),
|
|
140
|
+
'fasthtml.jupyter.JupyUviAsync.stop': ('api/jupyter.html#jupyuviasync.stop', 'fasthtml/jupyter.py'),
|
|
136
141
|
'fasthtml.jupyter.htmx_config_port': ('api/jupyter.html#htmx_config_port', 'fasthtml/jupyter.py'),
|
|
137
142
|
'fasthtml.jupyter.is_port_free': ('api/jupyter.html#is_port_free', 'fasthtml/jupyter.py'),
|
|
138
143
|
'fasthtml.jupyter.nb_serve': ('api/jupyter.html#nb_serve', 'fasthtml/jupyter.py'),
|
|
@@ -8,10 +8,10 @@ __all__ = ['named', 'html_attrs', 'hx_attrs', 'hx_attrs_annotations', 'show', 'a
|
|
|
8
8
|
'Article', 'Aside', 'Audio', 'B', 'Base', 'Bdi', 'Bdo', 'Blockquote', 'Body', 'Br', 'Button', 'Canvas',
|
|
9
9
|
'Caption', 'Cite', 'Code', 'Col', 'Colgroup', 'Data', 'Datalist', 'Dd', 'Del', 'Details', 'Dfn', 'Dialog',
|
|
10
10
|
'Div', 'Dl', 'Dt', 'Em', 'Embed', 'Fencedframe', 'Fieldset', 'Figcaption', 'Figure', 'Footer', 'Form', 'H1',
|
|
11
|
-
'H2', 'H3', 'H4', 'H5', 'H6', 'Head', 'Header', 'Hgroup', 'Hr', '
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
11
|
+
'H2', 'H3', 'H4', 'H5', 'H6', 'Head', 'Header', 'Hgroup', 'Hr', 'I', 'Iframe', 'Img', 'Input', 'Ins', 'Kbd',
|
|
12
|
+
'Label', 'Legend', 'Li', 'Link', 'Main', 'Map', 'Mark', 'Menu', 'Meta', 'Meter', 'Nav', 'Noscript', 'Object',
|
|
13
|
+
'Ol', 'Optgroup', 'Option', 'Output', 'P', 'Picture', 'PortalExperimental', 'Pre', 'Progress', 'Q', 'Rp',
|
|
14
|
+
'Rt', 'Ruby', 'S', 'Samp', 'Script', 'Search', 'Section', 'Select', 'Slot', 'Small', 'Source', 'Span',
|
|
15
15
|
'Strong', 'Style', 'Sub', 'Summary', 'Sup', 'Table', 'Tbody', 'Td', 'Template', 'Textarea', 'Tfoot', 'Th',
|
|
16
16
|
'Thead', 'Time', 'Title', 'Tr', 'Track', 'U', 'Ul', 'Var', 'Video', 'Wbr']
|
|
17
17
|
|
|
@@ -110,7 +110,7 @@ _all_ = [
|
|
|
110
110
|
'Button', 'Canvas', 'Caption', 'Cite', 'Code', 'Col', 'Colgroup', 'Data', 'Datalist', 'Dd', 'Del', 'Details', 'Dfn',
|
|
111
111
|
'Dialog', 'Div', 'Dl', 'Dt', 'Em', 'Embed', 'Fencedframe', 'Fieldset', 'Figcaption', 'Figure', 'Footer', 'Form',
|
|
112
112
|
'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'Head', 'Header',
|
|
113
|
-
'Hgroup', 'Hr', '
|
|
113
|
+
'Hgroup', 'Hr', 'I', 'Iframe', 'Img', 'Input', 'Ins', 'Kbd', 'Label', 'Legend', 'Li',
|
|
114
114
|
'Link', 'Main', 'Map', 'Mark', 'Menu', 'Meta', 'Meter', 'Nav', 'Noscript', 'Object', 'Ol', 'Optgroup', 'Option', 'Output',
|
|
115
115
|
'P', 'Picture', 'PortalExperimental', 'Pre', 'Progress', 'Q', 'Rp', 'Rt', 'Ruby', 'S', 'Samp', 'Script', 'Search',
|
|
116
116
|
'Section', 'Select', 'Slot', 'Small', 'Source', 'Span', 'Strong', 'Style', 'Sub', 'Summary', 'Sup', 'Table', 'Tbody',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""`ft_html` and `ft_hx` functions to add some conveniences to `ft`, along with a full set of basic HTML components, and functions to work with forms and `FT` conversion"""
|
|
2
|
-
__all__ = ['named', 'html_attrs', 'hx_attrs', 'hx_attrs_annotations', 'show', 'attrmap_x', 'ft_html', 'ft_hx', 'File', 'fill_form', 'fill_dataclass', 'find_inputs', 'html2ft', 'sse_message', 'A', 'Abbr', 'Address', 'Area', 'Article', 'Aside', 'Audio', 'B', 'Base', 'Bdi', 'Bdo', 'Blockquote', 'Body', 'Br', 'Button', 'Canvas', 'Caption', 'Cite', 'Code', 'Col', 'Colgroup', 'Data', 'Datalist', 'Dd', 'Del', 'Details', 'Dfn', 'Dialog', 'Div', 'Dl', 'Dt', 'Em', 'Embed', 'Fencedframe', 'Fieldset', 'Figcaption', 'Figure', 'Footer', 'Form', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'Head', 'Header', 'Hgroup', 'Hr', '
|
|
2
|
+
__all__ = ['named', 'html_attrs', 'hx_attrs', 'hx_attrs_annotations', 'show', 'attrmap_x', 'ft_html', 'ft_hx', 'File', 'fill_form', 'fill_dataclass', 'find_inputs', 'html2ft', 'sse_message', 'A', 'Abbr', 'Address', 'Area', 'Article', 'Aside', 'Audio', 'B', 'Base', 'Bdi', 'Bdo', 'Blockquote', 'Body', 'Br', 'Button', 'Canvas', 'Caption', 'Cite', 'Code', 'Col', 'Colgroup', 'Data', 'Datalist', 'Dd', 'Del', 'Details', 'Dfn', 'Dialog', 'Div', 'Dl', 'Dt', 'Em', 'Embed', 'Fencedframe', 'Fieldset', 'Figcaption', 'Figure', 'Footer', 'Form', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'Head', 'Header', 'Hgroup', 'Hr', 'I', 'Iframe', 'Img', 'Input', 'Ins', 'Kbd', 'Label', 'Legend', 'Li', 'Link', 'Main', 'Map', 'Mark', 'Menu', 'Meta', 'Meter', 'Nav', 'Noscript', 'Object', 'Ol', 'Optgroup', 'Option', 'Output', 'P', 'Picture', 'PortalExperimental', 'Pre', 'Progress', 'Q', 'Rp', 'Rt', 'Ruby', 'S', 'Samp', 'Script', 'Search', 'Section', 'Select', 'Slot', 'Small', 'Source', 'Span', 'Strong', 'Style', 'Sub', 'Summary', 'Sup', 'Table', 'Tbody', 'Td', 'Template', 'Textarea', 'Tfoot', 'Th', 'Thead', 'Time', 'Title', 'Tr', 'Track', 'U', 'Ul', 'Var', 'Video', 'Wbr']
|
|
3
3
|
from dataclasses import dataclass, asdict, is_dataclass, make_dataclass, replace, astuple, MISSING
|
|
4
4
|
from bs4 import BeautifulSoup, Comment
|
|
5
5
|
from typing import Literal, Optional
|
|
@@ -17,11 +17,23 @@ except ImportError:
|
|
|
17
17
|
def show(ft, *rest):
|
|
18
18
|
"""Renders FT Components into HTML within a Jupyter notebook."""
|
|
19
19
|
...
|
|
20
|
+
|
|
21
|
+
@patch
|
|
22
|
+
def __str__(self: FT):
|
|
23
|
+
...
|
|
24
|
+
|
|
25
|
+
@patch
|
|
26
|
+
def __radd__(self: FT, b):
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
@patch
|
|
30
|
+
def __add__(self: FT, b):
|
|
31
|
+
...
|
|
20
32
|
named = set('a button form frame iframe img input map meta object param select textarea'.split())
|
|
21
33
|
html_attrs = 'id cls title style accesskey contenteditable dir draggable enterkeyhint hidden inert inputmode lang popover spellcheck tabindex translate'.split()
|
|
22
34
|
hx_attrs = 'get post put delete patch trigger target swap swap_oob include select select_oob indicator push_url confirm disable replace_url vals disabled_elt ext headers history history_elt indicator inherit params preserve prompt replace_url request sync validate'
|
|
23
35
|
hx_attrs = [f'hx_{o}' for o in hx_attrs.split()]
|
|
24
|
-
hx_attrs_annotations = {'hx_swap': Literal['innerHTML', 'outerHTML', 'afterbegin', 'beforebegin', 'beforeend', 'afterend', 'delete', 'none'] | str, 'hx_swap_oob': Literal['true', 'innerHTML', 'outerHTML', 'afterbegin', 'beforebegin', 'beforeend', 'afterend', 'delete', 'none'] | str, 'hx_push_url': Literal['true', 'false'] | str, 'hx_replace_url': Literal['true', 'false'] | str, 'hx_disabled_elt': Literal['this', 'next', 'previous'] | str, 'hx_history': Literal['false'] | str, 'hx_params': Literal['*', 'none'] | str, '
|
|
36
|
+
hx_attrs_annotations = {'hx_swap': Literal['innerHTML', 'outerHTML', 'afterbegin', 'beforebegin', 'beforeend', 'afterend', 'delete', 'none'] | str, 'hx_swap_oob': Literal['true', 'innerHTML', 'outerHTML', 'afterbegin', 'beforebegin', 'beforeend', 'afterend', 'delete', 'none'] | str, 'hx_push_url': Literal['true', 'false'] | str, 'hx_replace_url': Literal['true', 'false'] | str, 'hx_disabled_elt': Literal['this', 'next', 'previous'] | str, 'hx_history': Literal['false'] | str, 'hx_params': Literal['*', 'none'] | str, 'hx_validate': Literal['true', 'false']}
|
|
25
37
|
hx_attrs_annotations |= {o: str for o in set(hx_attrs) - set(hx_attrs_annotations.keys())}
|
|
26
38
|
hx_attrs_annotations = {k: Optional[v] for k, v in hx_attrs_annotations.items()}
|
|
27
39
|
hx_attrs = html_attrs + hx_attrs
|
|
@@ -32,15 +44,16 @@ fh_cfg['attrmap'] = attrmap_x
|
|
|
32
44
|
fh_cfg['valmap'] = valmap
|
|
33
45
|
fh_cfg['ft_cls'] = FT
|
|
34
46
|
fh_cfg['auto_id'] = False
|
|
47
|
+
fh_cfg['auto_name'] = True
|
|
35
48
|
|
|
36
|
-
def ft_html(tag: str, *c, id=None, cls=None, title=None, style=None, attrmap=None, valmap=None, ft_cls=None,
|
|
49
|
+
def ft_html(tag: str, *c, id=None, cls=None, title=None, style=None, attrmap=None, valmap=None, ft_cls=None, **kwargs):
|
|
37
50
|
...
|
|
38
51
|
|
|
39
52
|
@use_kwargs(hx_attrs, keep=True)
|
|
40
53
|
def ft_hx(tag: str, *c, target_id=None, hx_vals=None, hx_target=None, **kwargs):
|
|
41
54
|
...
|
|
42
55
|
_g = globals()
|
|
43
|
-
_all_ = ['A', 'Abbr', 'Address', 'Area', 'Article', 'Aside', 'Audio', 'B', 'Base', 'Bdi', 'Bdo', 'Blockquote', 'Body', 'Br', 'Button', 'Canvas', 'Caption', 'Cite', 'Code', 'Col', 'Colgroup', 'Data', 'Datalist', 'Dd', 'Del', 'Details', 'Dfn', 'Dialog', 'Div', 'Dl', 'Dt', 'Em', 'Embed', 'Fencedframe', 'Fieldset', 'Figcaption', 'Figure', 'Footer', 'Form', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'Head', 'Header', 'Hgroup', 'Hr', '
|
|
56
|
+
_all_ = ['A', 'Abbr', 'Address', 'Area', 'Article', 'Aside', 'Audio', 'B', 'Base', 'Bdi', 'Bdo', 'Blockquote', 'Body', 'Br', 'Button', 'Canvas', 'Caption', 'Cite', 'Code', 'Col', 'Colgroup', 'Data', 'Datalist', 'Dd', 'Del', 'Details', 'Dfn', 'Dialog', 'Div', 'Dl', 'Dt', 'Em', 'Embed', 'Fencedframe', 'Fieldset', 'Figcaption', 'Figure', 'Footer', 'Form', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'Head', 'Header', 'Hgroup', 'Hr', 'I', 'Iframe', 'Img', 'Input', 'Ins', 'Kbd', 'Label', 'Legend', 'Li', 'Link', 'Main', 'Map', 'Mark', 'Menu', 'Meta', 'Meter', 'Nav', 'Noscript', 'Object', 'Ol', 'Optgroup', 'Option', 'Output', 'P', 'Picture', 'PortalExperimental', 'Pre', 'Progress', 'Q', 'Rp', 'Rt', 'Ruby', 'S', 'Samp', 'Script', 'Search', 'Section', 'Select', 'Slot', 'Small', 'Source', 'Span', 'Strong', 'Style', 'Sub', 'Summary', 'Sup', 'Table', 'Tbody', 'Td', 'Template', 'Textarea', 'Tfoot', 'Th', 'Thead', 'Time', 'Title', 'Tr', 'Track', 'U', 'Ul', 'Var', 'Video', 'Wbr']
|
|
44
57
|
for o in _all_:
|
|
45
58
|
_g[o] = partial(ft_hx, o.lower())
|
|
46
59
|
|
|
@@ -125,7 +138,6 @@ def Head(*c, id:Any=None, cls:Any=None, title:Any=None, style:Any=None, accesske
|
|
|
125
138
|
def Header(*c, id:Any=None, cls:Any=None, title:Any=None, style:Any=None, accesskey:Any=None, contenteditable:Any=None, dir:Any=None, draggable:Any=None, enterkeyhint:Any=None, hidden:Any=None, inert:Any=None, inputmode:Any=None, lang:Any=None, popover:Any=None, spellcheck:Any=None, tabindex:Any=None, translate:Any=None, hx_get:Optional[str]=None, hx_post:Optional[str]=None, hx_put:Optional[str]=None, hx_delete:Optional[str]=None, hx_patch:Optional[str]=None, hx_trigger:Optional[str]=None, hx_target:Optional[str]=None, hx_swap:Union[Literal['innerHTML', 'outerHTML', 'afterbegin', 'beforebegin', 'beforeend', 'afterend', 'delete', 'none'], str, NoneType]=None, hx_swap_oob:Union[Literal['true', 'innerHTML', 'outerHTML', 'afterbegin', 'beforebegin', 'beforeend', 'afterend', 'delete', 'none'], str, NoneType]=None, hx_include:Optional[str]=None, hx_select:Optional[str]=None, hx_select_oob:Optional[str]=None, hx_indicator:Optional[str]=None, hx_push_url:Union[Literal['true', 'false'], str, NoneType]=None, hx_confirm:Optional[str]=None, hx_disable:Optional[str]=None, hx_replace_url:Union[Literal['true', 'false'], str, NoneType]=None, hx_vals:Optional[str]=None, hx_disabled_elt:Union[Literal['this', 'next', 'previous'], str, NoneType]=None, hx_ext:Optional[str]=None, hx_headers:Optional[str]=None, hx_history:Union[Literal['false'], str, NoneType]=None, hx_history_elt:Optional[str]=None, hx_indicator:Optional[str]=None, hx_inherit:Optional[str]=None, hx_params:Union[Literal['*', 'none'], str, NoneType]=None, hx_preserve:Optional[str]=None, hx_prompt:Optional[str]=None, hx_replace_url:Union[Literal['true', 'false'], str, NoneType]=None, hx_request:Optional[str]=None, hx_sync:Optional[str]=None, hx_validate:Optional[Literal['true', 'false']]=None, **kwargs): ...
|
|
126
139
|
def Hgroup(*c, id:Any=None, cls:Any=None, title:Any=None, style:Any=None, accesskey:Any=None, contenteditable:Any=None, dir:Any=None, draggable:Any=None, enterkeyhint:Any=None, hidden:Any=None, inert:Any=None, inputmode:Any=None, lang:Any=None, popover:Any=None, spellcheck:Any=None, tabindex:Any=None, translate:Any=None, hx_get:Optional[str]=None, hx_post:Optional[str]=None, hx_put:Optional[str]=None, hx_delete:Optional[str]=None, hx_patch:Optional[str]=None, hx_trigger:Optional[str]=None, hx_target:Optional[str]=None, hx_swap:Union[Literal['innerHTML', 'outerHTML', 'afterbegin', 'beforebegin', 'beforeend', 'afterend', 'delete', 'none'], str, NoneType]=None, hx_swap_oob:Union[Literal['true', 'innerHTML', 'outerHTML', 'afterbegin', 'beforebegin', 'beforeend', 'afterend', 'delete', 'none'], str, NoneType]=None, hx_include:Optional[str]=None, hx_select:Optional[str]=None, hx_select_oob:Optional[str]=None, hx_indicator:Optional[str]=None, hx_push_url:Union[Literal['true', 'false'], str, NoneType]=None, hx_confirm:Optional[str]=None, hx_disable:Optional[str]=None, hx_replace_url:Union[Literal['true', 'false'], str, NoneType]=None, hx_vals:Optional[str]=None, hx_disabled_elt:Union[Literal['this', 'next', 'previous'], str, NoneType]=None, hx_ext:Optional[str]=None, hx_headers:Optional[str]=None, hx_history:Union[Literal['false'], str, NoneType]=None, hx_history_elt:Optional[str]=None, hx_indicator:Optional[str]=None, hx_inherit:Optional[str]=None, hx_params:Union[Literal['*', 'none'], str, NoneType]=None, hx_preserve:Optional[str]=None, hx_prompt:Optional[str]=None, hx_replace_url:Union[Literal['true', 'false'], str, NoneType]=None, hx_request:Optional[str]=None, hx_sync:Optional[str]=None, hx_validate:Optional[Literal['true', 'false']]=None, **kwargs): ...
|
|
127
140
|
def Hr(*c, id:Any=None, cls:Any=None, title:Any=None, style:Any=None, accesskey:Any=None, contenteditable:Any=None, dir:Any=None, draggable:Any=None, enterkeyhint:Any=None, hidden:Any=None, inert:Any=None, inputmode:Any=None, lang:Any=None, popover:Any=None, spellcheck:Any=None, tabindex:Any=None, translate:Any=None, hx_get:Optional[str]=None, hx_post:Optional[str]=None, hx_put:Optional[str]=None, hx_delete:Optional[str]=None, hx_patch:Optional[str]=None, hx_trigger:Optional[str]=None, hx_target:Optional[str]=None, hx_swap:Union[Literal['innerHTML', 'outerHTML', 'afterbegin', 'beforebegin', 'beforeend', 'afterend', 'delete', 'none'], str, NoneType]=None, hx_swap_oob:Union[Literal['true', 'innerHTML', 'outerHTML', 'afterbegin', 'beforebegin', 'beforeend', 'afterend', 'delete', 'none'], str, NoneType]=None, hx_include:Optional[str]=None, hx_select:Optional[str]=None, hx_select_oob:Optional[str]=None, hx_indicator:Optional[str]=None, hx_push_url:Union[Literal['true', 'false'], str, NoneType]=None, hx_confirm:Optional[str]=None, hx_disable:Optional[str]=None, hx_replace_url:Union[Literal['true', 'false'], str, NoneType]=None, hx_vals:Optional[str]=None, hx_disabled_elt:Union[Literal['this', 'next', 'previous'], str, NoneType]=None, hx_ext:Optional[str]=None, hx_headers:Optional[str]=None, hx_history:Union[Literal['false'], str, NoneType]=None, hx_history_elt:Optional[str]=None, hx_indicator:Optional[str]=None, hx_inherit:Optional[str]=None, hx_params:Union[Literal['*', 'none'], str, NoneType]=None, hx_preserve:Optional[str]=None, hx_prompt:Optional[str]=None, hx_replace_url:Union[Literal['true', 'false'], str, NoneType]=None, hx_request:Optional[str]=None, hx_sync:Optional[str]=None, hx_validate:Optional[Literal['true', 'false']]=None, **kwargs): ...
|
|
128
|
-
def Html(*c, id:Any=None, cls:Any=None, title:Any=None, style:Any=None, accesskey:Any=None, contenteditable:Any=None, dir:Any=None, draggable:Any=None, enterkeyhint:Any=None, hidden:Any=None, inert:Any=None, inputmode:Any=None, lang:Any=None, popover:Any=None, spellcheck:Any=None, tabindex:Any=None, translate:Any=None, hx_get:Optional[str]=None, hx_post:Optional[str]=None, hx_put:Optional[str]=None, hx_delete:Optional[str]=None, hx_patch:Optional[str]=None, hx_trigger:Optional[str]=None, hx_target:Optional[str]=None, hx_swap:Union[Literal['innerHTML', 'outerHTML', 'afterbegin', 'beforebegin', 'beforeend', 'afterend', 'delete', 'none'], str, NoneType]=None, hx_swap_oob:Union[Literal['true', 'innerHTML', 'outerHTML', 'afterbegin', 'beforebegin', 'beforeend', 'afterend', 'delete', 'none'], str, NoneType]=None, hx_include:Optional[str]=None, hx_select:Optional[str]=None, hx_select_oob:Optional[str]=None, hx_indicator:Optional[str]=None, hx_push_url:Union[Literal['true', 'false'], str, NoneType]=None, hx_confirm:Optional[str]=None, hx_disable:Optional[str]=None, hx_replace_url:Union[Literal['true', 'false'], str, NoneType]=None, hx_vals:Optional[str]=None, hx_disabled_elt:Union[Literal['this', 'next', 'previous'], str, NoneType]=None, hx_ext:Optional[str]=None, hx_headers:Optional[str]=None, hx_history:Union[Literal['false'], str, NoneType]=None, hx_history_elt:Optional[str]=None, hx_indicator:Optional[str]=None, hx_inherit:Optional[str]=None, hx_params:Union[Literal['*', 'none'], str, NoneType]=None, hx_preserve:Optional[str]=None, hx_prompt:Optional[str]=None, hx_replace_url:Union[Literal['true', 'false'], str, NoneType]=None, hx_request:Optional[str]=None, hx_sync:Optional[str]=None, hx_validate:Optional[Literal['true', 'false']]=None, **kwargs): ...
|
|
129
141
|
def I(*c, id:Any=None, cls:Any=None, title:Any=None, style:Any=None, accesskey:Any=None, contenteditable:Any=None, dir:Any=None, draggable:Any=None, enterkeyhint:Any=None, hidden:Any=None, inert:Any=None, inputmode:Any=None, lang:Any=None, popover:Any=None, spellcheck:Any=None, tabindex:Any=None, translate:Any=None, hx_get:Optional[str]=None, hx_post:Optional[str]=None, hx_put:Optional[str]=None, hx_delete:Optional[str]=None, hx_patch:Optional[str]=None, hx_trigger:Optional[str]=None, hx_target:Optional[str]=None, hx_swap:Union[Literal['innerHTML', 'outerHTML', 'afterbegin', 'beforebegin', 'beforeend', 'afterend', 'delete', 'none'], str, NoneType]=None, hx_swap_oob:Union[Literal['true', 'innerHTML', 'outerHTML', 'afterbegin', 'beforebegin', 'beforeend', 'afterend', 'delete', 'none'], str, NoneType]=None, hx_include:Optional[str]=None, hx_select:Optional[str]=None, hx_select_oob:Optional[str]=None, hx_indicator:Optional[str]=None, hx_push_url:Union[Literal['true', 'false'], str, NoneType]=None, hx_confirm:Optional[str]=None, hx_disable:Optional[str]=None, hx_replace_url:Union[Literal['true', 'false'], str, NoneType]=None, hx_vals:Optional[str]=None, hx_disabled_elt:Union[Literal['this', 'next', 'previous'], str, NoneType]=None, hx_ext:Optional[str]=None, hx_headers:Optional[str]=None, hx_history:Union[Literal['false'], str, NoneType]=None, hx_history_elt:Optional[str]=None, hx_indicator:Optional[str]=None, hx_inherit:Optional[str]=None, hx_params:Union[Literal['*', 'none'], str, NoneType]=None, hx_preserve:Optional[str]=None, hx_prompt:Optional[str]=None, hx_replace_url:Union[Literal['true', 'false'], str, NoneType]=None, hx_request:Optional[str]=None, hx_sync:Optional[str]=None, hx_validate:Optional[Literal['true', 'false']]=None, **kwargs): ...
|
|
130
142
|
def Iframe(*c, name:Any=None, id:Any=None, cls:Any=None, title:Any=None, style:Any=None, accesskey:Any=None, contenteditable:Any=None, dir:Any=None, draggable:Any=None, enterkeyhint:Any=None, hidden:Any=None, inert:Any=None, inputmode:Any=None, lang:Any=None, popover:Any=None, spellcheck:Any=None, tabindex:Any=None, translate:Any=None, hx_get:Optional[str]=None, hx_post:Optional[str]=None, hx_put:Optional[str]=None, hx_delete:Optional[str]=None, hx_patch:Optional[str]=None, hx_trigger:Optional[str]=None, hx_target:Optional[str]=None, hx_swap:Union[Literal['innerHTML', 'outerHTML', 'afterbegin', 'beforebegin', 'beforeend', 'afterend', 'delete', 'none'], str, NoneType]=None, hx_swap_oob:Union[Literal['true', 'innerHTML', 'outerHTML', 'afterbegin', 'beforebegin', 'beforeend', 'afterend', 'delete', 'none'], str, NoneType]=None, hx_include:Optional[str]=None, hx_select:Optional[str]=None, hx_select_oob:Optional[str]=None, hx_indicator:Optional[str]=None, hx_push_url:Union[Literal['true', 'false'], str, NoneType]=None, hx_confirm:Optional[str]=None, hx_disable:Optional[str]=None, hx_replace_url:Union[Literal['true', 'false'], str, NoneType]=None, hx_vals:Optional[str]=None, hx_disabled_elt:Union[Literal['this', 'next', 'previous'], str, NoneType]=None, hx_ext:Optional[str]=None, hx_headers:Optional[str]=None, hx_history:Union[Literal['false'], str, NoneType]=None, hx_history_elt:Optional[str]=None, hx_indicator:Optional[str]=None, hx_inherit:Optional[str]=None, hx_params:Union[Literal['*', 'none'], str, NoneType]=None, hx_preserve:Optional[str]=None, hx_prompt:Optional[str]=None, hx_replace_url:Union[Literal['true', 'false'], str, NoneType]=None, hx_request:Optional[str]=None, hx_sync:Optional[str]=None, hx_validate:Optional[Literal['true', 'false']]=None, **kwargs): ...
|
|
131
143
|
def Img(*c, name:Any=None, id:Any=None, cls:Any=None, title:Any=None, style:Any=None, accesskey:Any=None, contenteditable:Any=None, dir:Any=None, draggable:Any=None, enterkeyhint:Any=None, hidden:Any=None, inert:Any=None, inputmode:Any=None, lang:Any=None, popover:Any=None, spellcheck:Any=None, tabindex:Any=None, translate:Any=None, hx_get:Optional[str]=None, hx_post:Optional[str]=None, hx_put:Optional[str]=None, hx_delete:Optional[str]=None, hx_patch:Optional[str]=None, hx_trigger:Optional[str]=None, hx_target:Optional[str]=None, hx_swap:Union[Literal['innerHTML', 'outerHTML', 'afterbegin', 'beforebegin', 'beforeend', 'afterend', 'delete', 'none'], str, NoneType]=None, hx_swap_oob:Union[Literal['true', 'innerHTML', 'outerHTML', 'afterbegin', 'beforebegin', 'beforeend', 'afterend', 'delete', 'none'], str, NoneType]=None, hx_include:Optional[str]=None, hx_select:Optional[str]=None, hx_select_oob:Optional[str]=None, hx_indicator:Optional[str]=None, hx_push_url:Union[Literal['true', 'false'], str, NoneType]=None, hx_confirm:Optional[str]=None, hx_disable:Optional[str]=None, hx_replace_url:Union[Literal['true', 'false'], str, NoneType]=None, hx_vals:Optional[str]=None, hx_disabled_elt:Union[Literal['this', 'next', 'previous'], str, NoneType]=None, hx_ext:Optional[str]=None, hx_headers:Optional[str]=None, hx_history:Union[Literal['false'], str, NoneType]=None, hx_history_elt:Optional[str]=None, hx_indicator:Optional[str]=None, hx_inherit:Optional[str]=None, hx_params:Union[Literal['*', 'none'], str, NoneType]=None, hx_preserve:Optional[str]=None, hx_prompt:Optional[str]=None, hx_replace_url:Union[Literal['true', 'false'], str, NoneType]=None, hx_request:Optional[str]=None, hx_sync:Optional[str]=None, hx_validate:Optional[Literal['true', 'false']]=None, **kwargs): ...
|
|
@@ -11,7 +11,7 @@ __all__ = ['empty', 'htmx_hdrs', 'fh_cfg', 'htmx_resps', 'htmx_exts', 'htmxsrc',
|
|
|
11
11
|
'reg_re_param', 'MiddlewareBase', 'FtResponse', 'unqid', 'setup_ws']
|
|
12
12
|
|
|
13
13
|
# %% ../nbs/api/00_core.ipynb
|
|
14
|
-
import json,uuid,inspect,types,
|
|
14
|
+
import json,uuid,inspect,types,signal,asyncio,threading,inspect
|
|
15
15
|
|
|
16
16
|
from fastcore.utils import *
|
|
17
17
|
from fastcore.xml import *
|
|
@@ -260,7 +260,7 @@ async def _send_ws(ws, resp):
|
|
|
260
260
|
|
|
261
261
|
def _ws_endp(recv, conn=None, disconn=None):
|
|
262
262
|
cls = type('WS_Endp', (WebSocketEndpoint,), {"encoding":"text"})
|
|
263
|
-
|
|
263
|
+
|
|
264
264
|
async def _generic_handler(handler, ws, data=None):
|
|
265
265
|
wd = _wrap_ws(ws, loads(data) if data else {}, _params(handler))
|
|
266
266
|
resp = await _handle(handler, wd)
|
|
@@ -285,13 +285,13 @@ def EventStream(s):
|
|
|
285
285
|
|
|
286
286
|
# %% ../nbs/api/00_core.ipynb
|
|
287
287
|
def signal_shutdown():
|
|
288
|
+
from uvicorn.main import Server
|
|
288
289
|
event = asyncio.Event()
|
|
289
|
-
|
|
290
|
+
@patch
|
|
291
|
+
def handle_exit(self:Server, *args, **kwargs):
|
|
290
292
|
event.set()
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
for sig in (signal.SIGINT, signal.SIGTERM): signal.signal(sig, signal_handler)
|
|
293
|
+
self.force_exit = True
|
|
294
|
+
self._orig_handle_exit(*args, **kwargs)
|
|
295
295
|
return event
|
|
296
296
|
|
|
297
297
|
# %% ../nbs/api/00_core.ipynb
|
|
@@ -299,7 +299,7 @@ def uri(_arg, **kwargs):
|
|
|
299
299
|
return f"{quote(_arg)}/{urlencode(kwargs, doseq=True)}"
|
|
300
300
|
|
|
301
301
|
# %% ../nbs/api/00_core.ipynb
|
|
302
|
-
def decode_uri(s):
|
|
302
|
+
def decode_uri(s):
|
|
303
303
|
arg,_,kw = s.partition('/')
|
|
304
304
|
return unquote(arg), {k:v[0] for k,v in parse_qs(kw).items()}
|
|
305
305
|
|
|
@@ -329,7 +329,7 @@ def _url_for(req, t):
|
|
|
329
329
|
if callable(t): t = t.__routename__
|
|
330
330
|
kw = {}
|
|
331
331
|
if t.find('/')>-1 and (t.find('?')<0 or t.find('/')<t.find('?')): t,kw = decode_uri(t)
|
|
332
|
-
t,m,q = t.partition('?')
|
|
332
|
+
t,m,q = t.partition('?')
|
|
333
333
|
return f"{req.url_path_for(t, **kw)}{m}{q}"
|
|
334
334
|
|
|
335
335
|
def _find_targets(req, resp):
|
|
@@ -397,28 +397,28 @@ def _xt_cts(req, resp):
|
|
|
397
397
|
return _to_xml(req, resp, indent=fh_cfg.indent), http_hdrs, ts
|
|
398
398
|
|
|
399
399
|
# %% ../nbs/api/00_core.ipynb
|
|
400
|
-
def _xt_resp(req, resp):
|
|
400
|
+
def _xt_resp(req, resp, status_code):
|
|
401
401
|
cts,http_hdrs,tasks = _xt_cts(req, resp)
|
|
402
|
-
return HTMLResponse(cts, headers=http_hdrs, background=tasks)
|
|
402
|
+
return HTMLResponse(cts, status_code=status_code, headers=http_hdrs, background=tasks)
|
|
403
403
|
|
|
404
404
|
# %% ../nbs/api/00_core.ipynb
|
|
405
405
|
def _is_ft_resp(resp): return isinstance(resp, _iter_typs+(HttpHeader,FT)) or hasattr(resp, '__ft__')
|
|
406
406
|
|
|
407
407
|
# %% ../nbs/api/00_core.ipynb
|
|
408
|
-
def _resp(req, resp, cls=empty):
|
|
408
|
+
def _resp(req, resp, cls=empty, status_code=200):
|
|
409
409
|
if not resp: resp=()
|
|
410
410
|
if hasattr(resp, '__response__'): resp = resp.__response__(req)
|
|
411
411
|
if cls in (Any,FT): cls=empty
|
|
412
412
|
if isinstance(resp, FileResponse) and not os.path.exists(resp.path): raise HTTPException(404, resp.path)
|
|
413
|
-
if cls is not empty: return cls(resp)
|
|
414
|
-
if isinstance(resp, Response): return resp
|
|
415
|
-
if _is_ft_resp(resp): return _xt_resp(req, resp)
|
|
413
|
+
if cls is not empty: return cls(resp, status_code=status_code)
|
|
414
|
+
if isinstance(resp, Response): return resp # respect manually set status_code
|
|
415
|
+
if _is_ft_resp(resp): return _xt_resp(req, resp, status_code)
|
|
416
416
|
if isinstance(resp, str): cls = HTMLResponse
|
|
417
417
|
elif isinstance(resp, Mapping): cls = JSONResponse
|
|
418
418
|
else:
|
|
419
419
|
resp = str(resp)
|
|
420
420
|
cls = HTMLResponse
|
|
421
|
-
return cls(resp)
|
|
421
|
+
return cls(resp, status_code=status_code)
|
|
422
422
|
|
|
423
423
|
# %% ../nbs/api/00_core.ipynb
|
|
424
424
|
class Redirect:
|
|
@@ -467,19 +467,25 @@ def get_key(key=None, fname='.sesskey'):
|
|
|
467
467
|
def _list(o): return [] if not o else list(o) if isinstance(o, (tuple,list)) else [o]
|
|
468
468
|
|
|
469
469
|
# %% ../nbs/api/00_core.ipynb
|
|
470
|
-
def _wrap_ex(f, hdrs, ftrs, htmlkw, bodykw, body_wrap):
|
|
470
|
+
def _wrap_ex(f, status_code, hdrs, ftrs, htmlkw, bodykw, body_wrap):
|
|
471
471
|
async def _f(req, exc):
|
|
472
472
|
req.hdrs,req.ftrs,req.htmlkw,req.bodykw = map(deepcopy, (hdrs, ftrs, htmlkw, bodykw))
|
|
473
473
|
req.body_wrap = body_wrap
|
|
474
474
|
res = await _handle(f, (req, exc))
|
|
475
|
-
return _resp(req, res)
|
|
475
|
+
return _resp(req, res, status_code=status_code)
|
|
476
476
|
return _f
|
|
477
477
|
|
|
478
478
|
# %% ../nbs/api/00_core.ipynb
|
|
479
479
|
def qp(p:str, **kw) -> str:
|
|
480
|
-
"Add
|
|
481
|
-
|
|
482
|
-
|
|
480
|
+
"Add parameters kw to path p"
|
|
481
|
+
def _sub(m):
|
|
482
|
+
pre,post = m.groups()
|
|
483
|
+
if pre not in kw: return f'{{{pre}{post or ""}}}'
|
|
484
|
+
pre = kw.pop(pre)
|
|
485
|
+
return '' if pre in (False,None) else str(pre)
|
|
486
|
+
p = re.sub(r'\{([^:}]+)(:.+?)?}', _sub, p)
|
|
487
|
+
# encode query params
|
|
488
|
+
return p + ('?' + urlencode({k:'' if v in (False,None) else v for k,v in kw.items()},doseq=True) if kw else '')
|
|
483
489
|
|
|
484
490
|
# %% ../nbs/api/00_core.ipynb
|
|
485
491
|
def def_hdrs(htmx=True, surreal=True):
|
|
@@ -523,7 +529,8 @@ class FastHTML(Starlette):
|
|
|
523
529
|
from IPython.display import display,HTML
|
|
524
530
|
if nb_hdrs: display(HTML(to_xml(tuple(hdrs))))
|
|
525
531
|
middleware.append(cors_allow)
|
|
526
|
-
|
|
532
|
+
on_startup,on_shutdown = listify(on_startup) or None,listify(on_shutdown) or None
|
|
533
|
+
self.lifespan,self.hdrs,self.ftrs = lifespan,hdrs,ftrs
|
|
527
534
|
self.body_wrap,self.before,self.after,self.htmlkw,self.bodykw = body_wrap,before,after,htmlkw,bodykw
|
|
528
535
|
secret_key = get_key(secret_key, key_fname)
|
|
529
536
|
if sess_cls:
|
|
@@ -532,12 +539,12 @@ class FastHTML(Starlette):
|
|
|
532
539
|
https_only=sess_https_only, domain=sess_domain)
|
|
533
540
|
middleware.append(sess)
|
|
534
541
|
exception_handlers = ifnone(exception_handlers, {})
|
|
535
|
-
if 404 not in exception_handlers:
|
|
536
|
-
def _not_found(req, exc): return Response('404 Not Found', status_code=404)
|
|
542
|
+
if 404 not in exception_handlers:
|
|
543
|
+
def _not_found(req, exc): return Response('404 Not Found', status_code=404)
|
|
537
544
|
exception_handlers[404] = _not_found
|
|
538
|
-
excs = {k:_wrap_ex(v, hdrs, ftrs, htmlkw, bodykw, body_wrap=body_wrap) for k,v in exception_handlers.items()}
|
|
545
|
+
excs = {k:_wrap_ex(v, k, hdrs, ftrs, htmlkw, bodykw, body_wrap=body_wrap) for k,v in exception_handlers.items()}
|
|
539
546
|
super().__init__(debug, routes, middleware=middleware, exception_handlers=excs, on_startup=on_startup, on_shutdown=on_shutdown, lifespan=lifespan)
|
|
540
|
-
|
|
547
|
+
|
|
541
548
|
def add_route(self, route):
|
|
542
549
|
route.methods = [m.upper() for m in listify(route.methods)]
|
|
543
550
|
self.router.routes = [r for r in self.router.routes if not
|
|
@@ -635,7 +642,7 @@ def serve(
|
|
|
635
642
|
reload=True, # Default is to reload the app upon code changes
|
|
636
643
|
reload_includes:list[str]|str|None=None, # Additional files to watch for changes
|
|
637
644
|
reload_excludes:list[str]|str|None=None # Files to ignore for changes
|
|
638
|
-
):
|
|
645
|
+
):
|
|
639
646
|
"Run the app in an async server, with live reload set as the default."
|
|
640
647
|
bk = inspect.currentframe().f_back
|
|
641
648
|
glb = bk.f_globals
|
|
@@ -643,6 +650,7 @@ def serve(
|
|
|
643
650
|
if not appname:
|
|
644
651
|
if glb.get('__name__')=='__main__': appname = Path(glb.get('__file__', '')).stem
|
|
645
652
|
elif code.co_name=='main' and bk.f_back.f_globals.get('__name__')=='__main__': appname = inspect.getmodule(bk).__name__
|
|
653
|
+
import uvicorn
|
|
646
654
|
if appname:
|
|
647
655
|
if not port: port=int(os.getenv("PORT", default=5001))
|
|
648
656
|
print(f'Link: http://{"localhost" if host=="0.0.0.0" else host}:{port}')
|
|
@@ -664,7 +672,7 @@ for o in ('get', 'post', 'delete', 'put', 'patch', 'options'): setattr(Client, o
|
|
|
664
672
|
class RouteFuncs:
|
|
665
673
|
def __init__(self): super().__setattr__('_funcs', {})
|
|
666
674
|
def __setattr__(self, name, value): self._funcs[name] = value
|
|
667
|
-
def __getattr__(self, name):
|
|
675
|
+
def __getattr__(self, name):
|
|
668
676
|
if name in all_meths: raise AttributeError("Route functions with HTTP Names are not accessible here")
|
|
669
677
|
try: return self._funcs[name]
|
|
670
678
|
except KeyError: raise AttributeError(f"No route named {name} found in route functions")
|
|
@@ -673,7 +681,7 @@ class RouteFuncs:
|
|
|
673
681
|
# %% ../nbs/api/00_core.ipynb
|
|
674
682
|
class APIRouter:
|
|
675
683
|
"Add routes to an app"
|
|
676
|
-
def __init__(self, prefix:str|None=None, body_wrap=noop_body):
|
|
684
|
+
def __init__(self, prefix:str|None=None, body_wrap=noop_body):
|
|
677
685
|
self.routes,self.wss = [],[]
|
|
678
686
|
self.rt_funcs = RouteFuncs() # Store wrapped route function for discoverability
|
|
679
687
|
self.prefix = prefix if prefix else ""
|
|
@@ -695,7 +703,7 @@ class APIRouter:
|
|
|
695
703
|
self.routes.append((func, p, methods, name, include_in_schema, body_wrap or self.body_wrap))
|
|
696
704
|
return wrapped
|
|
697
705
|
return f(path) if callable(path) else f
|
|
698
|
-
|
|
706
|
+
|
|
699
707
|
def __getattr__(self, name):
|
|
700
708
|
try: return getattr(self.rt_funcs, name)
|
|
701
709
|
except AttributeError: return super().__getattr__(self, name)
|
|
@@ -704,7 +712,7 @@ class APIRouter:
|
|
|
704
712
|
"Add routes to `app`"
|
|
705
713
|
for args in self.routes: app._add_route(*args)
|
|
706
714
|
for args in self.wss: app._add_ws(*args)
|
|
707
|
-
|
|
715
|
+
|
|
708
716
|
def ws(self, path:str, conn=None, disconn=None, name=None, middleware=None):
|
|
709
717
|
"Add a websocket route at `path`"
|
|
710
718
|
def f(func=noop): return self.wss.append((func, f"{self.prefix}{path}", conn, disconn, name, middleware))
|
|
@@ -739,7 +747,7 @@ def reg_re_param(m, s):
|
|
|
739
747
|
# %% ../nbs/api/00_core.ipynb
|
|
740
748
|
# Starlette doesn't have the '?', so it chomps the whole remaining URL
|
|
741
749
|
reg_re_param("path", ".*?")
|
|
742
|
-
reg_re_param("static", "ico|gif|jpg|jpeg|webm|css|js|woff|png|svg|mp4|webp|ttf|otf|eot|woff2|txt|html|map")
|
|
750
|
+
reg_re_param("static", "ico|gif|jpg|jpeg|webm|css|js|woff|png|svg|mp4|webp|ttf|otf|eot|woff2|txt|html|map|pdf")
|
|
743
751
|
|
|
744
752
|
@patch
|
|
745
753
|
def static_route_exts(self:FastHTML, prefix='/', static_path='.', exts='static'):
|
|
@@ -768,7 +776,7 @@ class FtResponse:
|
|
|
768
776
|
def __init__(self, content, status_code:int=200, headers=None, cls=HTMLResponse, media_type:str|None=None):
|
|
769
777
|
self.content,self.status_code,self.headers = content,status_code,headers
|
|
770
778
|
self.cls,self.media_type = cls,media_type
|
|
771
|
-
|
|
779
|
+
|
|
772
780
|
def __response__(self, req):
|
|
773
781
|
cts,httphdrs,tasks = _xt_cts(req, self.content)
|
|
774
782
|
headers = {**(self.headers or {}), **httphdrs}
|
|
@@ -1,6 +1,6 @@
|
|
|
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', 'htmx_exts', 'htmxsrc', 'fhjsscr', 'surrsrc', 'scopesrc', 'viewport', 'charset', 'cors_allow', 'iframe_scr', 'all_meths', 'parsed_date', 'snake2hyphens', 'HtmxHeaders', 'HttpHeader', 'HtmxResponseHeaders', 'form2dict', 'parse_form', 'flat_xt', 'Beforeware', 'EventStream', 'signal_shutdown', 'uri', 'decode_uri', 'flat_tuple', 'noop_body', 'respond', 'Redirect', 'get_key', 'def_hdrs', 'FastHTML', 'serve', 'Client', 'APIRouter', 'cookie', 'reg_re_param', 'MiddlewareBase', 'FtResponse', 'unqid', 'setup_ws']
|
|
3
|
-
import json, uuid, inspect, types, uvicorn, signal, asyncio, threading
|
|
2
|
+
__all__ = ['empty', 'htmx_hdrs', 'fh_cfg', 'htmx_resps', 'htmx_exts', 'htmxsrc', 'fhjsscr', 'surrsrc', 'scopesrc', 'viewport', 'charset', 'cors_allow', 'iframe_scr', 'all_meths', 'parsed_date', 'snake2hyphens', 'HtmxHeaders', 'HttpHeader', 'HtmxResponseHeaders', 'form2dict', 'parse_form', 'flat_xt', 'Beforeware', 'EventStream', 'signal_shutdown', 'uri', 'decode_uri', 'flat_tuple', 'noop_body', 'respond', 'Redirect', 'get_key', 'qp', 'def_hdrs', 'FastHTML', 'nested_name', 'serve', 'Client', 'RouteFuncs', 'APIRouter', 'cookie', 'reg_re_param', 'MiddlewareBase', 'FtResponse', 'unqid', 'setup_ws']
|
|
3
|
+
import json, uuid, inspect, types, uvicorn, signal, asyncio, threading, inspect
|
|
4
4
|
from fastcore.utils import *
|
|
5
5
|
from fastcore.xml import *
|
|
6
6
|
from fastcore.meta import use_kwargs_dict
|
|
@@ -167,6 +167,7 @@ def _apply_ft(o):
|
|
|
167
167
|
|
|
168
168
|
def _to_xml(req, resp, indent):
|
|
169
169
|
...
|
|
170
|
+
_iter_typs = (tuple, list, map, filter, range, types.GeneratorType)
|
|
170
171
|
|
|
171
172
|
def flat_tuple(o):
|
|
172
173
|
"""Flatten lists"""
|
|
@@ -183,13 +184,13 @@ def respond(req, heads, bdy):
|
|
|
183
184
|
def _xt_cts(req, resp):
|
|
184
185
|
...
|
|
185
186
|
|
|
186
|
-
def _xt_resp(req, resp):
|
|
187
|
+
def _xt_resp(req, resp, status_code):
|
|
187
188
|
...
|
|
188
189
|
|
|
189
190
|
def _is_ft_resp(resp):
|
|
190
191
|
...
|
|
191
192
|
|
|
192
|
-
def _resp(req, resp, cls=empty):
|
|
193
|
+
def _resp(req, resp, cls=empty, status_code=200):
|
|
193
194
|
...
|
|
194
195
|
|
|
195
196
|
class Redirect:
|
|
@@ -203,9 +204,9 @@ class Redirect:
|
|
|
203
204
|
|
|
204
205
|
async def _wrap_call(f, req, params):
|
|
205
206
|
...
|
|
206
|
-
htmx_exts = {'head-support': 'https://unpkg.com/htmx-ext-head-support@2.0.
|
|
207
|
-
htmxsrc = Script(src='https://unpkg.com/htmx.org@
|
|
208
|
-
fhjsscr = Script(src='https://cdn.jsdelivr.net/gh/answerdotai/fasthtml-js@1.0.
|
|
207
|
+
htmx_exts = {'head-support': 'https://unpkg.com/htmx-ext-head-support@2.0.3/head-support.js', 'preload': 'https://unpkg.com/htmx-ext-preload@2.1.0/preload.js', 'class-tools': 'https://unpkg.com/htmx-ext-class-tools@2.0.1/class-tools.js', 'loading-states': 'https://unpkg.com/htmx-ext-loading-states@2.0.0/loading-states.js', 'multi-swap': 'https://unpkg.com/htmx-ext-multi-swap@2.0.0/multi-swap.js', 'path-deps': 'https://unpkg.com/htmx-ext-path-deps@2.0.0/path-deps.js', 'remove-me': 'https://unpkg.com/htmx-ext-remove-me@2.0.0/remove-me.js', 'ws': 'https://unpkg.com/htmx-ext-ws@2.0.2/ws.js', 'chunked-transfer': 'https://unpkg.com/htmx-ext-transfer-encoding-chunked@0.4.0/transfer-encoding-chunked.js'}
|
|
208
|
+
htmxsrc = Script(src='https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js')
|
|
209
|
+
fhjsscr = Script(src='https://cdn.jsdelivr.net/gh/answerdotai/fasthtml-js@1.0.12/fasthtml.js')
|
|
209
210
|
surrsrc = Script(src='https://cdn.jsdelivr.net/gh/answerdotai/surreal@main/surreal.js')
|
|
210
211
|
scopesrc = Script(src='https://cdn.jsdelivr.net/gh/gnat/css-scope-inline@main/script.js')
|
|
211
212
|
viewport = Meta(name='viewport', content='width=device-width, initial-scale=1, viewport-fit=cover')
|
|
@@ -217,10 +218,11 @@ def get_key(key=None, fname='.sesskey'):
|
|
|
217
218
|
def _list(o):
|
|
218
219
|
...
|
|
219
220
|
|
|
220
|
-
def _wrap_ex(f, hdrs, ftrs, htmlkw, bodykw, body_wrap):
|
|
221
|
+
def _wrap_ex(f, status_code, hdrs, ftrs, htmlkw, bodykw, body_wrap):
|
|
221
222
|
...
|
|
222
223
|
|
|
223
|
-
def
|
|
224
|
+
def qp(p: str, **kw) -> str:
|
|
225
|
+
"""Add query parameters to path p"""
|
|
224
226
|
...
|
|
225
227
|
|
|
226
228
|
def def_hdrs(htmx=True, surreal=True):
|
|
@@ -231,7 +233,7 @@ iframe_scr = Script(NotStr("\n function sendmsg() {\n window.parent.po
|
|
|
231
233
|
|
|
232
234
|
class FastHTML(Starlette):
|
|
233
235
|
|
|
234
|
-
def __init__(self, debug=False, routes=None, middleware=None, exception_handlers=None, on_startup=None, on_shutdown=None, lifespan=None, hdrs=None, ftrs=None, exts=None, before=None, after=None, surreal=True, htmx=True, default_hdrs=True, sess_cls=SessionMiddleware, secret_key=None, session_cookie='session_', max_age=365 * 24 * 3600, sess_path='/', same_site='lax', sess_https_only=False, sess_domain=None, key_fname='.sesskey', body_wrap=noop_body, htmlkw=None, nb_hdrs=
|
|
236
|
+
def __init__(self, debug=False, routes=None, middleware=None, title: str='FastHTML page', exception_handlers=None, on_startup=None, on_shutdown=None, lifespan=None, hdrs=None, ftrs=None, exts=None, before=None, after=None, surreal=True, htmx=True, default_hdrs=True, sess_cls=SessionMiddleware, secret_key=None, session_cookie='session_', max_age=365 * 24 * 3600, sess_path='/', same_site='lax', sess_https_only=False, sess_domain=None, key_fname='.sesskey', body_wrap=noop_body, htmlkw=None, nb_hdrs=False, **bodykw):
|
|
235
237
|
...
|
|
236
238
|
|
|
237
239
|
def add_route(self, route):
|
|
@@ -250,7 +252,7 @@ class FastHTML(Starlette):
|
|
|
250
252
|
def _add_route(self, func, path, methods, name, include_in_schema, body_wrap):
|
|
251
253
|
...
|
|
252
254
|
|
|
253
|
-
def route(self, path: str=None, methods=None, name=None, include_in_schema=True, body_wrap=
|
|
255
|
+
def route(self, path: str=None, methods=None, name=None, include_in_schema=True, body_wrap=None):
|
|
254
256
|
"""Add a route at `path`"""
|
|
255
257
|
...
|
|
256
258
|
|
|
@@ -262,6 +264,13 @@ class FastHTML(Starlette):
|
|
|
262
264
|
"""Add a static route at URL path `prefix` with files from `static_path` and single `ext` (including the '.')"""
|
|
263
265
|
...
|
|
264
266
|
all_meths = 'get post put delete patch head trace options'.split()
|
|
267
|
+
|
|
268
|
+
def _mk_locfunc(f, p):
|
|
269
|
+
...
|
|
270
|
+
|
|
271
|
+
def nested_name(f):
|
|
272
|
+
"""Get name of function `f` using '_' to join nested function names"""
|
|
273
|
+
...
|
|
265
274
|
for o in all_meths:
|
|
266
275
|
setattr(FastHTML, o, partialmethod(FastHTML.route, methods=o))
|
|
267
276
|
|
|
@@ -280,21 +289,41 @@ class Client:
|
|
|
280
289
|
for o in ('get', 'post', 'delete', 'put', 'patch', 'options'):
|
|
281
290
|
setattr(Client, o, partialmethod(Client._sync, o))
|
|
282
291
|
|
|
292
|
+
class RouteFuncs:
|
|
293
|
+
|
|
294
|
+
def __init__(self):
|
|
295
|
+
...
|
|
296
|
+
|
|
297
|
+
def __setattr__(self, name, value):
|
|
298
|
+
...
|
|
299
|
+
|
|
300
|
+
def __getattr__(self, name):
|
|
301
|
+
...
|
|
302
|
+
|
|
303
|
+
def __dir__(self):
|
|
304
|
+
...
|
|
305
|
+
|
|
283
306
|
class APIRouter:
|
|
284
307
|
"""Add routes to an app"""
|
|
285
308
|
|
|
286
|
-
def __init__(self):
|
|
309
|
+
def __init__(self, prefix: str | None=None, body_wrap=noop_body):
|
|
287
310
|
...
|
|
288
311
|
|
|
289
|
-
def
|
|
312
|
+
def _wrap_func(self, func, path=None):
|
|
313
|
+
...
|
|
314
|
+
|
|
315
|
+
def __call__(self, path: str=None, methods=None, name=None, include_in_schema=True, body_wrap=None):
|
|
290
316
|
"""Add a route at `path`"""
|
|
291
317
|
...
|
|
292
318
|
|
|
319
|
+
def __getattr__(self, name):
|
|
320
|
+
...
|
|
321
|
+
|
|
293
322
|
def to_app(self, app):
|
|
294
323
|
"""Add routes to `app`"""
|
|
295
324
|
...
|
|
296
325
|
|
|
297
|
-
def ws(self
|
|
326
|
+
def ws(self, path: str, conn=None, disconn=None, name=None, middleware=None):
|
|
298
327
|
"""Add a websocket route at `path`"""
|
|
299
328
|
...
|
|
300
329
|
for o in all_meths:
|
|
@@ -307,7 +336,7 @@ def cookie(key: str, value='', max_age=None, expires=None, path='/', domain=None
|
|
|
307
336
|
def reg_re_param(m, s):
|
|
308
337
|
...
|
|
309
338
|
reg_re_param('path', '.*?')
|
|
310
|
-
reg_re_param('static', 'ico|gif|jpg|jpeg|webm|css|js|woff|png|svg|mp4|webp|ttf|otf|eot|woff2|txt|html|map')
|
|
339
|
+
reg_re_param('static', 'ico|gif|jpg|jpeg|webm|css|js|woff|png|svg|mp4|webp|ttf|otf|eot|woff2|txt|html|map|pdf')
|
|
311
340
|
|
|
312
341
|
class MiddlewareBase:
|
|
313
342
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# %% auto 0
|
|
6
6
|
__all__ = ['nb_serve', 'nb_serve_async', 'is_port_free', 'wait_port_free', 'show', 'render_ft', 'htmx_config_port', 'JupyUvi',
|
|
7
|
-
'HTMX', 'ws_client']
|
|
7
|
+
'JupyUviAsync', 'HTMX', 'ws_client']
|
|
8
8
|
|
|
9
9
|
# %% ../nbs/api/06_jupyter.ipynb
|
|
10
10
|
import asyncio, socket, time, uvicorn
|
|
@@ -92,6 +92,19 @@ class JupyUvi:
|
|
|
92
92
|
self.server.should_exit = True
|
|
93
93
|
wait_port_free(self.port)
|
|
94
94
|
|
|
95
|
+
# %% ../nbs/api/06_jupyter.ipynb
|
|
96
|
+
class JupyUviAsync(JupyUvi):
|
|
97
|
+
"Start and stop an async Jupyter compatible uvicorn server with ASGI `app` on `port` with `log_level`"
|
|
98
|
+
def __init__(self, app, log_level="error", host='0.0.0.0', port=8000, **kwargs):
|
|
99
|
+
super().__init__(app, log_level=log_level, host=host, port=port, start=False, **kwargs)
|
|
100
|
+
|
|
101
|
+
async def start(self):
|
|
102
|
+
self.server = await nb_serve_async(self.app, log_level=self.log_level, host=self.host, port=self.port, **self.kwargs)
|
|
103
|
+
|
|
104
|
+
def stop(self):
|
|
105
|
+
self.server.should_exit = True
|
|
106
|
+
wait_port_free(self.port)
|
|
107
|
+
|
|
95
108
|
# %% ../nbs/api/06_jupyter.ipynb
|
|
96
109
|
def HTMX(path="", app=None, host='localhost', port=8000, height="auto", link=False, iframe=True):
|
|
97
110
|
"An iframe which displays the HTMX application in a notebook."
|
|
@@ -33,11 +33,11 @@ def CheckboxX(checked: bool=False, label=None, value='1', id=None, name=None, *,
|
|
|
33
33
|
"""A Checkbox optionally inside a Label, preceded by a `Hidden` with matching name"""
|
|
34
34
|
...
|
|
35
35
|
|
|
36
|
-
def Script(code: str='', *, id=None, cls=None, title=None, style=None, attrmap=None, valmap=None, ft_cls=None,
|
|
36
|
+
def Script(code: str='', *, id=None, cls=None, title=None, style=None, attrmap=None, valmap=None, ft_cls=None, **kwargs) -> FT:
|
|
37
37
|
"""A Script tag that doesn't escape its code"""
|
|
38
38
|
...
|
|
39
39
|
|
|
40
|
-
def Style(*c, id=None, cls=None, title=None, style=None, attrmap=None, valmap=None, ft_cls=None,
|
|
40
|
+
def Style(*c, id=None, cls=None, title=None, style=None, attrmap=None, valmap=None, ft_cls=None, **kwargs) -> FT:
|
|
41
41
|
"""A Style tag that doesn't escape its code"""
|
|
42
42
|
...
|
|
43
43
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-fasthtml
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.1
|
|
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 and contributors
|
|
@@ -22,7 +22,7 @@ Requires-Dist: oauthlib
|
|
|
22
22
|
Requires-Dist: itsdangerous
|
|
23
23
|
Requires-Dist: uvicorn[standard]>=0.30
|
|
24
24
|
Requires-Dist: httpx
|
|
25
|
-
Requires-Dist: fastlite>=0.
|
|
25
|
+
Requires-Dist: fastlite>=0.1.1
|
|
26
26
|
Requires-Dist: python-multipart
|
|
27
27
|
Requires-Dist: beautifulsoup4
|
|
28
28
|
Provides-Extra: dev
|
|
@@ -185,3 +185,51 @@ Finally, join the FastHTML community to ask questions, share your work,
|
|
|
185
185
|
and learn from others:
|
|
186
186
|
|
|
187
187
|
- [Discord](https://discord.gg/qcXvcxMhdP)
|
|
188
|
+
|
|
189
|
+
## Other languages and related projects
|
|
190
|
+
|
|
191
|
+
If you’re not a Python user, or are keen to try out a new language,
|
|
192
|
+
we’ll list here other projects that have a similar approach to FastHTML.
|
|
193
|
+
(Please reach out if you know of any other projects that you’d like to
|
|
194
|
+
see added.)
|
|
195
|
+
|
|
196
|
+
- [htmgo](https://htmgo.dev/) (Go): “*htmgo is a lightweight pure go way
|
|
197
|
+
to build interactive websites / web applications using go & htmx. By
|
|
198
|
+
combining the speed & simplicity of go + hypermedia attributes (htmx)
|
|
199
|
+
to add interactivity to websites, all conveniently wrapped in pure go,
|
|
200
|
+
you can build simple, fast, interactive websites without touching
|
|
201
|
+
javascript. All compiled to a single deployable binary*”
|
|
202
|
+
|
|
203
|
+
If you’re just interested in functional HTML components, rather than a
|
|
204
|
+
full HTMX server solution, consider:
|
|
205
|
+
|
|
206
|
+
- [fastcore.xml.FT](https://fastcore.fast.ai/xml.html): This is actually
|
|
207
|
+
what FastHTML uses behind the scenes
|
|
208
|
+
- [htpy](https://htpy.dev/): Similar to
|
|
209
|
+
[`fastcore.xml.FT`](https://fastcore.fast.ai/xml.html#ft), but with a
|
|
210
|
+
somewhat different syntax
|
|
211
|
+
- [elm-html](https://package.elm-lang.org/packages/elm/html/latest/):
|
|
212
|
+
Elm’s built-in HTML library with a type-safe functional approach
|
|
213
|
+
- [hiccup](https://github.com/weavejester/hiccup): Popular library for
|
|
214
|
+
representing HTML in Clojure using vectors
|
|
215
|
+
- [hiccl](https://github.com/garlic0x1/hiccl): HTML generation library
|
|
216
|
+
for Common Lisp inspired by Clojure’s Hiccup
|
|
217
|
+
- [Falco.Markup](https://github.com/pimbrouwers/Falco): F# HTML DSL and
|
|
218
|
+
web framework with type-safe HTML generation
|
|
219
|
+
- [Lucid](https://github.com/chrisdone/lucid): Type-safe HTML generation
|
|
220
|
+
for Haskell using monad transformers
|
|
221
|
+
- [dream-html](https://github.com/aantron/dream): Part of the Dream web
|
|
222
|
+
framework for OCaml, provides type-safe HTML templating
|
|
223
|
+
|
|
224
|
+
For other hypermedia application platforms, not based on HTMX, take a
|
|
225
|
+
look at:
|
|
226
|
+
|
|
227
|
+
- [Hotwire/Turbo](https://turbo.hotwired.dev/): Rails-oriented framework
|
|
228
|
+
that similarly uses HTML-over-the-wire
|
|
229
|
+
- [LiveView](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html):
|
|
230
|
+
Phoenix framework’s solution for building interactive web apps with
|
|
231
|
+
minimal JavaScript
|
|
232
|
+
- [Unpoly](https://unpoly.com/): Another HTML-over-the-wire framework
|
|
233
|
+
with progressive enhancement
|
|
234
|
+
- [Livewire](https://laravel-livewire.com/): Laravel’s take on building
|
|
235
|
+
dynamic interfaces with minimal JavaScript
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[DEFAULT]
|
|
2
2
|
repo = fasthtml
|
|
3
3
|
lib_name = fasthtml
|
|
4
|
-
version = 0.
|
|
4
|
+
version = 0.12.1
|
|
5
5
|
min_python = 3.10
|
|
6
6
|
license = apache2
|
|
7
|
-
requirements = fastcore>=1.7.18 python-dateutil starlette>0.33 oauthlib itsdangerous uvicorn[standard]>=0.30 httpx fastlite>=0.
|
|
7
|
+
requirements = fastcore>=1.7.18 python-dateutil starlette>0.33 oauthlib itsdangerous uvicorn[standard]>=0.30 httpx fastlite>=0.1.1 python-multipart beautifulsoup4
|
|
8
8
|
dev_requirements = ipython lxml pysymbol_llm
|
|
9
9
|
black_formatting = False
|
|
10
10
|
conda_user = fastai
|
|
@@ -16,8 +16,8 @@ tst_flags = notest
|
|
|
16
16
|
put_version_in_init = True
|
|
17
17
|
branch = main
|
|
18
18
|
custom_sidebar = False
|
|
19
|
-
doc_host = https://
|
|
20
|
-
doc_baseurl = /
|
|
19
|
+
doc_host = https://docs.fastht.ml
|
|
20
|
+
doc_baseurl = /
|
|
21
21
|
git_url = https://github.com/AnswerDotAI/fasthtml
|
|
22
22
|
title = fasthtml
|
|
23
23
|
audience = Developers
|
|
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.11.0 → python_fasthtml-0.12.1}/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
|