My new blog engine!
It’s about time a get a blog going for myself. Well… Here it is!
Since at least 1997, I’ve been dabbling with software generating HTML from other formats. Today, being the “pipeline person” I am, I enjoy a decent static site generator when I see one.
Besides a pipeline person, I’m also a software developer. Retired by now, but you get the idea. I sometimes entertain an idea how to enhance stuff with a bit of home-grown code. So a plugin-enticing site generator is what I want.
A few years ago, I switched to Python as my main language for small private projects. For two previous sites, I had been using Pelican. But now, it is:
Good bye to Pelican!
Why?
The plugin interface problem
The interface to a Pelican plugin is not too well-specified in the pertinent documentation. I had to find out quite a bit of stuff via trial and error. In the end, I managed to write plugins that did what I intended them to do. But I remained unsure whether they would continue to function flawlessly in the future, with new Pelican versions. Was I using stable API or ephemeral implementation detail? The uncertainty didn’t feel good.
Broken error handling
Then came the day when I was starting yet another plugin. I coded the initial version to simply raise some error. To my horror, Pelican just went ahead and built the site as if nothing were wrong.
Now these two ideas are important to me:
- Fail early
- Once a failure is detected, processing should stop, lest later processing tries to feed on state that earlier processing failed to provide.
- Fail loudly
- When the result the user reasonably expects can not be produced by the software, the software should confess that error (e.g., by a non-zero exit value).
Some experimentation and study of code turned out Pelican follows neither of those two ideas. Instead:
- It specifically contains “fail late” functionality. Errors are caught and logged and otherwise ignored. To remember the error becomes the responsibility of the logger object. To me, that’s a 🤦.
-
Pelican fails silently. Errors are ignored out of the box. The
pelican
CLI happily reports “no error” via its exit value. Doesn’t matter whether the HTML generated is or isn’t what the user wanted. 🤦 - One has to specifically provide
--fatal errors
topelican
to get “fail loudly”. (In passing: I have not found out how to do--fatal errors
via the API used bytasks.py
. Didn’t find it in the documentation, and didn’t look very hard in the code.) Software that wants me to like it works the other way around: Fail loudly by default, and it may provide a special command line switch to suppress error handling. Though I’m unlikely to use it. - I duly reported the silent failure as a bug, and provided a fix (that reads the logger info and fails cleanly on error). The Pelican maintainer seriously argued “Read the log to find out whether something is wrong”. Two months after my initial bug report, there is no indication “silent failure by default” is seen as a problem worthy of a fix.
High time to move on.
Nikola - you’re next!
After a few hours of research, I found Nikola and decided to try liking it.
- Architecture seems cleaner.
- Documentation apparently comprehensive.
- Auto-reloading actually seems to work - never had that working under Pelican.
- Build on top of the task runner Doit, which I also like - seems to be an improvement over the straightforward invoke I’ve been using.
- I also investigated Nikola’s issue tracker briefly and found their issue handling reasonable.
Other choices investigated
I also looked into other static page generators powered by Python:
- blag Too simple. No plugin support I could find.
- Hyde “Currently hyde is only supported on python 2.7.x. Python 3.x support is in progress … .”
- makesite More a cooking recipe how to build a site generator yourself than a site generator.
-
Stapy Not well documented. Somehow I had
a funny feeling when investigating this one. Also, part of the text
of the repo’s
README
is actually a screen shot. - Tinkerer declares itself dead and recommends Baku in its will.
- Baku uses its own, home-grown templating engine, “to avoid dependencies”. – To paraphrase a well-known saying: “It is easy to remain small standing on no-ones shoulders.”
- Lektor came in close second. I mostly chose Nikola over Lektor as I was impressed by Doit and like the prospect of partial and parallel rebuilds.
- I also happened to stumble over Staticman. This isn’t a static site generator, but an interesting approach at a home-grown system for users to leave comments: There is no database, instead, a git repo is used. Not sure I consider that approach viable, but it is interesting.
Nikola’s peculiarities
Out of the box, Nikola produces a tree with relative links only. So
if you write ![foo](/images/bar.png)
, what you get might actually be
<img alt="foo" src="../../images/bar.png">
depending on the level of
the file that’s generated from your markdown.
I applaud that idea. It means you give an archive of your output
folder to a friend and have them point a browser at it, and it will
just work. Without a server.
But it can lead to confusion. For example, if you have a reference to something on your own server outside the material that Nikola generates.
This can be changed by setting a configuration variable URL_TYPE =
"full_path"
. This produces src="/blog/images/bar.png"
. So far, so
good. Another configuration variable can be used to configure a
non-root URI, under which the blog lives. This blog does this, via
SITE_URL = "https://dj3ei.famsik.de/blog/"
. If you do both, the
build in development server no longer works. It serves the files below
http://localhost:8000/
, not http://localhost:8000/blog/
. This
breaks links.
2024-12-21 addition: I fixed this and offered my fix as a
PR to the Nikola
project. Of course, I’m also using my own fix. So
nikola serve
works for me.