python-fasthtml 0.12.22__tar.gz → 0.12.23__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.
Files changed (42) hide show
  1. {python_fasthtml-0.12.22/python_fasthtml.egg-info → python_fasthtml-0.12.23}/PKG-INFO +1 -1
  2. python_fasthtml-0.12.23/fasthtml/__init__.py +2 -0
  3. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/core.py +32 -8
  4. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23/python_fasthtml.egg-info}/PKG-INFO +1 -1
  5. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/settings.ini +1 -1
  6. python_fasthtml-0.12.22/fasthtml/__init__.py +0 -2
  7. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/CONTRIBUTING.md +0 -0
  8. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/LICENSE +0 -0
  9. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/MANIFEST.in +0 -0
  10. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/README.md +0 -0
  11. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/_modidx.py +0 -0
  12. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/authmw.py +0 -0
  13. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/basics.py +0 -0
  14. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/cli.py +0 -0
  15. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/common.py +0 -0
  16. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/components.py +0 -0
  17. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/components.pyi +0 -0
  18. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/core.pyi +0 -0
  19. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/fastapp.py +0 -0
  20. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/ft.py +0 -0
  21. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/js.py +0 -0
  22. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/jupyter.py +0 -0
  23. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/katex.js +0 -0
  24. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/live_reload.py +0 -0
  25. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/oauth.py +0 -0
  26. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/pico.py +0 -0
  27. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/starlette.py +0 -0
  28. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/stripe_otp.py +0 -0
  29. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/svg.py +0 -0
  30. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/toaster.py +0 -0
  31. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/xtend.py +0 -0
  32. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/fasthtml/xtend.pyi +0 -0
  33. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/pyproject.toml +0 -0
  34. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/python_fasthtml.egg-info/SOURCES.txt +0 -0
  35. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/python_fasthtml.egg-info/dependency_links.txt +0 -0
  36. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/python_fasthtml.egg-info/entry_points.txt +0 -0
  37. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/python_fasthtml.egg-info/not-zip-safe +0 -0
  38. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/python_fasthtml.egg-info/requires.txt +0 -0
  39. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/python_fasthtml.egg-info/top_level.txt +0 -0
  40. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/setup.cfg +0 -0
  41. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/setup.py +0 -0
  42. {python_fasthtml-0.12.22 → python_fasthtml-0.12.23}/tests/test_toaster.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-fasthtml
3
- Version: 0.12.22
3
+ Version: 0.12.23
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
@@ -0,0 +1,2 @@
1
+ __version__ = "0.12.23"
2
+ from .core import *
@@ -106,8 +106,7 @@ def _form_arg(k, v, d):
106
106
  class HttpHeader: k:str;v:str
107
107
 
108
108
  # %% ../nbs/api/00_core.ipynb
109
- def _to_htmx_header(s):
110
- return 'HX-' + s.replace('_', '-').title()
109
+ def _to_htmx_header(s): return 'HX-' + s.replace('_', '-').title()
111
110
 
112
111
  htmx_resps = dict(location=None, push_url=None, redirect=None, refresh=None, replace_url=None,
113
112
  reswap=None, retarget=None, reselect=None, trigger=None, trigger_after_settle=None, trigger_after_swap=None)
@@ -126,7 +125,7 @@ def _annotations(anno):
126
125
  return get_annotations(anno)
127
126
 
128
127
  # %% ../nbs/api/00_core.ipynb
129
- def _is_body(anno): return issubclass(anno, (dict,ns)) or _annotations(anno)
128
+ def _is_body(anno): return issubclass(anno, (dict,ns)) or hasattr(anno,'__from_request__') or _annotations(anno)
130
129
 
131
130
  # %% ../nbs/api/00_core.ipynb
132
131
  def _formitem(form, k):
@@ -156,11 +155,13 @@ async def parse_form(req: Request) -> FormData:
156
155
 
157
156
  # %% ../nbs/api/00_core.ipynb
158
157
  async def _from_body(req, p):
158
+ "Parse request form/query data and create an instance of the annotated parameter type"
159
159
  anno = p.annotation
160
- # Get the fields and types of type `anno`, if available
161
- d = _annotations(anno)
162
160
  data = form2dict(await parse_form(req))
163
161
  if req.query_params: data = {**data, **dict(req.query_params)}
162
+ ctor = getattr(anno, '__from_request__', None)
163
+ if ctor: return await ctor(data, req) if asyncio.iscoroutinefunction(ctor) else ctor(data, req)
164
+ d = _annotations(anno)
164
165
  cargs = {k: _form_arg(k, v, d) for k, v in data.items() if not d or k in d}
165
166
  return anno(**cargs)
166
167
 
@@ -306,10 +307,12 @@ def signal_shutdown():
306
307
 
307
308
  # %% ../nbs/api/00_core.ipynb
308
309
  def uri(_arg, **kwargs):
310
+ "Create a URI by URL-encoding `_arg` and appending query parameters from `kwargs`"
309
311
  return f"{quote(_arg)}/{urlencode(kwargs, doseq=True)}"
310
312
 
311
313
  # %% ../nbs/api/00_core.ipynb
