Skip to content

Commit 78250dc

Browse files
committed
fixes #733
1 parent e66b3f0 commit 78250dc

File tree

3 files changed

+215
-75
lines changed

3 files changed

+215
-75
lines changed

fasthtml/_modidx.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
'fasthtml.core._mk_list': ('api/core.html#_mk_list', 'fasthtml/core.py'),
9191
'fasthtml.core._mk_locfunc': ('api/core.html#_mk_locfunc', 'fasthtml/core.py'),
9292
'fasthtml.core._params': ('api/core.html#_params', 'fasthtml/core.py'),
93+
'fasthtml.core._part_resp': ('api/core.html#_part_resp', 'fasthtml/core.py'),
9394
'fasthtml.core._resp': ('api/core.html#_resp', 'fasthtml/core.py'),
9495
'fasthtml.core._send_ws': ('api/core.html#_send_ws', 'fasthtml/core.py'),
9596
'fasthtml.core._to_htmx_header': ('api/core.html#_to_htmx_header', 'fasthtml/core.py'),
@@ -101,14 +102,14 @@
101102
'fasthtml.core._wrap_ws': ('api/core.html#_wrap_ws', 'fasthtml/core.py'),
102103
'fasthtml.core._ws_endp': ('api/core.html#_ws_endp', 'fasthtml/core.py'),
103104
'fasthtml.core._xt_cts': ('api/core.html#_xt_cts', 'fasthtml/core.py'),
104-
'fasthtml.core._xt_resp': ('api/core.html#_xt_resp', 'fasthtml/core.py'),
105105
'fasthtml.core.cookie': ('api/core.html#cookie', 'fasthtml/core.py'),
106106
'fasthtml.core.decode_uri': ('api/core.html#decode_uri', 'fasthtml/core.py'),
107107
'fasthtml.core.def_hdrs': ('api/core.html#def_hdrs', 'fasthtml/core.py'),
108108
'fasthtml.core.flat_tuple': ('api/core.html#flat_tuple', 'fasthtml/core.py'),
109109
'fasthtml.core.flat_xt': ('api/core.html#flat_xt', 'fasthtml/core.py'),
110110
'fasthtml.core.form2dict': ('api/core.html#form2dict', 'fasthtml/core.py'),
111111
'fasthtml.core.get_key': ('api/core.html#get_key', 'fasthtml/core.py'),
112+
'fasthtml.core.is_full_page': ('api/core.html#is_full_page', 'fasthtml/core.py'),
112113
'fasthtml.core.nested_name': ('api/core.html#nested_name', 'fasthtml/core.py'),
113114
'fasthtml.core.noop_body': ('api/core.html#noop_body', 'fasthtml/core.py'),
114115
'fasthtml.core.parse_form': ('api/core.html#parse_form', 'fasthtml/core.py'),

fasthtml/core.py

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
'charset', 'cors_allow', 'iframe_scr', 'all_meths', 'devtools_loc', 'parsed_date', 'snake2hyphens',
88
'HtmxHeaders', 'HttpHeader', 'HtmxResponseHeaders', 'form2dict', 'parse_form', 'JSONResponse', 'flat_xt',
99
'Beforeware', 'EventStream', 'signal_shutdown', 'uri', 'decode_uri', 'flat_tuple', 'noop_body', 'respond',
10-
'Redirect', 'get_key', 'qp', 'def_hdrs', 'FastHTML', 'nested_name', 'serve', 'Client', 'RouteFuncs',
11-
'APIRouter', 'cookie', 'reg_re_param', 'MiddlewareBase', 'FtResponse', 'unqid', 'setup_ws']
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', 'setup_ws']
1212

1313
# %% ../nbs/api/00_core.ipynb
1414
import json,uuid,inspect,types,signal,asyncio,threading,inspect
@@ -389,25 +389,35 @@ def respond(req, heads, bdy):
389389
return Html(Head(*heads, *flat_xt(req.hdrs)), body, **req.htmlkw)
390390

