Type | Default | Details | |
---|---|---|---|
appname | NoneType | None | Name of the module |
app | str | app | App instance to be served |
host | str | 0.0.0.0 | If host is 0.0.0.0 will convert to localhost |
port | NoneType | None | If port is None it will default to 5001 or the PORT environment variable |
reload | bool | True | Default is to reload the app upon code changes |
reload_includes | list[str] | str | None | None | Additional files to watch for changes |
reload_excludes | list[str] | str | None | None | Files to ignore for changes |
there
' in txt ``` ``` python @rt('/oops') def get(nope): return nope test_warns(lambda: cli.get('/oops?nope=1')) ``` ``` python def test_r(cli, path, exp, meth='get', hx=False, **kwargs): if hx: kwargs['headers'] = {'hx-request':"1"} test_eq(getattr(cli, meth)(path, **kwargs).text, exp) ModelName = str_enum('ModelName', "alexnet", "resnet", "lenet") fake_db = [{"name": "Foo"}, {"name": "Bar"}] ``` ``` python @rt('/html/{idx}') async def get(idx:int): return Body(H4(f'Next is {idx+1}.')) ``` ``` python @rt("/models/{nm}") def get(nm:ModelName): return nm @rt("/files/{path}") async def get(path: Path): return path.with_suffix('.txt') @rt("/items/") def get(idx:int|None = 0): return fake_db[idx] @rt("/idxl/") def get(idx:list[int]): return str(idx) ``` ``` python r = cli.get('/html/1', headers={'hx-request':"1"}) assert '15, Lorem
" in response ``` ``` python # Testing POST with Content-Type: application/json @app.post("/bodytext") def index(body): return body response = cli.post('/bodytext', headers={"Content-Type": "application/json"}, data=s).text test_eq(response, '{"b": "Lorem", "a": 15}') ``` ``` python files = [ ('files', ('file1.txt', b'content1')), ('files', ('file2.txt', b'content2')) ] ``` ``` python @rt("/uploads") async def post(files:list[UploadFile]): return ','.join([(await file.read()).decode() for file in files]) res = cli.post('/uploads', files=files) print(res.status_code) print(res.text) ``` 200 content1,content2 ``` python res = cli.post('/uploads', files=[files[0]]) print(res.status_code) print(res.text) ``` 200 content1 ``` python @rt("/setsess") def get(sess, foo:str=''): now = datetime.now() sess['auth'] = str(now) return f'Set to {now}' @rt("/getsess") def get(sess): return f'Session time: {sess["auth"]}' print(cli.get('/setsess').text) time.sleep(0.01) cli.get('/getsess').text ``` Set to 2025-05-29 08:31:48.235262 'Session time: 2025-05-29 08:31:48.235262' ``` python @rt("/sess-first") def post(sess, name: str): sess["name"] = name return str(sess) cli.post('/sess-first', data={'name': 2}) @rt("/getsess-all") def get(sess): return sess['name'] test_eq(cli.get('/getsess-all').text, '2') ``` ``` python @rt("/upload") async def post(uf:UploadFile): return (await uf.read()).decode() with open('../../CHANGELOG.md', 'rb') as f: print(cli.post('/upload', files={'uf':f}, data={'msg':'Hello'}).text[:15]) ``` # Release notes ``` python @rt("/form-submit/{list_id}") def options(list_id: str): headers = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'POST', 'Access-Control-Allow-Headers': '*', } return Response(status_code=200, headers=headers) ``` ``` python h = cli.options('/form-submit/2').headers test_eq(h['Access-Control-Allow-Methods'], 'POST') ``` ``` python from fasthtml.authmw import user_pwd_auth ``` ``` python def _not_found(req, exc): return Div('nope') app,cli,rt = get_cli(FastHTML(exception_handlers={404:_not_found})) txt = cli.get('/').text assert '