If anything sync (not async) infects everything you do.
Of course it depends if you call the infrastructure (then it's better for it to be sync) of if the infrastructure calls you (then it's better to be async).
Rendering engine is something you rarely call, but it often calls your functions.
https://journal.stuffwithstuff.com/2015/02/01/what-color-is-...
Is that specific to the threading model for Python?
The reverse is true in nodejs where once you’ve got one async call, the entire chain must be async.
I guess the point is to have components know how to fetch their own data, particularly when combining with HTMX and having backend return page fragments that correspond to components. But maybe this makes more sense in React than it does when translating the pattern back to server-side?
e.g. same author has this https://github.com/volfpeter/fasthx?tab=readme-ov-file#htmy-... which is doing that, but there's still a 'view' endpoint. Why not put the data fetch code there and have 'dumb' components that don't need to be async?
While on some level it makes sense for HTML rendering to be a pure function where the inputs are gathered from elsewhere (potentially asynchronously), it looks like htmy wants to make it easy to define hierarchies of components. Instead of `is_admin()`, imagine a dashboard whose layout is stored in a database, supporting configurable charts of various flavors. The heterogeneity of the data supporting different types of charts makes it hard to efficiently pull data in a single SQL query (equivalently, any reasonable database model), so somewhere in your code you're pulling a bunch of data asynchronously, and somewhere else you're rendering it. The question, still, is "where?"
Going back to the idea of htmy defining hierarchies of components, imagine how annoying it would be to have to manually grab all the data for a "reporting page" component only to feed it straight back into the renderer -- either having to duplicate the hierarchial structure when feeding data into the renderer (a technique some UI libraries employ, though I don't like it) -- or having to come up with a method for flattening that hierarchy when instantiating the component (another technique some UI libraries employ, one I like more for small projects and less for large ones).
They solve that (to the extent that you think it needs solving) by bundling all that background logic into the components themselves. Did they really need to implement that recursively instead of just walking the hierarchy, gathering the data up-front, and populating it? Eh. The code winds up being similar either way, and either way it definitely forces async back into the middle of HTML rendering.
Mind you, that tends to either make some applications hard to build or to cause the framework to explode in complexity over time as people need new and new ways to say "yes, re-render this thing; no, re-render that other thing, but don't grab its data, ...." There's enough less particularly annoying code involved though that fat, smart components are a natural place for people to gravitate.
Unrelated to htmy completely, a technique I like from time to time even for problems which don't need async per se (and I'm usually using lower-level languages, so the implementation is some sort of more manual continuation pattern, but all those things are basically async, so I won't dwell on the details) is explicitly designing pausable/restartable structures for long-running computations. It's about as easy to write as purely iterative code, and you can run the result as purely iterative with no runtime overhead, so the downsides are low. It opens the door though to easily tuning how long you defer invariant maintenance (too infrequent and your algorithm devolves to the slow thing it's replacing, too frequent and the overhead isn't worth it), easily checkpointing a computation, adding other custom runners for an algorithm (like animating its progress), .... I can absolutely see a use-case for wanting to visualize each step of an HTML rendering, or log OS network counters after each step, and so on. Python's async isn't really the right tool for the job for that (it's hard to modify the runtime to support them without building quite a lot of nonsense from scratch), but async in the abstract isn't bad at all per se.
But there are many others. Not sure I understand the point of async rendering, unless you want to mix IO with rendering? Which feels a bit too close to old PHP+html for my blood.
Of course if you want client-side whatever, you need JavaScript.
You're right, for example the documentation should be improved quite a bit. Keep in mind that this project is pretty new, I simply had no time to add more docs or further improve the existing one.
Ps.: with the Snippet utility and markdown support, you can actually write quite a bit of your HTML in a html files rather than Python. You could even use Jinja templates as the backend for some of your components. This part will see more work as I have spare time to work more on the project.
For those asking the point is being able to do similar to React JSX components, but on the server side. It's so much nicer to use than templates like Django or Jinja (there might be other reasons, but this is quite clearly the goal of htpy and I assume this too).
Just looking at this one briefly it seems to use magic methods on dataclasses. What's the advantage of that over just a function? Seems like unnecessary nesting.
There are no magic methods really, you can even write function components. Using dataclasses in examples is also an irrelevant technical details.
The actual reason for requiring an `htmy()` method is that this way you can turn any of your business objects (be it Pydantic or SQLModel classes for example) into components without the fear of a method name conflict with your business stuff. Actually, I expect/planned this to be a very frequent use-case, and then there'll be zero unnecessary nesting.
Note that rendering to the “screen” really means writing bits at a memory range, which is just one interface for displaying things. Html is another, higher level interface these days.
This reminds me of the best part of Flutter UI composition, but in a language I always return to.
Have you done any benchmarking? I don't even know what the comparison would be.
Much simpler than this library, components are simply functions, rendered to strings.
I made one microbenchmark, it's "only" 2x slower than Jinja2 right now, but I know how to make it faster.
I've implemented a bunch of AlpineJS "components" as jinja macros in my current project and ... it works, but it's pretty ugly and it sucks not having type safety or ability for the IDE to understand connections between the template and the Python code
what I really want is something like JSX/TSX for Python... having gone through this process I can see why that approach is desirable. I kind of feel like libs which mimic the syntax but unable to provide the type-safety/IDE support are missing the point. So although I love the look of "Python HTML element objects" approach libs like yours and OP have I think for now it is probably the best way available.
for my current project we are pre-compiling all the jinja templates (via Jinja's own utils) for deployment as AWS Lambda
I did look into JinjaX but it has its own separate jinja env and secondary template cache and didn't look like it would be easy to plug it into the pre-compile step
I'll probably do a simple comparison with Jinja (using FastAPI) this week. Given that I can put an htmy() method on my business objects (it was an important design consideration, no conflict with other tools), I expect an okay results, but we'll see.
The renderer is as simple/minimal as possible at this point (the focus was on the core feature set I needed until now), so it's performance is as bad as it can be :) There's plenty of room for improvement. I'll work on a few optimizations as I have time, but contributions are more than welcome.
Flask/quart are painful to work with due to horrible documentation in my experience, but they're popular too. Quart is just an async rewrite of flask by the same owners.
Litestar has a half baked comparison chart here: https://docs.litestar.dev/latest/
https://github.com/sfermigier/awesome-python-web-frameworks
Note: as you probably know, popularity is not necessarily correlated with "actively maintained". For instance, Hug and Sanic are quite popular, but haven't seen a commit for quite a long time.
I’ve found the performance issue to be serious in some situations. Fortunately, there’s a number of accelerators for Python code that boost its performance. They range from JIT’s (eg PyPy) to custom VM’s (eg Cinder) to writing fast paths in Cython to Python-to-C++ compilers (eg mycpp).
So, you get the productivity and familiarity of Python with performance boosting in many use cases. If it doesn’t work, then it’s better to write that component as an extension in a systems language.
Reflex is a great project with a great feature set, it does everything (client rendering, state sync, API) and you can even write your callbacks in Python. It seems like the best option from this family of frameworks (alternative is NiceGUI for example, but having worked quite a bit with that, I probably wouldn't recommend it). Doing everything has some downsides though: there's a ton of "magic" under the hood, the lib is obviously very opinionated (it couldn't exist otherwise) and you may have a hard time if you need something that's not built in to the framework.
htmy is pretty much the opposite, it only does HTML rendering and comes with a set of utilities for advanced uses, e.g. async support, context usage, styled markdown, etc.. With FastHX, you also get a pretty convenient, declarative integration with FastAPI and HTMX. The tool is ergonomic, but you do need to put in more work compared to Reflex (create APIs, use HTMX, maybe AlpineJS or similar client-side tools). In exchange for simplicity (and lack of magic), you get full control over everything: you can convert your business objects to components, use any CSS/UI lib, any backend tooling. Extra benefit is you can migrate to (and from) it relatively easily from tools like Jinja.
Based on everything I see in the documentation, you should be able to use Pydantic models as well, or standard python objects, or anything else, as long as it has a method `def htmy(self, context: Context) -> Component`.
Dataclasses let you return "incorrect" data and that's a good thing. I'd rather get an unexpected None here and there (which can be handled) than have library code crash because the wrong type snuck into a field I don't even care about.
As for support, is any explicit support needed? You can Pydantic models into things expecting dataclasses and often the other way around too.