-
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added the project + tests + docs + CI
- Loading branch information
Showing
22 changed files
with
789 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
name: Build documentation | ||
on: | ||
push: | ||
branches: | ||
- main | ||
permissions: | ||
contents: write | ||
jobs: | ||
deploy: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Check out repository code | ||
uses: actions/checkout@v3 | ||
|
||
- name: Install python | ||
uses: actions/setup-python@v4 | ||
with: | ||
python-version-file: pyproject.toml | ||
|
||
- uses: actions/cache@v2 | ||
with: | ||
key: ${{ github.ref }} | ||
path: .cache | ||
|
||
- run: pip install mkdocs-material mkdocstrings[python] | ||
|
||
- run: mkdocs gh-deploy --force |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
name: Linters | ||
on: push | ||
jobs: | ||
Linters: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Check out repository code | ||
uses: actions/checkout@v3 | ||
|
||
- name: Install python | ||
uses: actions/setup-python@v4 | ||
with: | ||
python-version-file: pyproject.toml | ||
|
||
- name: Install poetry | ||
run: | | ||
curl -sSL https://install.python-poetry.org | python3 - | ||
- name: Install dependencies | ||
run: | | ||
poetry install | ||
- name: Run static checks | ||
run: | | ||
poetry run poe static-checks |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
name: Tests | ||
on: push | ||
jobs: | ||
Linters: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Check out repository code | ||
uses: actions/checkout@v3 | ||
|
||
- name: Install python | ||
uses: actions/setup-python@v4 | ||
with: | ||
python-version-file: pyproject.toml | ||
|
||
- name: Install poetry | ||
run: | | ||
curl -sSL https://install.python-poetry.org | python3 - | ||
- name: Install dependencies | ||
run: | | ||
poetry install | ||
- name: Run tests | ||
run: | | ||
poetry run poe test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
![Tests](https://github.com/volfpeter/fasthx/actions/workflows/tests.yml/badge.svg) | ||
![Linters](https://github.com/volfpeter/fasthx/actions/workflows/linters.yml/badge.svg) | ||
![Documentation](https://github.com/volfpeter/fasthx/actions/workflows/build-docs.yml/badge.svg) | ||
![PyPI package](https://img.shields.io/pypi/v/fasthx?color=%2334D058&label=PyPI%20Package) | ||
|
||
**Source code**: [https://github.com/volfpeter/fasthx](https://github.com/volfpeter/fasthx) | ||
|
||
**Documentation and examples**: [https://volfpeter.github.io/fasthx](https://volfpeter.github.io/fasthx/) | ||
|
||
# FastHX | ||
|
||
FastAPI and HTMX, the right way. | ||
|
||
Key features: | ||
|
||
- **Decorator syntax** that works with FastAPI as one would expect, no need for unused or magic dependencies in routes. | ||
- Works with **any templating engine** or server-side rendering rendering library, e.g. `markyp-html` or `dominate`. | ||
- Built-in **Jinja2 templating support** (even with multiple template folders). | ||
- Gives the rendering engine **access to all dependencies** of the decorated route. | ||
- FastAPI **routes will keep working normally by default** if they receive **non-HTMX** requests, so the same route can serve data and render HTML at the same time. | ||
- **Correct typing** makes it possible to apply other (typed) decorators to your routes. | ||
- Works with both **sync** and **async routes**. | ||
|
||
## Installation | ||
|
||
The package is available on PyPI and can be installed with: | ||
|
||
```console | ||
$ pip install fasthx | ||
``` | ||
|
||
## Examples | ||
|
||
### Jinja2 templating | ||
|
||
To start serving HTMX requests, all you need to do is create an instance of `fasthx.Jinja` and use it as a decorator on your routes like this: | ||
|
||
```python | ||
from fastapi import FastAPI | ||
from fastapi.templating import Jinja2Templates | ||
from fasthx import Jinja | ||
|
||
# Create the app. | ||
app = FastAPI() | ||
|
||
# Create a FastAPI Jinja2Templates instance and use it to create a | ||
# FastHX Jinja instance that will serve as your decorator. | ||
jinja = Jinja(Jinja2Templates("templates")) | ||
|
||
@app.get("/htmx-or-data") | ||
@jinja("user-list.html") # Render the response with the user-list.html template. | ||
def htmx_or_data() -> dict[str, list[dict[str, str]]]: | ||
return {"users": [{"name": "Joe"}]} | ||
|
||
@app.get("/htmx-only") | ||
@jinja.template("user-list.html", no_data=True) # Render the response with the user-list.html template. | ||
def htmx_only() -> dict[str, list[dict[str, str]]]: | ||
# no_data is set to True, so this route can not serve JSON, it only responds to HTMX requests. | ||
return {"users": [{"name": "Joe"}]} | ||
``` | ||
|
||
### Custom templating | ||
|
||
Custom templating offers more flexibility than the built-in `Jinja` renderer by giving access to all dependencies of the decorated route to the renderer function: | ||
|
||
```python | ||
from typing import Annotated | ||
|
||
from fastapi import Depends, FastAPI | ||
from fasthx import hx | ||
|
||
# Create a dependecy to see that its return value is available in the render function. | ||
def get_random_number() -> int: | ||
return 4 # Chosen by fair dice roll. | ||
|
||
DependsRandomNumber = Annotated[int, Depends(get_random_number)] | ||
|
||
# Create the render method: it must always have these three arguments. | ||
# If you're using static type checkers, the type hint of `result` must match the return type | ||
# annotation of the route on which this render method is used. | ||
def render_user_list(result: list[dict[str, str]], *, context: dict[str, Any], request: Request) -> str: | ||
# The value of the `DependsRandomNumber` dependency is accessible with the same name as in the route. | ||
random_number = context["random_number"] | ||
lucky_number = f"<h1>{random_number}</h1>" | ||
users = "".join(("<ul>", *(f"<li>{u.name}</li>" for u in result), "</ul>")) | ||
return f"{lucky_number}\n{users}" | ||
|
||
@app.get("/htmx-or-data") | ||
@hx(render_user_list) | ||
def htmx_or_data(random_number: DependsRandomNumber) -> list[dict[str, str]]: | ||
return [{"name": "Joe"}] | ||
|
||
@app.get("/htmx-only") | ||
@hx(render_user_list, no_data=True) | ||
async def htmx_only(random_number: DependsRandomNumber) -> list[dict[str, str]]: | ||
return [{"name": "Joe"}] | ||
``` | ||
|
||
## Dependencies | ||
|
||
The only dependency of this package is `fastapi`. | ||
|
||
## Development | ||
|
||
Use `ruff` for linting and formatting, `mypy` for static code analysis, and `pytest` for testing. | ||
|
||
The documentation is built with `mkdocs-material` and `mkdocstrings`. | ||
|
||
## Contributing | ||
|
||
All contributions are welcome. | ||
|
||
## License - MIT | ||
|
||
The package is open-sourced under the conditions of the [MIT license](https://choosealicense.com/licenses/mit/). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# `DependsHXRequest` | ||
|
||
::: fasthx.main.DependsHXRequest |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# `HTMXRenderer` | ||
|
||
::: fasthx.main.HTMXRenderer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# `Jinja` | ||
|
||
::: fasthx.main.Jinja |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# `get_hx_request()` | ||
|
||
::: fasthx.main.get_hx_request |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# `hx()` | ||
|
||
::: fasthx.main.hx |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
![Tests](https://github.com/volfpeter/fasthx/actions/workflows/tests.yml/badge.svg) | ||
![Linters](https://github.com/volfpeter/fasthx/actions/workflows/linters.yml/badge.svg) | ||
![Documentation](https://github.com/volfpeter/fasthx/actions/workflows/build-docs.yml/badge.svg) | ||
![PyPI package](https://img.shields.io/pypi/v/fasthx?color=%2334D058&label=PyPI%20Package) | ||
|
||
**Source code**: [https://github.com/volfpeter/fasthx](https://github.com/volfpeter/fasthx) | ||
|
||
**Documentation and examples**: [https://volfpeter.github.io/fasthx](https://volfpeter.github.io/fasthx/) | ||
|
||
# FastHX | ||
|
||
FastAPI and HTMX, the right way. | ||
|
||
Key features: | ||
|
||
- **Decorator syntax** that works with FastAPI as one would expect, no need for unused or magic dependencies in routes. | ||
- Works with **any templating engine** or server-side rendering rendering library, e.g. `markyp-html` or `dominate`. | ||
- Built-in **Jinja2 templating support** (even with multiple template folders). | ||
- Gives the rendering engine **access to all dependencies** of the decorated route. | ||
- FastAPI **routes will keep working normally by default** if they receive **non-HTMX** requests, so the same route can serve data and render HTML at the same time. | ||
- **Correct typing** makes it possible to apply other (typed) decorators to your routes. | ||
- Works with both **sync** and **async routes**. | ||
|
||
## Installation | ||
|
||
The package is available on PyPI and can be installed with: | ||
|
||
```console | ||
$ pip install fasthx | ||
``` | ||
|
||
## Examples | ||
|
||
### Jinja2 templating | ||
|
||
To start serving HTMX requests, all you need to do is create an instance of `fasthx.Jinja` and use it as a decorator on your routes like this: | ||
|
||
```python | ||
from fastapi import FastAPI | ||
from fastapi.templating import Jinja2Templates | ||
from fasthx import Jinja | ||
|
||
# Create the app. | ||
app = FastAPI() | ||
|
||
# Create a FastAPI Jinja2Templates instance and use it to create a | ||
# FastHX Jinja instance that will serve as your decorator. | ||
jinja = Jinja(Jinja2Templates("templates")) | ||
|
||
@app.get("/htmx-or-data") | ||
@jinja("user-list.html") # Render the response with the user-list.html template. | ||
def htmx_or_data() -> dict[str, list[dict[str, str]]]: | ||
return {"users": [{"name": "Joe"}]} | ||
|
||
@app.get("/htmx-only") | ||
@jinja.template("user-list.html", no_data=True) # Render the response with the user-list.html template. | ||
def htmx_only() -> dict[str, list[dict[str, str]]]: | ||
# no_data is set to True, so this route can not serve JSON, it only responds to HTMX requests. | ||
return {"users": [{"name": "Joe"}]} | ||
``` | ||
|
||
### Custom templating | ||
|
||
Custom templating offers more flexibility than the built-in `Jinja` renderer by giving access to all dependencies of the decorated route to the renderer function: | ||
|
||
```python | ||
from typing import Annotated | ||
|
||
from fastapi import Depends, FastAPI | ||
from fasthx import hx | ||
|
||
# Create a dependecy to see that its return value is available in the render function. | ||
def get_random_number() -> int: | ||
return 4 # Chosen by fair dice roll. | ||
|
||
DependsRandomNumber = Annotated[int, Depends(get_random_number)] | ||
|
||
# Create the render method: it must always have these three arguments. | ||
# If you're using static type checkers, the type hint of `result` must match the return type | ||
# annotation of the route on which this render method is used. | ||
def render_user_list(result: list[dict[str, str]], *, context: dict[str, Any], request: Request) -> str: | ||
# The value of the `DependsRandomNumber` dependency is accessible with the same name as in the route. | ||
random_number = context["random_number"] | ||
lucky_number = f"<h1>{random_number}</h1>" | ||
users = "".join(("<ul>", *(f"<li>{u.name}</li>" for u in result), "</ul>")) | ||
return f"{lucky_number}\n{users}" | ||
|
||
@app.get("/htmx-or-data") | ||
@hx(render_user_list) | ||
def htmx_or_data(random_number: DependsRandomNumber) -> list[dict[str, str]]: | ||
return [{"name": "Joe"}] | ||
|
||
@app.get("/htmx-only") | ||
@hx(render_user_list, no_data=True) | ||
async def htmx_only(random_number: DependsRandomNumber) -> list[dict[str, str]]: | ||
return [{"name": "Joe"}] | ||
``` | ||
|
||
## Dependencies | ||
|
||
The only dependency of this package is `fastapi`. | ||
|
||
## Development | ||
|
||
Use `ruff` for linting and formatting, `mypy` for static code analysis, and `pytest` for testing. | ||
|
||
The documentation is built with `mkdocs-material` and `mkdocstrings`. | ||
|
||
## Contributing | ||
|
||
All contributions are welcome. | ||
|
||
## License - MIT | ||
|
||
The package is open-sourced under the conditions of the [MIT license](https://choosealicense.com/licenses/mit/). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from .main import DependsHXRequest as DependsHXRequest | ||
from .main import HTMXRenderer as HTMXRenderer | ||
from .main import Jinja as Jinja | ||
from .main import get_hx_request as get_hx_request | ||
from .main import hx as hx |
Oops, something went wrong.