python-fasthtml 0.12.28__tar.gz → 0.12.30__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.28/python_fasthtml.egg-info → python_fasthtml-0.12.30}/PKG-INFO +1 -1
  2. python_fasthtml-0.12.30/fasthtml/__init__.py +2 -0
  3. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/_modidx.py +12 -0
  4. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/core.py +9 -1
  5. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/oauth.py +3 -1
  6. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/xtend.py +92 -10
  7. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30/python_fasthtml.egg-info}/PKG-INFO +1 -1
  8. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/settings.ini +1 -1
  9. python_fasthtml-0.12.28/fasthtml/__init__.py +0 -2
  10. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/CONTRIBUTING.md +0 -0
  11. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/LICENSE +0 -0
  12. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/MANIFEST.in +0 -0
  13. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/README.md +0 -0
  14. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/authmw.py +0 -0
  15. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/basics.py +0 -0
  16. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/cli.py +0 -0
  17. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/common.py +0 -0
  18. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/components.py +0 -0
  19. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/components.pyi +0 -0
  20. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/core.pyi +0 -0
  21. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/fastapp.py +0 -0
  22. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/ft.py +0 -0
  23. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/js.py +0 -0
  24. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/jupyter.py +0 -0
  25. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/katex.js +0 -0
  26. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/live_reload.py +0 -0
  27. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/pico.py +0 -0
  28. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/starlette.py +0 -0
  29. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/stripe_otp.py +0 -0
  30. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/svg.py +0 -0
  31. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/toaster.py +0 -0
  32. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/fasthtml/xtend.pyi +0 -0
  33. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/pyproject.toml +0 -0
  34. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/python_fasthtml.egg-info/SOURCES.txt +0 -0
  35. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/python_fasthtml.egg-info/dependency_links.txt +0 -0
  36. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/python_fasthtml.egg-info/entry_points.txt +0 -0
  37. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/python_fasthtml.egg-info/not-zip-safe +0 -0
  38. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/python_fasthtml.egg-info/requires.txt +0 -0
  39. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/python_fasthtml.egg-info/top_level.txt +0 -0
  40. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/setup.cfg +0 -0
  41. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/setup.py +0 -0
  42. {python_fasthtml-0.12.28 → python_fasthtml-0.12.30}/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.28