312
314
  def decode_uri(s):
315
+ "Decode a URI created by `uri()` back into argument and keyword dict"
313
316
  arg,_,kw = s.partition('/')
314
317
  return unquote(arg), {k:v[0] for k,v in parse_qs(kw).items()}
315
318
 
@@ -336,6 +339,7 @@ def url_path_for(self:HTTPConnection, name: str, **path_params):
336
339
  _verbs = dict(get='hx-get', post='hx-post', put='hx-post', delete='hx-delete', patch='hx-patch', link='href')
337
340
 
338
341
  def _url_for(req, t):
342
+ "Generate URL for route `t` using request `req`"
339
343
  if callable(t): t = t.__routename__
340
344
  kw = {}
341
345
  if t.find('/')>-1 and (t.find('?')<0 or t.find('/')<t.find('?')): t,kw = decode_uri(t)
@@ -343,6 +347,7 @@ def _url_for(req, t):
343
347
  return f"{req.url_path_for(t, **kw)}{m}{q}"
344
348
 
345
349
  def _find_targets(req, resp):
350
+ "Find and convert route targets in response attributes to URLs"
346
351
  if isinstance(resp, tuple):
347
352
  for o in resp: _find_targets(req, o)
348
353
  if isinstance(resp, FT):
@@ -352,12 +357,14 @@ def _find_targets(req, resp):
352
357
  if t: resp.attrs[v] = _url_for(req, t)
353
358
 
354
359
  def _apply_ft(o):
360
+ "Apply FastTag transformation recursively to object `o`"
355
361
  if isinstance(o, tuple): o = tuple(_apply_ft(c) for c in o)
356
362
  if hasattr(o, '__ft__'): o = o.__ft__()
357
363
  if isinstance(o, FT): o.children = tuple(_apply_ft(c) for c in o.children)
358
364
  return o
359
365
 
360
366
  def _to_xml(req, resp, indent):
367
+ "Convert response to XML string with target URL resolution"
361
368
  resp = _apply_ft(resp)
362
369
  _find_targets(req, resp)
363
370
  return to_xml(resp, indent)
@@ -367,7 +374,7 @@ _iter_typs = (tuple,list,map,filter,range,types.GeneratorType)
367
374
 
368
375
  # %% ../nbs/api/00_core.ipynb
369
376
  def flat_tuple(o):
370
- "Flatten lists"
377
+ "Flatten nested iterables into a single tuple"
371
378
  result = []
372
379
  if not isinstance(o,_iter_typs): o=[o]
373
380
  o = list(o)
@@ -392,11 +399,13 @@ def respond(req, heads, bdy):
392
399
 
393
400
  # %% ../nbs/api/00_core.ipynb
394
401
  def is_full_page(req, resp):
402
+ "Check if response should be rendered as full page or fragment"
395
403
  if resp and any(getattr(o, 'tag', '')=='html' for o in resp): return True
396
404
  return 'hx-request' in req.headers and 'hx-history-restore-request' not in req.headers
397
405
 
398
406
  # %% ../nbs/api/00_core.ipynb
399
407
  def _part_resp(req, resp):
408
+ "Partition response into HTTP headers, background tasks, and content"
400
409
  resp = flat_tuple(resp)
401
410
  resp = resp + tuple(getattr(req, 'injects', ()))
402
411
  http_hdrs,resp = partition(resp, risinstance(HttpHeader))
@@ -413,6 +422,7 @@ def _part_resp(req, resp):
413
422
 
414
423
  # %% ../nbs/api/00_core.ipynb
415
424
  def _xt_cts(req, resp):
425
+ "Extract content and headers, render as full page or fragment"
416
426
  hdr_tags = 'title','meta','link','style','base'
417
427
  resp = tuplify(resp)
418
428
  heads,bdy = partition(resp, lambda o: getattr(o, 'tag', '') in hdr_tags)
@@ -423,10 +433,13 @@ def _xt_cts(req, resp):
423
433
  return _to_xml(req, resp, indent=fh_cfg.indent)
424
434
 
425
435
  # %% ../nbs/api/00_core.ipynb
426
- def _is_ft_resp(resp): return isinstance(resp, _iter_typs+(HttpHeader,FT)) or hasattr(resp, '__ft__')
436
+ def _is_ft_resp(resp):
437
+ "Check if response is a FastTag-compatible type"
438
+ return isinstance(resp, _iter_typs+(HttpHeader,FT)) or hasattr(resp, '__ft__')
427
439
 
428
440
  # %% ../nbs/api/00_core.ipynb
429
441
  def _resp(req, resp, cls=empty, status_code=200):
442
+ "Create appropriate HTTP response from request and response data"
430
443
  if not resp: resp=''
431
444
  if hasattr(resp, '__response__'): resp = resp.__response__(req)
432
445
  if cls in (Any,FT): cls=empty
@@ -454,6 +467,7 @@ class Redirect:
454
467
 
455
468
  # %% ../nbs/api/00_core.ipynb
456
469
  async def _wrap_call(f, req, params):
470
+ "Wrap function call with request parameter injection"
457
471
  wreq = await _wrap_req(req, params)
