Metadata-Version: 2.1
Name: python-fasthtml
Version: 0.1.7
Summary: The fastest way to create an HTML app
Home-page: https://github.com/AnswerDotAI/fasthtml
Author: Jeremy Howard
Author-email: github@jhoward.fastmail.fm
License: Apache Software License 2.0
Keywords: nbdev jupyter notebook python
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Natural Language :: English
Classifier: Programming Language :: Python :: 3.10
Classifier: License :: OSI Approved :: Apache Software License
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastcore >=1.5.46
Requires-Dist: python-dateutil
Requires-Dist: starlette >0.33
Requires-Dist: oauthlib
Requires-Dist: itsdangerous
Requires-Dist: uvicorn[standard] >=0.30
Requires-Dist: httpx
Requires-Dist: fastlite >=0.0.6
Requires-Dist: python-multipart
Requires-Dist: beautifulsoup4
Provides-Extra: dev
Requires-Dist: ipython ; extra == 'dev'
Requires-Dist: lxml ; extra == 'dev'

# fasthtml


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

**Please note**: this repo is public so that we can build in the open,
and interested folks can participate and/or observe. But it’s not
released yet, so please don’t share links to this project on social
media etc – the project is not ready yet for public use and it would be
very distracting to deal with publicity of an unreleased project.

------------------------------------------------------------------------

`fasthtml` is a library for writing fast and scalable Starlette-powered
web applications, without having to learn much (if any!) Starlette.
Instead, you just use plain python functions for each page in your app –
you don’t even need to learn Javascript. The finished app will be about
as fast as a Python web server can be (which is pretty fast –
e.g. Instagram runs on Python), and you can create pretty much anything.
This isn’t one of those stripped-down dashboard making thingies.

This is a way to write real web applications, without the fuss.