3
+ Version: 0.12.30
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.30"
2
+ from .core import *
@@ -73,6 +73,9 @@ d = { 'settings': { 'branch': 'main',
73
73
  'fasthtml.core.RouteFuncs.__getattr__': ('api/core.html#routefuncs.__getattr__', 'fasthtml/core.py'),
74
74
  'fasthtml.core.RouteFuncs.__init__': ('api/core.html#routefuncs.__init__', 'fasthtml/core.py'),
75
75
  'fasthtml.core.RouteFuncs.__setattr__': ('api/core.html#routefuncs.__setattr__', 'fasthtml/core.py'),
76
+ 'fasthtml.core.StaticNoCache': ('api/core.html#staticnocache', 'fasthtml/core.py'),
77
+ 'fasthtml.core.StaticNoCache.file_response': ( 'api/core.html#staticnocache.file_response',
78
+ 'fasthtml/core.py'),
76
79
  'fasthtml.core.StringConvertor.to_string': ('api/core.html#stringconvertor.to_string', 'fasthtml/core.py'),
77
80
  'fasthtml.core._add_ids': ('api/core.html#_add_ids', 'fasthtml/core.py'),
78
81
  'fasthtml.core._annotations': ('api/core.html#_annotations', 'fasthtml/core.py'),
@@ -260,6 +263,12 @@ d = { 'settings': { 'branch': 'main',
260
263
  'fasthtml.xtend.Fragment.__init__': ('api/xtend.html#fragment.__init__', 'fasthtml/xtend.py'),
261
264
  'fasthtml.xtend.Hidden': ('api/xtend.html#hidden', 'fasthtml/xtend.py'),
262
265
  'fasthtml.xtend.HtmxOn': ('api/xtend.html#htmxon', 'fasthtml/xtend.py'),
266
+ 'fasthtml.xtend.LdContactPoint': ('api/xtend.html#ldcontactpoint', 'fasthtml/xtend.py'),
267
+ 'fasthtml.xtend.LdCourse': ('api/xtend.html#ldcourse', 'fasthtml/xtend.py'),
268
+ 'fasthtml.xtend.LdCourseInstance': ('api/xtend.html#ldcourseinstance', 'fasthtml/xtend.py'),
269
+ 'fasthtml.xtend.LdJson': ('api/xtend.html#ldjson', 'fasthtml/xtend.py'),
270
+ 'fasthtml.xtend.LdOrg': ('api/xtend.html#ldorg', 'fasthtml/xtend.py'),
271
+ 'fasthtml.xtend.LdWebsite': ('api/xtend.html#ldwebsite', 'fasthtml/xtend.py'),
263
272
  'fasthtml.xtend.Nbsp': ('api/xtend.html#nbsp', 'fasthtml/xtend.py'),
264
273
  'fasthtml.xtend.Now': ('api/xtend.html#now', 'fasthtml/xtend.py'),
265
274
  'fasthtml.xtend.On': ('api/xtend.html#on', 'fasthtml/xtend.py'),
@@ -277,6 +286,9 @@ d = { 'settings': { 'branch': 'main',
277
286
  'fasthtml.xtend.jsd': ('api/xtend.html#jsd', 'fasthtml/xtend.py'),
278
287
  'fasthtml.xtend.loose_format': ('api/xtend.html#loose_format', 'fasthtml/xtend.py'),
279
288
  'fasthtml.xtend.replace_css_vars': ('api/xtend.html#replace_css_vars', 'fasthtml/xtend.py'),
289
+ 'fasthtml.xtend.robots_txt': ('api/xtend.html#robots_txt', 'fasthtml/xtend.py'),
280
290
  'fasthtml.xtend.run_js': ('api/xtend.html#run_js', 'fasthtml/xtend.py'),
291
+ 'fasthtml.xtend.sitemap_url': ('api/xtend.html#sitemap_url', 'fasthtml/xtend.py'),
292
+ 'fasthtml.xtend.sitemap_xml': ('api/xtend.html#sitemap_xml', 'fasthtml/xtend.py'),
281
293
  'fasthtml.xtend.undouble_braces': ('api/xtend.html#undouble_braces', 'fasthtml/xtend.py'),
282
294
  'fasthtml.xtend.with_sid': ('api/xtend.html#with_sid', 'fasthtml/xtend.py')}}}
@@ -8,7 +8,8 @@ __all__ = ['empty', 'htmx_hdrs', 'fh_cfg', 'htmx_resps', 'htmx_exts', 'htmxsrc',
8
8
  'HtmxHeaders', 'HttpHeader', 'HtmxResponseHeaders', 'form2dict', 'parse_form', 'JSONResponse', 'flat_xt',
9
9
  'Beforeware', 'EventStream', 'signal_shutdown', 'uri', 'decode_uri', 'flat_tuple', 'noop_body', 'respond',
10
10
  'is_full_page', 'Redirect', 'get_key', 'qp', 'def_hdrs', 'FastHTML', 'nested_name', 'serve', 'Client',
11
- 'RouteFuncs', 'APIRouter', 'cookie', 'reg_re_param', 'MiddlewareBase', 'FtResponse', 'unqid']
11
+ 'RouteFuncs', 'APIRouter', 'cookie', 'reg_re_param', 'StaticNoCache', 'MiddlewareBase', 'FtResponse',
12
+ 'unqid']
12
13
 
13
14
  # %% ../nbs/api/00_core.ipynb
14
15
  import json,uuid,inspect,types,signal,asyncio,threading,inspect,random,contextlib
@@ -820,6 +821,13 @@ def static_route(self:FastHTML, ext='', prefix='/', static_path='.'):
820
821
  @self.route(f"{prefix}{{fname:path}}{ext}")
821
822
  async def get(fname:str): return FileResponse(f'{static_path}/{fname}{ext}')
822
823
 
824
+ # %% ../nbs/api/00_core.ipynb
825
+ class StaticNoCache(StaticFiles):
826
+ def file_response(self, *args, **kwargs):
827
+ resp = super().file_response(*args, **kwargs)
828
+ resp.headers.setdefault("Cache-Control", "no-cache")
829
+ return resp
830
+
823
831
  # %% ../nbs/api/00_core.ipynb
824
832
  class MiddlewareBase:
825
833
  async def __call__(self, scope, receive, send) -> None:
@@ -184,7 +184,9 @@ class OAuth:
184
184
 
185
185
  @app.get(redir_path)
186
186
  def redirect(req, session, code:str=None, error:str=None, state:str=None):
187
- if not code: session['oauth_error']=error; return RedirectResponse(self.error_path, status_code=303)
187
+ if not code:
188
+ session['oauth_error']=error
189
+ return RedirectResponse(self.error_path, status_code=303)
188
190
  scheme = 'http' if url_match(req,self.http_patterns) or not self.https else 'https'
189
191
  base_url = f"{scheme}://{get_host(req)}"
190
192
  info = AttrDictDefault(cli.retr_info(code, base_url+redir_path))
@@ -5,7 +5,9 @@
5
5
  # %% auto 0
6
6
  __all__ = ['sid_scr', 'A', 'AX', 'Form', 'Hidden', 'CheckboxX', 'Script', 'Style', 'double_braces', 'undouble_braces',
7
7
  'loose_format', 'ScriptX', 'replace_css_vars', 'StyleX', 'Nbsp', 'Surreal', 'On', 'Prev', 'Now', 'AnyNow',
8
- 'run_js', 'HtmxOn', 'jsd', 'Fragment', 'Titled', 'Socials', 'YouTubeEmbed', 'Favicon', 'clear', 'with_sid']
8
+ 'run_js', 'HtmxOn', 'jsd', 'Fragment', 'Titled', 'Socials', 'YouTubeEmbed', 'Favicon', 'clear', 'with_sid',
9
+ 'LdJson', 'LdContactPoint', 'LdOrg', 'LdWebsite', 'LdCourseInstance', 'LdCourse', 'robots_txt',
10
+ 'sitemap_url', 'sitemap_xml']
9
11
 
10
12
  # %% ../nbs/api/02_xtend.ipynb
11
13
  from dataclasses import dataclass, asdict
@@ -224,18 +226,13 @@ def YouTubeEmbed(video_id:str, *, width:int=560, height:int=315, start_time:int=
224
226
  print(f"https://www.youtube.com/embed/{video_id}{query_string}")
225
227
  return Div(
226
228
  Iframe(
227
- width=width,
228
- height=height,
229
+ width=width, height=height,
229
230
  src=f"https://www.youtube.com/embed/{video_id}{query_string}",
230
- title=title,
231
- frameborder="0",
231
+ title=title, frameborder="0",
232
232
  allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share",
233
- referrerpolicy="strict-origin-when-cross-origin",
234
- allowfullscreen='',
233
+ referrerpolicy="strict-origin-when-cross-origin", allowfullscreen='',
235
234
  **kwargs
236
- ),
237
- cls=cls
238
- )
235
+ ), cls=cls)
239
236
 