391391
# %% ../nbs/api/00_core.ipynb
392-
def _xt_cts(req, resp):
392+
def is_full_page(req, resp):
393+
is_frag = 'hx-request' in req.headers and 'hx-history-restore-request' not in req.headers
394+
return (resp and not is_frag and not any(getattr(o, 'tag', '')=='html' for o in resp))
395+
396+
# %% ../nbs/api/00_core.ipynb
397+
def _part_resp(req, resp):
393398
resp = flat_tuple(resp)
394399
resp = resp + tuple(getattr(req, 'injects', ()))
395400
http_hdrs,resp = partition(resp, risinstance(HttpHeader))
396-
http_hdrs = {o.k:str(o.v) for o in http_hdrs}
397401
tasks,resp = partition(resp, risinstance(BackgroundTask))
398-
ts = BackgroundTasks()
399-
for t in tasks: ts.tasks.append(t)
402+
kw = {"headers": {"vary": "HX-Request, HX-History-Restore-Request"}}
403+
if http_hdrs: kw['headers'] |= {o.k:str(o.v) for o in http_hdrs}
404+
if tasks:
405+
ts = BackgroundTasks()
406+
for t in tasks: ts.tasks.append(t)
407+
kw['background'] = ts
408+
resp = tuple(resp)
409+
if len(resp)==1: resp = resp[0]
410+
return resp,kw
411+
412+
# %% ../nbs/api/00_core.ipynb
413+
def _xt_cts(req, resp):
400414
hdr_tags = 'title','meta','link','style','base'
415+
resp = tuplify(resp)
401416
heads,bdy = partition(resp, lambda o: getattr(o, 'tag', '') in hdr_tags)
402-
if resp and 'hx-request' not in req.headers and not any(getattr(o, 'tag', '')=='html' for o in resp):
417+
if is_full_page(req, resp):
403418
title = [] if any(getattr(o, 'tag', '')=='title' for o in heads) else [Title(req.app.title)]
404419
resp = respond(req, [*heads, *title], bdy)
405-
return _to_xml(req, resp, indent=fh_cfg.indent), http_hdrs, ts
406-
407-
# %% ../nbs/api/00_core.ipynb
408-
def _xt_resp(req, resp, status_code):
409-
cts,http_hdrs,tasks = _xt_cts(req, resp)
410-
return HTMLResponse(cts, status_code=status_code, headers=http_hdrs, background=tasks)
420+
return _to_xml(req, resp, indent=fh_cfg.indent)
411421

412422
# %% ../nbs/api/00_core.ipynb
413423
def _is_ft_resp(resp): return isinstance(resp, _iter_typs+(HttpHeader,FT)) or hasattr(resp, '__ft__')
@@ -418,15 +428,18 @@ def _resp(req, resp, cls=empty, status_code=200):
418428
if hasattr(resp, '__response__'): resp = resp.__response__(req)
419429
if cls in (Any,FT): cls=empty
420430
if isinstance(resp, FileResponse) and not os.path.exists(resp.path): raise HTTPException(404, resp.path)
421-
if cls is not empty: return cls(resp, status_code=status_code)
422-
if isinstance(resp, Response): return resp # respect manually set status_code
423-
if _is_ft_resp(resp): return _xt_resp(req, resp, status_code)
431+
resp,kw = _part_resp(req, resp)
432+
if cls is not empty: return cls(resp, status_code=status_code, **kw)
433+
if isinstance(resp, Response): return resp
434+
if _is_ft_resp(resp):
435+
cts = _xt_cts(req, resp)
436+
return HTMLResponse(cts, status_code=status_code, **kw)
424437
if isinstance(resp, str): cls = HTMLResponse
425438
elif isinstance(resp, Mapping): cls = JSONResponse
426439
else:
427440
resp = str(resp)
428441
cls = HTMLResponse
429-
return cls(resp, status_code=status_code)
442+
return cls(resp, status_code=status_code, **kw)
430443

431444
# %% ../nbs/api/00_core.ipynb
432445
class Redirect:
@@ -788,8 +801,10 @@ def __init__(self, content, status_code:int=200, headers=None, cls=HTMLResponse,
788801
self.cls,self.media_type,self.background = cls,media_type,background
789802

790803
def __response__(self, req):
791-
cts,httphdrs,tasks = _xt_cts(req, self.content)
792-
if not tasks.tasks: tasks = self.background
804+
resp,kw = _part_resp(req, self.content)
805+
cts = _xt_cts(req, resp)
806+
tasks,httphdrs = kw.get('background'),kw.get('headers')
807+
if not tasks: tasks = self.background
793808
headers = {**(self.headers or {}), **httphdrs}
794809
return self.cls(cts, status_code=self.status_code, headers=headers, media_type=self.media_type, background=tasks)
795810

0 commit comments

Comments
 (0)