To learn how to use it, please visit the
[documentation](https://answerdotai.github.io/fasthtml/).

## Install

``` sh
pip install python-fasthtml
```

For a minimal app, create a file “main.py” as follows:

<div class="code-with-filename">

**main.py**

``` python
from fasthtml.common import *
import uvicorn

app = FastHTML()
rt = app.route

@rt("/")
def get():
  return Title("FastHTML"), H1("Hello World!")

if __name__ == '__main__':
    uvicorn.run(app, host='0.0.0.0', port=int(os.getenv("PORT", default=8000)))
```

</div>

## How to use

Import from `fasthtml.common`:

``` python
from fasthtml.common import *
```

Create your app.

``` python
app = FastHTML()
```

Create your routes. The syntax is largely the same as the wonderful
[FastAPI](https://fastapi.tiangolo.com/) (which is what you should be
using instead of this if you’re creating a JSON service. FastHTML is for
mainly for making HTML web apps, not APIs).

Note that you need to include the types of your parameters, so that
`FastHTML` knows what to pass to your function. Here, we’re just
expecting a string:

``` python
@app.get('/user/{nm}')
def get_nm(nm:str): return f"Good day to you, {nm}!"
```

Normally you’d save this into a file such as main.py, and then run it in
`uvicorn` using:

    uvicorn main:app

However, for testing, we can use Starlette’s `TestClient` to try it out:

``` python
from starlette.testclient import TestClient
```

``` python
client = TestClient(app)
r = client.get('/user/Jeremy')
r
```

    <Response [200 OK]>

TestClient uses `httpx` behind the scenes, so it returns a
`httpx.Response`, which has a `text` attribute with our response body:

``` python
r.text
```

    'Good day to you, Jeremy!'

In the previous example, the function name (`get_nm`) didn’t actually
matter – we could have just called it `_`, for instance, since we never
actually call it directly. It’s just called through HTTP. In fact, we
often do call our functions `_` when using this style of route, since
that’s one less thing we have to worry about naming.

An alternative approach to creating a route is to use `app.route`
instead, in which case you make the function name the HTTP method you
want. Since this is such a common pattern, you might like to give a
shorter name to `app.route` – we normally use `rt`:

``` python
rt = app.route

@rt('/')
def post(): return "Going postal!"

client.post('/').text
```

    'Going postal!'

FastHTML has special handling of tags created using `fastcore.xml`, so
you can return web pages without worrying about Jinja, templates, or any
of that stuff. This also means you can `pip install` styled rich
component libraries, since it’s all just pure python:

``` python
@app.get('/html/{idx}')
async def _(idx:int):
    return Body(
        H4("Wow look here"),
        P(f'It looks like you are visitor {idx}! Next is {idx+1}.')
    )
```

``` python
from IPython import display
```

``` python
display.HTML(client.get('/html/1').text)
```

<body>
  <h4>
Wow look here
  </h4>
  <p>
It looks like you are visitor 1! Next is 2.
  </p>
</body>

## Features

Here’s a brief demo of all the features of the library:

``` python
from starlette.responses import Response
from datetime import datetime
from fastcore.utils import *
from dataclasses import dataclass, asdict
```

``` python
def todict(req): return {k:str(v) for k,v in req.items()}
```

``` python
app = FastHTML()
rt = app.route

@app.get("/")
def _(req): return todict(req.scope)
```

``` python
cli = TestClient(app)
r = cli.get('/')
print(r.text)
```

    {"type":"http","http_version":"1.1","method":"GET","path":"/","raw_path":"b'/'","root_path":"","scheme":"http","query_string":"b''","headers":"[(b'host', b'testserver'), (b'accept', b'*/*'), (b'accept-encoding', b'gzip, deflate, br'), (b'connection', b'keep-alive'), (b'user-agent', b'testclient')]","client":"['testclient', 50000]","server":"['testserver', 80]","extensions":"{'http.response.debug': {}}","state":"{}","app":"<fasthtml.core.FastHTML object>","session":"{}","starlette.exception_handlers":"({<class 'starlette.exceptions.HTTPException'>: <bound method ExceptionMiddleware.http_exception of <starlette.middleware.exceptions.ExceptionMiddleware object>>, <class 'starlette.exceptions.WebSocketException'>: <bound method ExceptionMiddleware.websocket_exception of <starlette.middleware.exceptions.ExceptionMiddleware object>>}, {})","router":"<fasthtml.core.RouterX object>","endpoint":"<function _wrap_ep.<locals>._f>","path_params":"{}"}

``` python
@app.get('/user/{nm}')
def _(nm:str): return f"Good day to you, {nm}!"

cli.get('/user/jph').text
```

    'Good day to you, jph!'

``` python
@rt('/html/{idx}')
async def get(idx:int):
    return Body(
        H4("Wow look here"),
        P(f'It looks like you are visitor {idx}! Next is {idx+1}.')
    )

display.HTML(cli.get('/html/1').text)
```

<body>
  <h4>
Wow look here
  </h4>
  <p>
It looks like you are visitor 1! Next is 2.
  </p>
</body>

``` python
reg_re_param("imgext", "ico|gif|jpg|jpeg|webm")

@app.get(r'/static/{path:path}{fn}.{ext:imgext}')
def get_img(fn:str, path:str, ext:str): return f"Getting {fn}.{ext} from /{path}"

cli.get('/static/foo/jph.ico').text
```

    'Getting jph.ico from /foo/'

``` python
ModelName = str_enum('ModelName', "alexnet", "resnet", "lenet")

@app.get("/models/{nm}")
def model(nm:ModelName): return nm

print(cli.get('/models/alexnet').text)
```

    alexnet

``` python
@app.get("/files/{path}")
async def txt(path: Path): return path.with_suffix('.txt')

print(cli.get('/files/foo').text)
```

    foo.txt

``` python
fake_db = [{"name": "Foo"}, {"name": "Bar"}]

@app.get("/items/")
def read_item(idx:int|None = 0): return fake_db[idx]

print(cli.get('/items/?idx=1').text)
```

    {"name":"Bar"}

``` python
print(cli.get('/items/').text)
```

    {"name":"Foo"}

``` python
@app.get("/booly/")
def booly(coming:bool=True): return 'Coming' if coming else 'Not coming'

print(cli.get('/booly/?coming=true').text)
```

    Coming

``` python
print(cli.get('/booly/?coming=no').text)
```

    Not coming

``` python
@app.get("/datie/")
def datie(d:date): return d

date_str = "17th of May, 2024, 2p"
print(cli.get(f'/datie/?d={date_str}').text)
```

    2024-05-17 14:00:00

``` python
@dataclass
class Bodie:
    a:int;b:str

@rt("/bodie/{nm}")
async def post(nm:str, data:Bodie):
    res = asdict(data)
    res['nm'] = nm
    return res

cli.post('/bodie/me', data=dict(a=1, b='foo')).text
```

    '{"a":1,"b":"foo","nm":"me"}'

``` python
@app.get("/setcookie")
async def setc(req):
    now = datetime.now()
    res = Response(f'Set to {now}')
    res.set_cookie('now', str(now))
    return res

cli.get('/setcookie').text
```

    'Set to 2024-06-01 19:55:03.088788'

``` python
@app.get("/getcookie")
async def getc(now:date): return f'Cookie was set at time {now.time()}'

cli.get('/getcookie').text
```

    'Cookie was set at time 19:55:03.088788'

``` python
@app.get("/ua")
async def ua(user_agent:str): return user_agent

cli.get('/ua', headers={'User-Agent':'FastHTML'}).text
```

    'FastHTML'

``` python
@app.get("/hxtest")
def hxtest(htmx): return htmx.request

cli.get('/hxtest', headers={'HX-Request':'1'}).text
```

    '1'