240
237
  # %% ../nbs/api/02_xtend.ipynb
241
238
  def Favicon(light_icon, dark_icon):
@@ -268,3 +265,88 @@ htmx.on("htmx:configRequest", (e) => {
268
265
  def with_sid(app, dest, path='/'):
269
266
  @app.route(path)
270
267
  def get(): return Div(hx_get=dest, hx_trigger=f'load delay:0.001s', hx_swap='outerHTML')
268
+
269
+ # %% ../nbs/api/02_xtend.ipynb
270
+ def LdJson(typ, data:dict, script=False, extra=None, **kwargs)->FT:
271
+ "A script tag containing JSON-LD structured data"
272
+ cts = {'@type':typ, "@context": "https://schema.org"} | data | (extra or {})
273
+ if not script: return cts
274
+ return Script(dumps(cts, indent=1), type="application/ld+json", **kwargs)
275
+
276
+ # %% ../nbs/api/02_xtend.ipynb
277
+ def LdContactPoint(contact_type:str, email:str=None, phone:str=None, script=False, **extra)->dict:
278
+ "Create a ContactPoint for JSON-LD"
279
+ data = {"contactType": contact_type}
280
+ if email: data["email"] = email
281
+ if phone: data["telephone"] = phone
282
+ return LdJson("ContactPoint", data, extra=extra, script=script)
283
+
284
+ # %% ../nbs/api/02_xtend.ipynb
285
+ def LdOrg(name:str, url:str=None, logo:str=None, alt_name:str=None,
286
+ same_as:list=None, contact_points:list=None, script=False, **extra)->dict:
287
+ "JSON-LD Organization structured data"
288
+ data = {"name": name}
289
+ if alt_name: data["alternateName"] = alt_name
290
+ if url: data["url"] = url
291
+ if logo: data["logo"] = logo
292
+ if same_as: data["sameAs"] = same_as
293
+ if contact_points: data["contactPoint"] = contact_points
294
+ return LdJson("Organization", data, extra=extra, script=script)
295
+
296
+ # %% ../nbs/api/02_xtend.ipynb
297
+ def LdWebsite(name:str, url:str, script=False, **extra)->dict:
298
+ "Create JSON-LD WebSite structured data"
299
+ data = {"name": name, "url": url}
300
+ return LdJson("WebSite", data, extra=extra, script=script)
301
+
302
+ # %% ../nbs/api/02_xtend.ipynb
303
+ def LdCourseInstance(course_mode:str="Online", start_date:str=None, end_date:str=None,
304
+ location:dict=None, instructor:dict=None, script=False, **extra)->dict:
305
+ "Create a CourseInstance for JSON-LD"
306
+ data = {"courseMode": course_mode}
307
+ if start_date: data["startDate"] = start_date
308
+ if end_date: data["endDate"] = end_date
309
+ if location: data["location"] = location
310
+ if instructor: data["instructor"] = instructor
311
+ return LdJson("CourseInstance", data, extra=extra, script=script)
312
+
313
+ # %% ../nbs/api/02_xtend.ipynb
314
+ def LdCourse(name:str, description:str, provider:dict, course_instance:dict=None, script=False, **extra)->dict:
315
+ "Create JSON-LD Course structured data"
316
+ data = { "name": name, "description": description, "provider": provider }
317
+ if course_instance: data["hasCourseInstance"] = course_instance
318
+ return LdJson("Course", data, extra=extra, script=script)
319
+
320
+ # %% ../nbs/api/02_xtend.ipynb
321
+ def robots_txt(app, allow_all=True, disallow_paths=None, sitemap_url=None, crawl_delay=None):
322
+ "Add a /robots.txt route to the app"
323
+ @app.route("/robots.txt")
324
+ def get():
325
+ lines = ["User-agent: *"]
326
+ if allow_all and not disallow_paths: lines.append("Allow: /")
327
+ elif disallow_paths: lines.extend(f"Disallow: {path}" for path in disallow_paths)
328
+ else: lines.append("Disallow: /")
329
+ if crawl_delay: lines.append(f"Crawl-delay: {crawl_delay}")
330
+ if sitemap_url: lines.append(f"Sitemap: {sitemap_url}")
331
+ return "\n".join(lines)
332
+
333
+ # %% ../nbs/api/02_xtend.ipynb
334
+ from fastcore.xml import Url,Loc,Lastmod,Changefreq,Priority,Urlset
335
+
336
+ # %% ../nbs/api/02_xtend.ipynb
337
+ def sitemap_url(url_info, loc_base=""):
338
+ "Create a sitemap URL element from url_info (string or dict)"
339
+ if isinstance(url_info, str): return Url(Loc(loc_base + url_info))
340
+ loc = loc_base + url_info['loc']
341
+ url_elem = [Loc(loc)]
342
+ if 'lastmod' in url_info: url_elem.append(Lastmod(url_info['lastmod']))
343
+ if 'changefreq' in url_info: url_elem.append(Changefreq(url_info['changefreq']))
344
+ if 'priority' in url_info: url_elem.append(Priority(str(url_info['priority'])))
345
+ return Url(*url_elem)
346
+
347
+ def sitemap_xml(app, urls, loc_base=""):
348
+ "Add a /sitemap.xml route to the app with list of URLs"
349
+ @app.route("/sitemap.xml")
350
+ def get():
351
+ urlset = [sitemap_url(url_info, loc_base) for url_info in urls]
352
+ return Urlset(*urlset, xmlns="http://www.sitemaps.org/schemas/sitemap/0.9")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-fasthtml
3
- Version: 0.12.28
3
+ Version: 0.12.30
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.28
4
+ version = 0.12.30
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.28"
2
- from .core import *