FlyWeb is a smol live immediate-mode web framework for Python:
- smol: minimal dependencies (no frontend build required).
- live: updates are pushed to the browser in real time.
- immediate-mode: you write a function that emits HTML elements for the whole page, immediate mode GUI-style.
- web framework: FlyWeb takes care of sending the web page contents to the browser, and calling your Python event handlers magically.
- Python: 3.10+ and an ASGI server required.
Here's a minimal program that renders a counter and an [INCREMENT] button:
import asyncio
import flyweb
class Counter:
def __init__(self):
self._count = 0
def render(self, w: flyweb.FlyWeb) -> None:
with w.div():
w.text(f"count is {self._count}")
with w.div():
w.button("INCREMENT", onclick=self._increment)
def _increment(self, _) -> None:
self._count += 1
async def main():
counter = Counter()
await flyweb.Server(counter.render, port=8000).run()
if __name__ == "__main__":
asyncio.run(main())
There are a couple more examples under src/flyweb/examples.
$ pip install 'flyweb-framework[examples]'
$ python -m flyweb.examples.todo
Then go to http://localhost:8000/.
Behind the scenes, FlyWeb works like this:
- Your
render
function builds up a virtual DOM. - This virtual DOM gets serialized to JSON and sent to the frontend over socket.io. Any event handlers get converted to magic strings that say "hey frontend, please send me a message if this event happens".
- Frontend turns the JSON structure into a VDOM that Maquette then turns into a real DOM.
- As you interact with the web page, events are sent to the backend,
decoded, and matching event handlers are called. Your
render
function is called again, and the results are sent to the frontend. Maquette diffs the VDOMs and updates the real DOM with any changes that happened.
FlyWeb uses anyio library (thus should
work with either built-in asyncio
or
trio libraries).
FlyWeb is mostly intended as a quick way of adding simple web interfaces to internal tools without having to do a bunch of scaffolding or frontend builds. It probably won't be suitable for handling complex pages or many users.
Currently it assumes that you want to share the state between all the webpage users. Implementing per-user state would not be too difficult.
Security: currently there is none, and it's probably pretty easy to crash the backend if you try.
- It works, and I've been successfully using it for a handful of different internal tools.
- Using stateful elements (e.g., checkboxes or textboxes) isn't yet as streamlined as I would like. You have to instantiate (and reuse) objects that keep track of the state (example).
- The API is likely going to change as things evolve.