Ludic: New framework for Python with seamless Htmx support
github.com
github.com
Interested in feedback!
* Docs: https://ludic.readthedocs.io/ * Code: https://github.com/paveldedik/ludic/ * Examples: https://github.com/paveldedik/ludic/tree/main/examples
Now you can use it in f-strings, and well, other components.
The idea from the beginning was to integrate it with htmx.org, so to me, it feels kind of similar when you are writing endpoints using these "components".
But I understand why you are raising this question, I also didn't know how to name it, and React is a completely different framework. I didn't know how to describe the framework in just a few sentences but wanted to somehow. I might change the description at some point.
I still don't know how useful the framework is, I am playing around with it and I like it. Who knows what other people think, though.
// edit - code block don't work here, removed code sample.
The example does nothing other than renaming `href` to `to` and adding a style.
How should the framework know which styles you want to apply to a link?
I think the example explains the component-driven design of the library well.
It’s somewhere between pointless and confusing to me, and isn’t illustrative of why one would want to use components, or what a competent even is.
https://www.ruby-lang.org/en/about/
As a web developer (and therefore, I imagine, part of the target demographic for this sort of tool), I personally find this component example very useful. Components are very powerful, but most existing Python templating languages make it difficult or overly verbose to use them to their full extent. This is a really good demonstration of how I can write simple presentational components and use them with server-rendered HTMX. That shows off one of the main things I would want to do with this sort of framework.
So from that perspective, this is very much the perfect sort of example.
But this might also mean that one is not the target audience in this case…
BTW in Rust, you can create macros, that is something I like a lot as you can see in yew framework - https://yew.rs/docs/getting-started/build-a-sample-app#updat... - you can write HTML which probably are typed. Python doesn't have anything like that, I don't know about any other way to do this.
// edit typo
One word of warning for people considering HTMX though – it's not a framework, it's an appliance.
In my experience with it, I ran into problems integrating with HTMX very quickly. Drop it into your project, use the HTML attributes, and it'll be great. However if you need custom Javascript, HTMX provides little in the way of an integration path (the lifecycle events are basic), and nothing in the way of structure, and can even get in the way in places where its lifecycle causes issues in the lifecycle of whatever you're adding. To add your own Javascript you'll almost certainly want Jquery or React/Svelte/etc, and with the latter you'll get interactions between their lifecycles.
If you're using it entirely via the HTML API, and you're happy to accept that sometimes it won't do something, you'll be fine. If you're looking to add units of more complex client-side functionality in places, try prototyping with it to make sure it works well, it didn't for me.
My context here is a mostly server-side application using HTMX, where I wanted to add a few small bits of additional client-side code. I tried to do it without any libraries but rapidly realised that Jquery would simplify things a lot, and managed to do ok with that, but was still fighting HTMX throughout, and I ended up with a lot of poorly managed local state quite quickly. It doesn't take much complexity to need some better form of state management, and something like React provides some direction there, but of course there are other options, my point was only really to say that HTMX will not do any of this for you, and may get in the way depending on what you need to do.
I kind of see HTMX as discouraging the traditional SPA-ish state management approach though. Or, it could be that the idea just gives me peace of mind. But, the idea being that you manage state less on the client because you're updating the UI with DOM content generated on the server versus rendering from local state on the client.
In practice, of course, avoiding client-state can be tough, especially as you go from sprinkling in limited dynamic interaction to a full-on SPA experience. I'm a little different though, in that I'm not sure SPAs were ever a good idea—at least without shifting completely away from building directly on Web idioms like DOM, HTML and CSS.
Yes, it can be used to render the entire UI app with rich state management across all pages, routing, etc.
However it can just as simply be used to render 1 complex part of an app that has an isolated but possibly complex state. In this scenario I think sprinkling react is reasonable.
At that point, you can just write your own Javascript functions / classes for rendering, and handle your own state management
HTMX lends itself more to "sprinkling". Its basically just a very compact version of fetch(), combined with trigger logic (e.g. setTimeout(), onclick, etc.
If most of your logic is server-side and client JS is kept small, custom elements let you lean on lifecycle events built right into the browser specs.
['p', 'This is a ', ['a', {'href': 'https://example.com'}, 'link']]
to <p>This is a <a href="https://example.com">link</a></p>- <https://github.com/abilian/webbits>
Inspirations for the idea of generating HTML code from Python:
- <https://tylerbakke.github.io/MarkupPy/>
- <https://github.com/michaeljones/packed>
- <https://github.com/twidi/mixt/>
- <https://github.com/byteface/domonic>
- <https://pypi.org/project/hyperpython/>
- <https://pypi.org/project/PyHTML/>
- <https://github.com/jviide/htm.py>
- <https://viewdom.readthedocs.io/>
- <https://github.com/pcarbonn/fast_html>
Most of the frameworks here are calling side effects upon shared objects in the guise of `with` statements. Not to mention the extreme verbosity.
> <if cond>
> <long code 1>
> <else>
> <long code 2>
>
> [longcode1() if cond else longcode2()]
>
> <for i in ls>
> <long code 1>
>
>[longcode() for i in ls]
I started using it for some documentation generation purposes. I'm not a web-dev.
Disclaimer: Not my code, just an example I found.
div {
onClick(loadItems)
}
I don't know whether I'll ever actually get it in good enough shape to release, but I do plan to blog about how some of the tricks I've found it fitting it together. One thing I definitely need to do before anybody looks at it is clean up how routes and targets are managed.There is a strong developer instinct to "simplify through complexity". That is, to stuff down so much functionality and saw off all edges in an effort to hide complexity. "Let's expose a simple interface that will allow devs to do everything they want to do."
It starts with good intentions and initially provides some improvements in productivity. But it continues to evolve to the point of untenability: the complexity starts to ooze out and become a thing to manage, but with abstractions that make it more difficult; the framework becomes so opinionated that it begins to stifle; performance issues are introduced as we drift farther from the metal.
We continue to try to solve with newer constructs and abstractions, frequently introducing more issues. Eventually, it comes full circle ("hey, let's add SSR!"), and we're again looking for another solution. One that's simpler.
Then the cycle starts over again.
I think there is an inflection point with all of these things, wherein whatever perceived productivity gains are had by pushing more functionality into the framework quietly begins the path to regression. I don't exactly know where it is, but I would bet that it happens far sooner than we suspect.
So, I kind of like HTMX where it is, in spirit and implementation. Whatever additional productivity I can pick up via helper functionality and reuse is something I believe there is value in devs doing on their own, for their individual use cases and preferences. There's a tendency to think "well why reinvent the wheel? Let's make it a framework". I've come to believe that impulse is somewhat pernicious in its effect. Ultimately we're going to have to write our own code at some point. I'd suggest that we're better off doing it much sooner than frameworks encourage.
- Type safe HTML: you get an exception instead of malformed HTML
- Truly reusable components: any Python web framework, component packages are truly reusable in any other projects. I never seen this with template engines.
- Huge productivity boost: not even close! No hunting for templates, no jumping between files, everything is in Python functions.
- Composable elements: you can nest things as much as you want, refactor very easily. Doing this with templates is not even possible, or such a pain I never did it.
- Storybook: There are a couple ones for Django, but they are clunky and not reusable at all.
I felt the same for a long time, but started thinking about a nice API. It took me months, but I finally got it: https://github.com/kissgyorgy/compone
Here is an example of a HTML page layout written with the DOMinate [1] library for example, in a "JSX-like" way:
https://github.com/olivierphi/zakuchess/blob/main/src/apps/w...
It may hurt your eyes at first sight, for sure... But similarly to technologies like Tailwind CSS, it's mostly a matter of getting used to it - and after a while it end ups feeling very natural to use :-)
- The most flexible shim approaches typically end up wrapping JS/React components anyway (like Reflex/Pinecone)
- We really need better IDE support for html strings that are within python strings. The editing problem is a big setback.
The ergonomics of Python + JS in separate code files won out and the user experience has been better than forcing them both into a common language would be.
This has the benefit of leveraging whatever the two languages are best at, in native code, so you have access to all the native APIs without having to learn a shim on top of it. Way more longevity to that approach too. Context switching between two languages isn't that bad if you minimize the glue layer that you have to write between them.
I remember coding PL/SQL to emmit HTML in Oracle around 1999 or 2000 and using functions to code the various elements.
That got old and repetitive very quickly - for instance, everytime I had to correct a spelling error, I had to recompile the code.
To get around it I used one or two tables to hold html snippets to decouple the business/backend logic from the frontend, and stopped using the PL/SQL functions completely.
My speed of developmet skyrocketed, and separating and abstracting the frontend from the backend made so much sense.
A few years later, I was doing web developemt with Python using the Zope framework (not many people know about it tiday, I think).
It uses a specialised serverside templating language called TAL (Template Attribute Language)[1] that basically builds the front end dynamically, and then you feed it data from the backend.
Very neat and allowed me to build reusable compoments as well as collections of a schema definition (basically a dict), html template(s) and the code to validate that the input matched the schema and could be rendered.
Or something like that - its been 2 employers and almost 20 years since I worked with that :)
I did build a small extension for Wordpress using a PHP implementation[2] of TAL a few years ago, and TAL still works like a charm :)
My point is that I still believe there is value in keeping python out of the html-templating, and in keeping the front end logic apart from the backend logic.
There is something I am not understanding about the renewal of mixing HTML/GUI template with code, but I haven't fully found it yet.
[1] https://en.m.wikipedia.org/wiki/Template_Attribute_Language
[2] I believe it was this one https://phptal.org/
https://github.com/paveldedik/ludic/blob/main/examples/click...
It's the kind of thing that looks very cool and concise in small examples, but tends to become a nightmare when you are working on larger projects.
Python template engines have the exact same problem, just way less obvious.
It doesn't have to be that way. Make all the queries up-front and pass the result the same way as you would pass context to templates. This way, all your components are pure. The difference is explicitness. Much easier to spot where side-effects happen than in templates.
I don't think the issue is "markup expressed in another language" - I think it's "poor application architecture". I don't dispute there might be a correlation between libraries and frameworks that do poorly on each - but that doesn't mean it's intrinsic.
I had a lot of people tell me I was crazy to write aiohttp servers because of the problems the previous poster mentioned but I had a long run of it working pretty well. On the other hand I’ve frequently had the experience of writing something in Python that falls apart like a tower of Jenga blocks the moment somebody else touches it. (Quite different from my experience with Java, Javascript, C#, PHP, …)
Once I started serving images along with the text and using the app over a 2Mbps link it seemed to me the aiohttp server was getting a little unreliable.
Most basically, an aiohttp server can only use one CPU thread at a time with the consequences that if one request holds the CPU for 5 seconds the server stops processing requests completely.. Although the aiohttp server could be reliable if you are almost entirely waiting for data to get back from the database, the residual amount of CPU load is going to put an upper bound on what the server can handle in the best case. If a coder is not so careful you get burned. My RSS reader has an AI component which runs completely in batch jobs, I don’t do any inference during requests which limits my flexibility and makes the application less “real-time” than it could be.
You can imagine systems where one aio server hands tasks off the another aio server but the blocking situation doesn’t get any better and if you want to make the system scalable there has to be at least one multiprocess or multithreaded server somewhere. At some point I am like “I have a 16 core machine but I am only using 1 core like my laptop in the 1990s” It didn’t help that my aio database drivers aren’t in conda which is another build hassle.
So last week I tore up the image sorter and rewrote it in Flask (my aio framework looks like Flask so it’s not too hard) and I run it out of gunicorn, I put a reverse proxy in front to deal with some Tailscale problems so I have IIS serving the images.
Does that imply that it was due to images being heavier, and thus if you were to stream something like video from aiohttp it would not go so well?
Currently for my case the worst problem is head-of-line blocking, the system could be downloading 50 large images but to put a tag on a gallery it needs to load a small HTML page for a modal dialog, then it has to populate a dropdown box after a tag category is selected, then process the form request.
Since all these images are queued to download, the system has trouble prioritizing the HTML requests. The best answer to this for me is to build that thumbnailer to get the image size down, but I am also thinking of putting images on an entirely different port.
https://stackoverflow.com/q/78036302/265521
I've written about 200 lines of Python async code in total, so to run into a bug like that so soon was not encouraging.
I suspect it's just not a very popular feature and so it doesn't get a lot of use and debugging. And it's Python so it really needs a lot of ton of real world use to detect bugs.
Anyway I'm not going to waste my time debugging Python internals so I just switched to Deno.
Complicating them with hooks for a bunch of random APIs is asking for trouble. Let the application servers sit behind them so they don't have to worry about those issues, and nginx/apache don't have to worry about application server issues
At work, we have been exploring alternatives to templates in our Django project and just released it as a lib to help with this:
Have you tried to "bring up front" if and for? For example
> if_(cond, tag1(), tag2()) # without eagerly execute both branches
Instead of
> tag1() if cond else tag2()
And
for_(cars, lambda car: car_details(car)) # while still being type checked
Instead of
> [car_details(car) for car in cars]
htpy supports generators/callables as children (https://htpy.dev/streaming/). As long as you are careful to wrap things in generators/lambdas everything is lazy. Implementing if_ and for_ is straightforward and anyone can build constructs like that to use. But will not be lazy unless all arguments are wrapped in lambdas:
from htpy import div, li, ul
def if_(cond, a, b):
return a() if cond() else b()
def for_(items, func):
return (func(item) for item in items())
print(div[if_(lambda: True, lambda: "True!", lambda: "False!")])
print(div[if_(lambda: False, lambda: "True!", lambda: "False!")])
print(ul[for_(lambda: ["a", "b", "c"], lambda x: li[x])])
output: <div>True!</div>
<div>False!</div>
<ul><li>a</li><li>b</li><li>c</li></ul>[0]: https://lxml.de/lxmlhtml.html#creating-html-with-the-e-facto...
[1]: https://ricardoanderegg.com/posts/python-build-html-componen...
I personally like hyperdiv [1] approach (even if no use of Htmx) which is very intuitive for python code.
https://github.com/maxpert/htmxido
Hopefully will be useful for people as well
.js callbacks later to fill stuff in, sure.
Thanks for the wave of nostalgia.
I'm still a bit sceptical if this is the solution but I'm definitely going to look into it as it fixes a few things I'm annoyed with.
Thanks!
Whats wrong with just writing HTML f-strings and using flask tho?
https://github.com/j4mie/hotmetal/
The core implementation is only 85 lines of Python, and has no imports at all, so works nicely on minimal/alternative Pythons like Micropython.
I'm using it on a medium-sized side project and it's shockingly productive compared to string templates. I don't think anyone is using it except me, though.
I've also been working on a library with a similar idea:
https://github.com/SamDudley/neat-html
It's still a work in progress so an example might be better to look at:
https://github.com/SamDudley/neat-html/blob/main/examples/fo...
I was inspired by some JavaScript libraries that I've used in the past:
- https://github.com/jorgebucaran/hyperapp
- https://github.com/hyperhype/hyperscript
There is also a working integration with Django that enables the use of neat-html as a template backend, however it isn't up on GitHub yet.
I find the space of HTML generation libraries which can leverage the power of Python, really interesting.
Apart from the simplicity, I like the fact you can "install" it by copy-pasting the code at the top of your script, or running:
curl -L https://raw.githubusercontent.com/j4mie/hotmetal/main/hotmetal/__init__.py >> myscript.py
which is nice when you want a self-contained file.I think I'll be using this in the future.
https://github.com/weavejester/hiccup https://github.com/JuneKelly/sneeze
https://github.com/twisted/nevow/blob/master/examples/simple...