Genie in a Bottle
Imagine you work very hard on something just to discover it has already been done, except way better.
Haha. Just imagine. Hum...
So... it seems that I was just reinventing Bottle, but worse.
To my defense, it's more like "I had no idea Bottle had streaming capabilities". But then a user named ahmedlemine just came and made a Datastar <-> Bottle adapter. What a Chad.
However, I won't go down without a fight, so let's explore the differences between Bottle and Red Dwarf.
What Bottle does better
A lot of things obviously. Bottle is way more advanced and handles stuff like file uploads, has a test framework and a cool error page...
The Bottle code is very pleasant to read. Cool docstrings while not being overly typed.
Speaking of typing, they do typed dynamic routes (like
doc_id:int on the routes).
Overall, Bottle tries to be very close to the spec (with a careful
consideration of possible response types, correct handling of trailing
slashes in URLs...) while maintaining broad options (for example, it
will handle different encodings, whereas Red Dwarf will tell you to
scram if you don't use utf-8).
They also use a lot of cached @property to skip unecessary
computing or raise informative errors. I don't use them because I find
them unwieldy (just give me the damn dict) but Adam from
Stario
said it's a good thing so I'm putting it in the bonus points.
The oddity of Bottle is putting everything in environ for
reasons I don't understand. Is it to have a correct behavior in a
multithreaded environment as the use of
threading.local() suggests? Is it for spec compliance, or
code readability? I can't tell.
Oh, and they do middlewares.
What Red Dwarf does different
The obvious one: everything in Red Dwarf is async. That was one of the main motivations, because async behavior plays nicely with Datastar.
Turns out Bottle can do streaming generators just fine, and even get to
async behaviors using gevent (like Flask). This nonetheless
leads to different code.
Another big one: capabilities. Red Dwarf doesn't implement a lot of stuff (like JSON responses, after_response hooks...) because it assumes Datastar and "YAGNI". That's by design.
For the same reason, Red Dwarf doesn't do "no return value from handler
-> empty response" because I want empty() to be explicitly
written as a better teaching tool.
While I like Bottle's templating engine, Red Dwarf doesn't ship with one, as 1. This is still an open problem and 2. People like Jinja (not me, I prefer Stario's way).
Other small stuff: I don't do static mounting (can't understand the appeal, protect from path traversal maybe?) and I use secure cookies by default instead of the spec behavior.
And I don't do CLI arguments. Who does that? You write an app, call it there.
Where we agree
We very much align in philosophy: no dependencies for example, that's always sexy.
We also agree to defer as much stuff as possible: defer the compression to the reverse proxy, defer the session handling (they recommend beaker, which I didn't know about, but some people might prefer a row in a SQLite database for example), and most importantly, defer glue code to the user (teach these "Claudependents" a lesson).
Bottom line
Bottle is very small for what it does: only 5k lines of commented code.
I'll be honest: it was a bit of a bummer. Had I found it beforehand, I never would have committed to writing Red Dwarf.
But in the end, it's a good thing:
-
Reading their code allowed me to fix some bugs in mine.
-
It forced me to define a more precise goal: I want Red Dwarf to be
reference
tutorial
for Datastar + Python. Not the fastest micro framework, not the most
complete, not the smallest in terms of lines of code, but the
smallest
in terms of documentation.
So... cheers to Bottle!