458
472
  return await _handle(f, wreq)
459
473
 
@@ -481,6 +495,7 @@ charset = Meta(charset="utf-8")
481
495
 
482
496
  # %% ../nbs/api/00_core.ipynb
483
497
  def get_key(key=None, fname='.sesskey'):
498
+ "Get session key from `key` param or read/create from file `fname`"
484
499
  if key: return key
485
500
  fname = Path(fname)
486
501
  if fname.exists(): return fname.read_text()
@@ -489,10 +504,13 @@ def get_key(key=None, fname='.sesskey'):
489
504
  return key
490
505
 
491
506
  # %% ../nbs/api/00_core.ipynb
492
- def _list(o): return [] if not o else list(o) if isinstance(o, (tuple,list)) else [o]
507
+ def _list(o):
508
+ "Wrap non-list item in a list, returning empty list if None"
509
+ return [] if not o else list(o) if isinstance(o, (tuple,list)) else [o]
493
510
 
494
511
  # %% ../nbs/api/00_core.ipynb
495
512
  def _wrap_ex(f, status_code, hdrs, ftrs, htmlkw, bodykw, body_wrap):
513
+ "Wrap exception handler with FastHTML request processing"
496
514
  async def _f(req, exc):
497
515
  req.hdrs,req.ftrs,req.htmlkw,req.bodykw = map(deepcopy, (hdrs, ftrs, htmlkw, bodykw))
498
516
  req.body_wrap = body_wrap
@@ -573,6 +591,7 @@ class FastHTML(Starlette):
573
591
  # %% ../nbs/api/00_core.ipynb
574
592
  @patch
575
593
  def add_route(self:FastHTML, route):
594
+ "Add or replace a route in the FastHTML app"
576
595
  route.methods = [m.upper() for m in listify(route.methods)]
577
596
  self.router.routes = [r for r in self.router.routes if not
578
597
  (r.path==route.path and r.name == route.name and
@@ -585,6 +604,7 @@ all_meths = 'get post put delete patch head trace options'.split()
585
604
  # %% ../nbs/api/00_core.ipynb
586
605
  @patch
587
606
  def _endp(self:FastHTML, f, body_wrap):
607
+ "Create endpoint wrapper with before/after middleware processing"
588
608
  sig = signature_ex(f, True)
589
609
  async def _f(req):
590
610
  resp = None
@@ -609,6 +629,7 @@ def _endp(self:FastHTML, f, body_wrap):
609
629
  # %% ../nbs/api/00_core.ipynb
610
630
  @patch
611
631
  def _add_ws(self:FastHTML, func, path, conn, disconn, name, middleware):
632
+ "Add websocket route to FastHTML app"
612
633
  endp = _ws_endp(func, conn, disconn)
613
634
  route = WebSocketRoute(path, endpoint=endp, name=name, middleware=middleware)
614
635
  route.methods = ['ws']
@@ -624,6 +645,7 @@ def ws(self:FastHTML, path:str, conn=None, disconn=None, name=None, middleware=N
624
645
 
625
646
  # %% ../nbs/api/00_core.ipynb
626
647
  def _mk_locfunc(f,p):
648
+ "Create a location function wrapper with route path and to() method"
627
649
  class _lf:
628
650
  def __init__(self): update_wrapper(self, f)
629
651
  def __call__(self, *args, **kw): return f(*args, **kw)
@@ -639,6 +661,7 @@ def nested_name(f):
639
661
  # %% ../nbs/api/00_core.ipynb
640
662
  @patch
641
663
  def _add_route(self:FastHTML, func, path, methods, name, include_in_schema, body_wrap):
664
+ "Add HTTP route to FastHTML app with automatic method detection"
642
665
  n,fn,p = name,nested_name(func),None if callable(path) else path
643
666
  if methods: m = [methods] if isinstance(methods,str) else methods
644
667
  elif fn in all_meths and p is not None: m = [fn]
@@ -663,6 +686,7 @@ for o in all_meths: setattr(FastHTML, o, partialmethod(FastHTML.route, methods=o
663
686
  # %% ../nbs/api/00_core.ipynb
664
687
  @patch
665
688
  def set_lifespan(self:FastHTML, value):
689
+ "Set the lifespan context manager for the FastHTML app"
666
690
  if inspect.isasyncgenfunction(value): value = contextlib.asynccontextmanager(value)
667
691
  self.router.lifespan_context = value
668
692
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-fasthtml
3
- Version: 0.12.22
3
+ Version: 0.12.23
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
@@ -1,7 +1,7 @@
1
1
  [DEFAULT]
2
2
  repo = fasthtml
3
3
  lib_name = python-fasthtml
4
- version = 0.12.22
4
+ version = 0.12.23
5
5
  min_python = 3.10
6
6
  license = apache2
7
7
  requirements = fastcore>=1.8.1 python-dateutil starlette>0.33 oauthlib itsdangerous uvicorn[standard]>=0.30 httpx fastlite>=0.1.1 python-multipart beautifulsoup4
@@ -1,2 +0,0 @@
1
- __version__ = "0.12.22"
2
- from .core import *