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:

So... cheers to Bottle!