Hacker News new | past | comments | ask | show | jobs | submit | polyrand's comments login

Very cool! After trying different approaches to render HTML from Python objects (including lxml, xml, etc.) I ended up liking htpy[0] the most, and the apps I built look similar to the examples in the FastHTML docs. I'll definitely try it.

One pattern I use is putting all the functions that generate HTML inside their own class. That way, I can more easily create and reuse components like:

  class Views:
      ...

  def comp1(self):
      return Div(self.header(), P("too"))
Then `self.header()` can be reused in other parts, or to return partial HTML. It also makes it easy to pass the "request" object to the class, and do conditional rendering based on it (cookies, auth, language, etc).

[0]: https://htpy.dev/


Yes htpy is nice! Other interesting examples of functional HTML include Elm-html (Elm), hiccl (Common Lisp), hiccup (Clojure), Falco.Markup (F#), Lucid (Haskell), and dream-html (OCaml). FastHTML's system, called "FastTag" (FT) is a bit of a mashup of all of them plus some extra bits. I seriously considered just using htpy actually -- but in the end decided I preferred something a little different.

I've wondered about a class-based approach like that -- interesting to hear it's worked for you. I should try it! I'm using a purely functional approach for re-use, as you see in this example of the code for about.fastht.ml:

https://github.com/AnswerDotAI/fh-about/blob/main/overview.p...


Thanks for making FastHTML, it is great to see more Python tooling that embraces Python for generating HTML.

What made you build FastTag instead of going with htpy? I am the author of htpy and any feedback would be very welcome!


For what it's worth. One thing I really like about `htpy` is that the element attributes go before the child elements. I find this easier to write and read. Other things I like:

Having child elements as a list (i.e: the __getitem__ override) makes it convenient to build elements based on simple conditions + list comprehensions. This can be done with other frameworks, but it seems more natural to me when using `htpy`.

I also like that you can just `print()` elements and get the final HTML without having to pass it through a different function. This is not something specific about FastHTML, but rather something I've found I also had to do when using `lxml` or similar tools (I wrote about my experiments here[0])

[0]: https://ricardoanderegg.com/posts/python-build-html-componen...


I wrote a few things with each of FT and htpy, and looked at the resulting code -- I felt like the htpy approach was slightly less neat personally. htpy has the benefit that '.' and '#' can have special meanings, but the downside of needing to use both __getitem__ and __call__. I didn't feel like that was a tradeoff I wanted to make. I actually originally wrote FT for a different purpose (XML for language model input) so id and class attributes weren't of any interest at all at that time!

Also, I was able to implement FT using just 2 lines of code -- it felt like a very natural data structure that was a good fit with Python.

Having said all that, I think htpy is really nifty and elegant. :D


Thanks, that makes sense! :)


FastHTML is very interesting and reading this thread has led me to discover htpy as well which I am shocked I have never seen before! The htpy website and docs are also great. So now I am a bit of a dilemma over which one to use.

I actually hate working in HTML with all those closing tags etc so I nearly always set up a build/make process to edit my templates in PUG format. When I paste my PUG->html output into https://h2x.answer.ai/, or run html2htpy over them, I get python code that basically looks the same as those PUG templates. What a realization that is! So I may as well create and edit them in python rather than PUG and exploit the power of my beloved python dev environment and tools (as nicely stated in that "Throw out your templates" essay https://github.com/tavisrudd/throw_out_your_templates reference from the htpy docs). Thanks very much Jeremy and Andreas for this fantastic insight :)


