introduction
SWL (Seecr Web Lib) lets you build interactive web applications in Python, without a JavaScript frontend.
It is a small layer on top of Starlette, Jinja and htmx 4. The server renders HTML. An action answers with a short list of instructions: put this HTML there, reload that list, close the dialog, show a toast. SWL carries them out in the browser.
You write Python and templates. There is no build step, and no second application to keep in sync.
SWL is made for the software around data: admin screens, editors, review tools, form builders and dashboards. Lots of forms, lists, dialogs and tabs, used every day by people who need them to work.
motivation
- Why does a screen with a form and a list need two applications, an API and a JavaScript frontend, that have to be kept in sync?
- Why should the browser know how a list looks, when the server already renders it?
- Why do you need a build step and a
node_modulesfolder for a dialog? - Why test a button click in a browser, when the answer to it is just data?
Keep the server in charge of the HTML, let it say what should happen next, and let the browser do it. That is SWL.
quick start
pip install swl
A form that adds a note, and a list that loads itself:
{% from "swl/placeholder.j2" import placeholder %}
<form hx-post="{{ register('note_add') }}">
<input name="text" class="{{ SWL.ui.input }}">
<button type="submit" class="{{ SWL.ui.button_primary }}">Add</button>
</form>
{{ placeholder("notes", register('notes_list')) }}
async def notes_list(request, swl, db, **kwargs):
html = swl.template("notes/list.j2").render(notes=db.notes())
return swl.success().hydrate("notes", html)
async def note_add(request, swl, db, **kwargs):
async with request.form() as form:
text = form.get("text", "").strip()
if not text:
return swl.fail().message("A note needs some text")
db.add_note(text)
return swl.success().reset_form().rehydrate("notes").toast("Note added")
__actions__ = [notes_list, note_add]
note_add doesn’t render the list. It asks the list to reload itself, so notes_list stays the
only place that knows how the list looks. And because a response is data, you test an action
without a browser.
SWL needs Python 3.11 or newer.
what you get
- actions
- Async Python functions, registered by module, with login and authorisation checks. A
template gets an action's url with
register("name"). - a response builder
hydrate,rehydrate,open_modal,close_modal,open_contextmenu,toast,call,set_url,redirect,trigger, andswl.fail().message(...)for errors.- placeholders
- Parts of the page that load themselves and can be reloaded by name from any action.
- live updates
- A placeholder can reload when the data it shows changes, also in other browser tabs and for other users, over a websocket. A change goes to everyone or only to the users it concerns, so private things stay private. Pages catch up after a lost connection.
- translations
- Mark texts with
_()and SWL shows them in the visitor's language: their own choice or their browser's. Dates and numbers follow. SWL's own screens come in English, Dutch, German, French and Spanish. - deep links
- Actions keep the url up to date, and a page route renders the same screen from it. Screens survive a reload and can be shared.
- ready-made parts
- A modal, confirm and error dialogs, toasts and context menus, all plain HTML
(
<dialog>, popover). Login, "my account" and user management screens. - your css framework
- Bootstrap (the default), Tabler, Bulma, or SWL's own small stylesheet with no framework at all. Light and dark work with each of them.
try the example
The example application is a small task list. It uses nearly everything: dialogs, search as you type, tabs, a context menu, live updates between browser tabs, private tasks that only their owner hears about, screens that survive a reload, and switches for the language, the CSS framework and dark mode.
git clone https://github.com/seecr/swl.git
cd swl
pip install -e . -e example/
python -m swl_tasks
Open http://127.0.0.1:8000/ and log in as demo / demo-password.
docs
sources
SWL is open source under the MIT License.
- Code: github.com/seecr/swl
- Package: pypi.org/project/swl
- Issues: github.com/seecr/swl/issues
- Security problems: see SECURITY.md
SWL is made and maintained by Seecr, who use it for their own applications.
Simplicity is prerequisite for reliability.