python-fasthtml


Namepython-fasthtml JSON
Version 0.0.16 PyPI version JSON
download
home_pagehttps://github.com/AnswerDotAI/fasthtml
SummaryThe fastest way to create an HTML app
upload_time2024-06-16 23:35:31
maintainerNone
docs_urlNone
authorJeremy Howard
requires_python>=3.10
licenseApache Software License 2.0
keywords nbdev jupyter notebook python
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # 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'

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/AnswerDotAI/fasthtml",
    "name": "python-fasthtml",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": "nbdev jupyter notebook python",
    "author": "Jeremy Howard",
    "author_email": "github@jhoward.fastmail.fm",
    "download_url": "https://files.pythonhosted.org/packages/ec/e0/7c1fd8a6199cbb93c94a15f7e24e2b1737c9d623cc982d7d12dfd59480e2/python-fasthtml-0.0.16.tar.gz",
    "platform": null,
    "description": "# fasthtml\n\n\n<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->\n\n**Please note**: this repo is public so that we can build in the open,\nand interested folks can participate and/or observe. But it\u2019s not\nreleased yet, so please don\u2019t share links to this project on social\nmedia etc \u2013 the project is not ready yet for public use and it would be\nvery distracting to deal with publicity of an unreleased project.\n\n------------------------------------------------------------------------\n\n`fasthtml` is a library for writing fast and scalable Starlette-powered\nweb applications, without having to learn much (if any!) Starlette.\nInstead, you just use plain python functions for each page in your app \u2013\nyou don\u2019t even need to learn Javascript. The finished app will be about\nas fast as a Python web server can be (which is pretty fast \u2013\ne.g.\u00a0Instagram runs on Python), and you can create pretty much anything.\nThis isn\u2019t one of those stripped-down dashboard making thingies.\n\nThis is a way to write real web applications, without the fuss.\n\nTo learn how to use it, please visit the\n[documentation](https://answerdotai.github.io/fasthtml/).\n\n## Install\n\n``` sh\npip install python-fasthtml\n```\n\nFor a minimal app, create a file \u201cmain.py\u201d as follows:\n\n<div class=\"code-with-filename\">\n\n**main.py**\n\n``` python\nfrom fasthtml.common import *\nimport uvicorn\n\napp = FastHTML()\nrt = app.route\n\n@rt(\"/\")\ndef get():\n  return Title(\"FastHTML\"), H1(\"Hello World!\")\n\nif __name__ == '__main__':\n    uvicorn.run(app, host='0.0.0.0', port=int(os.getenv(\"PORT\", default=8000)))\n```\n\n</div>\n\n## How to use\n\nImport from `fasthtml.common`:\n\n``` python\nfrom fasthtml.common import *\n```\n\nCreate your app.\n\n``` python\napp = FastHTML()\n```\n\nCreate your routes. The syntax is largely the same as the wonderful\n[FastAPI](https://fastapi.tiangolo.com/) (which is what you should be\nusing instead of this if you\u2019re creating a JSON service. FastHTML is for\nmainly for making HTML web apps, not APIs).\n\nNote that you need to include the types of your parameters, so that\n`FastHTML` knows what to pass to your function. Here, we\u2019re just\nexpecting a string:\n\n``` python\n@app.get('/user/{nm}')\ndef get_nm(nm:str): return f\"Good day to you, {nm}!\"\n```\n\nNormally you\u2019d save this into a file such as main.py, and then run it in\n`uvicorn` using:\n\n    uvicorn main:app\n\nHowever, for testing, we can use Starlette\u2019s `TestClient` to try it out:\n\n``` python\nfrom starlette.testclient import TestClient\n```\n\n``` python\nclient = TestClient(app)\nr = client.get('/user/Jeremy')\nr\n```\n\n    <Response [200 OK]>\n\nTestClient uses `httpx` behind the scenes, so it returns a\n`httpx.Response`, which has a `text` attribute with our response body:\n\n``` python\nr.text\n```\n\n    'Good day to you, Jeremy!'\n\nIn the previous example, the function name (`get_nm`) didn\u2019t actually\nmatter \u2013 we could have just called it `_`, for instance, since we never\nactually call it directly. It\u2019s just called through HTTP. In fact, we\noften do call our functions `_` when using this style of route, since\nthat\u2019s one less thing we have to worry about naming.\n\nAn alternative approach to creating a route is to use `app.route`\ninstead, in which case you make the function name the HTTP method you\nwant. Since this is such a common pattern, you might like to give a\nshorter name to `app.route` \u2013 we normally use `rt`:\n\n``` python\nrt = app.route\n\n@rt('/')\ndef post(): return \"Going postal!\"\n\nclient.post('/').text\n```\n\n    'Going postal!'\n\nFastHTML has special handling of tags created using `fastcore.xml`, so\nyou can return web pages without worrying about Jinja, templates, or any\nof that stuff. This also means you can `pip install` styled rich\ncomponent libraries, since it\u2019s all just pure python:\n\n``` python\n@app.get('/html/{idx}')\nasync def _(idx:int):\n    return Body(\n        H4(\"Wow look here\"),\n        P(f'It looks like you are visitor {idx}! Next is {idx+1}.')\n    )\n```\n\n``` python\nfrom IPython import display\n```\n\n``` python\ndisplay.HTML(client.get('/html/1').text)\n```\n\n<body>\n  <h4>\nWow look here\n  </h4>\n  <p>\nIt looks like you are visitor 1! Next is 2.\n  </p>\n</body>\n\n## Features\n\nHere\u2019s a brief demo of all the features of the library:\n\n``` python\nfrom starlette.responses import Response\nfrom datetime import datetime\nfrom fastcore.utils import *\nfrom dataclasses import dataclass, asdict\n```\n\n``` python\ndef todict(req): return {k:str(v) for k,v in req.items()}\n```\n\n``` python\napp = FastHTML()\nrt = app.route\n\n@app.get(\"/\")\ndef _(req): return todict(req.scope)\n```\n\n``` python\ncli = TestClient(app)\nr = cli.get('/')\nprint(r.text)\n```\n\n    {\"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\":\"{}\"}\n\n``` python\n@app.get('/user/{nm}')\ndef _(nm:str): return f\"Good day to you, {nm}!\"\n\ncli.get('/user/jph').text\n```\n\n    'Good day to you, jph!'\n\n``` python\n@rt('/html/{idx}')\nasync def get(idx:int):\n    return Body(\n        H4(\"Wow look here\"),\n        P(f'It looks like you are visitor {idx}! Next is {idx+1}.')\n    )\n\ndisplay.HTML(cli.get('/html/1').text)\n```\n\n<body>\n  <h4>\nWow look here\n  </h4>\n  <p>\nIt looks like you are visitor 1! Next is 2.\n  </p>\n</body>\n\n``` python\nreg_re_param(\"imgext\", \"ico|gif|jpg|jpeg|webm\")\n\n@app.get(r'/static/{path:path}{fn}.{ext:imgext}')\ndef get_img(fn:str, path:str, ext:str): return f\"Getting {fn}.{ext} from /{path}\"\n\ncli.get('/static/foo/jph.ico').text\n```\n\n    'Getting jph.ico from /foo/'\n\n``` python\nModelName = str_enum('ModelName', \"alexnet\", \"resnet\", \"lenet\")\n\n@app.get(\"/models/{nm}\")\ndef model(nm:ModelName): return nm\n\nprint(cli.get('/models/alexnet').text)\n```\n\n    alexnet\n\n``` python\n@app.get(\"/files/{path}\")\nasync def txt(path: Path): return path.with_suffix('.txt')\n\nprint(cli.get('/files/foo').text)\n```\n\n    foo.txt\n\n``` python\nfake_db = [{\"name\": \"Foo\"}, {\"name\": \"Bar\"}]\n\n@app.get(\"/items/\")\ndef read_item(idx:int|None = 0): return fake_db[idx]\n\nprint(cli.get('/items/?idx=1').text)\n```\n\n    {\"name\":\"Bar\"}\n\n``` python\nprint(cli.get('/items/').text)\n```\n\n    {\"name\":\"Foo\"}\n\n``` python\n@app.get(\"/booly/\")\ndef booly(coming:bool=True): return 'Coming' if coming else 'Not coming'\n\nprint(cli.get('/booly/?coming=true').text)\n```\n\n    Coming\n\n``` python\nprint(cli.get('/booly/?coming=no').text)\n```\n\n    Not coming\n\n``` python\n@app.get(\"/datie/\")\ndef datie(d:date): return d\n\ndate_str = \"17th of May, 2024, 2p\"\nprint(cli.get(f'/datie/?d={date_str}').text)\n```\n\n    2024-05-17 14:00:00\n\n``` python\n@dataclass\nclass Bodie:\n    a:int;b:str\n\n@rt(\"/bodie/{nm}\")\nasync def post(nm:str, data:Bodie):\n    res = asdict(data)\n    res['nm'] = nm\n    return res\n\ncli.post('/bodie/me', data=dict(a=1, b='foo')).text\n```\n\n    '{\"a\":1,\"b\":\"foo\",\"nm\":\"me\"}'\n\n``` python\n@app.get(\"/setcookie\")\nasync def setc(req):\n    now = datetime.now()\n    res = Response(f'Set to {now}')\n    res.set_cookie('now', str(now))\n    return res\n\ncli.get('/setcookie').text\n```\n\n    'Set to 2024-06-01 19:55:03.088788'\n\n``` python\n@app.get(\"/getcookie\")\nasync def getc(now:date): return f'Cookie was set at time {now.time()}'\n\ncli.get('/getcookie').text\n```\n\n    'Cookie was set at time 19:55:03.088788'\n\n``` python\n@app.get(\"/ua\")\nasync def ua(user_agent:str): return user_agent\n\ncli.get('/ua', headers={'User-Agent':'FastHTML'}).text\n```\n\n    'FastHTML'\n\n``` python\n@app.get(\"/hxtest\")\ndef hxtest(htmx): return htmx.request\n\ncli.get('/hxtest', headers={'HX-Request':'1'}).text\n```\n\n    '1'\n",
    "bugtrack_url": null,
    "license": "Apache Software License 2.0",
    "summary": "The fastest way to create an HTML app",
    "version": "0.0.16",
    "project_urls": {
        "Homepage": "https://github.com/AnswerDotAI/fasthtml"
    },
    "split_keywords": [
        "nbdev",
        "jupyter",
        "notebook",
        "python"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "4e351b6c546d6a05b2c4a0319f9bb3788057506192d9657637cc3d3dee7eb659",
                "md5": "09f8d91ed3cd2d2065eb17e80acdb09f",
                "sha256": "5b1d61a3f2cb5ee71fce44f5bc3f38e2bd0b38c4b2179a89800849c65bad45f6"
            },
            "downloads": -1,
            "filename": "python_fasthtml-0.0.16-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "09f8d91ed3cd2d2065eb17e80acdb09f",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 27595,
            "upload_time": "2024-06-16T23:35:29",
            "upload_time_iso_8601": "2024-06-16T23:35:29.827899Z",
            "url": "https://files.pythonhosted.org/packages/4e/35/1b6c546d6a05b2c4a0319f9bb3788057506192d9657637cc3d3dee7eb659/python_fasthtml-0.0.16-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "ece07c1fd8a6199cbb93c94a15f7e24e2b1737c9d623cc982d7d12dfd59480e2",
                "md5": "73ce6edbbcb246ff6abb6c6a3501c05f",
                "sha256": "05d63d766238de3b2f21a839d8b50086c1c6e44a1641530348adee3f6e8c6cb1"
            },
            "downloads": -1,
            "filename": "python-fasthtml-0.0.16.tar.gz",
            "has_sig": false,
            "md5_digest": "73ce6edbbcb246ff6abb6c6a3501c05f",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 29077,
            "upload_time": "2024-06-16T23:35:31",
            "upload_time_iso_8601": "2024-06-16T23:35:31.829732Z",
            "url": "https://files.pythonhosted.org/packages/ec/e0/7c1fd8a6199cbb93c94a15f7e24e2b1737c9d623cc982d7d12dfd59480e2/python-fasthtml-0.0.16.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-06-16 23:35:31",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "AnswerDotAI",
    "github_project": "fasthtml",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "python-fasthtml"
}
        
Elapsed time: 0.29282s