This is wonderful, I love it. I recently wrote a blog post about generating HTML in Python using LXML (https://ricardoanderegg.com/posts/python-build-html-componen...) and I didn't know about your library. I'll update it to add a mention to it.

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.


I think these kinds of tools are awesome, although I normally prefer not having to rely on getting the functions/classes/types defined for me, I usually find it easier to just use lxml[0] to build HTML components from composable Python functions[1], similar to React.

[0]: https://lxml.de/lxmlhtml.html#creating-html-with-the-e-facto...

[1]: https://ricardoanderegg.com/posts/python-build-html-componen...


I also built something similar for Python [0]. I decided to go with a separate DB file for the queue, I think it's better for performance but also to avoid mixing the data storage with the message queue (in my case, I assume the message queue is just an implementation detail and not really tied to the rest of the DB schema).

[0]: https://github.com/litements/litequeue


Yeah, that’s a good idea! I was today old when I learned that SQLite can have transactions across separate db files.


Only if you don't use WAL.


Ooooooooh. TIL!


No problem, I totally understand! Thanks for the clarification.


I kind of agree with the post. For me, this technique almost starts overlapping with literate programming[0].

One trick I use[1] is adding certain characters before the section name. e.g:

  ¡¡ settings
This makes searching and jumping between sections a lot easier.

[0]: https://en.wikipedia.org/wiki/Literate_programming

[1]: https://ricardoanderegg.com/posts/write-apps-in-single-file/


The Emacs version of this is using ^L (the ASCII form feed character), which lets you jump around using Emacs's built in page navigation commands.

I've used this occasionally in Emacs Lisp code and it's pretty nice, but I've never done it in other languages because I suspect that non-Emacs-users won't be able to handle it :P

The Emacs Wiki has a whole page on this: https://www.emacswiki.org/emacs/PageBreaks


The technical details in the README are quite an interesting read:

https://github.com/mozilla-Ocho/llamafile#technical-details


A bit unrelated, but one thing I absolutely love is the fact you can install it by copying a single file to a folder in your PATH. I have been trying to follow this approach for my Python scripts (standard library only, everything in one file) and I really enjoy the experience. Most of the features I need only require Python 3.8, and Ubuntu comes with Python pre-installed, so

  rsync
  chmod +x
"just works".


You can use shiv to make any script with deps a single file


Yes, I've used shiv and PEX in the past, and I love those. But that means "adding a build step". Also, as far as I understand, by default, those tools normally decompress the generated zip file on startup, but the decompressed artefacts have to be manually cleaned.

I think they are worth for more complex apps (I actually have a draft blog post with some experiments I did using PEX + gunicorn), but for my use case, it's not worth the effort when I only need the standard library.


You can try https://github.com/jaraco/pip-run or https://github.com/PyAr/fades (or eventually pipx: https://github.com/pypa/pipx/issues/913). They don't require a build step.

For example,

  #! /usr/bin/env -S pip-run Jinja2==3.*
  
  from jinja2 import Environment
  
  env = Environment(autoescape=True)
  template = env.from_string("Hello, {{ name }}!")
  print(template.render(name="world"))
There is a downside. Because pip-run recreates the virtualenv every time, the script takes a second to start up. pipx will cache virtualenvs once the single-file script feature is released. I haven't used fades yet.

Edit: fades caches virtualenvs.

  #! /usr/bin/env fades

  from jinja2 import Environment  # fades Jinja2==3.*

  env = Environment(autoescape=True)
  template = env.from_string("Hello, {{ name }}!")
  print(template.render(name="world"))


If you do that, you may want to know about this magic to test code that's in a single file Python CLI program: https://linsomniac.com/post/2023-03-21-python_testing_a_cli_...

I like to set up at least some tests on my scripts so that I can reduce the number of times I push something out that is obviously broken. pre-commit can also help with preventing shipping things with syntax errors if you enable the "ast" check, which does a simple syntax check on the code.


Some time ago, I wrote a queue using SQLite[0]. Instead of SKIP LOCKED, you can use RETURNING to lock-and-read a message and ensure only one worker is going to pick it up:

  UPDATE ... SET status = 'locked' ... RETURNING message_id

Or you can just use an IMMEDIATE transaction, SELECT the next message ID to retrieve, and UPDATE the row.

On top of that, if you want to be extra safe, you can do:

  UPDATE Queue SET status = 'locked' WHERE status = 'ready' AND message_id = '....'

To make sure you that the message you are trying to retrieve hasn't been locked already by another worker.

[0]: https://github.com/litements/litequeue/

[1]: https://github.com/litements/litequeue/blob/3fece7aa9e9a31e4...


I really like Bitwarden, but I found it buggy in terms of user experience, and I'm now considering switching. Some annoying bugs I've experienced:

* Desktop app not focusing correctly when opening it

* Browser extension asking to save password even if it's already there

* Log-In suggestions not showing

* Slow and laggy on iOS

* Biometrics log-in breaks half of the times I try to use it

I like the fact it's open-source, although that doesn't make it more secure, and I don't think I would like to self-host.


If I create a new login on mobile it never syncs to the vault/desktop correctly. I have so many empty vault items that then sync back to mobile. Nothing seems to fix it.


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: