
Why should bottlepy stick to a file? - Al0neStar
https://github.com/bottlepy/bottle/issues/1158
======
fermigier
I've learned recently, to my astonishment (I've been using Flask for the last
10 years and Bottle briefly before) that both names are puns on the WSGI
acronym (which is pronounced "woos-ghee", and sounds similar to "whisky").

In Flask's case, it's indirect, as Armin admitted in the 2011 presentation
([http://mitsuhiko.pocoo.org/flask-
pycon-2011.pdf](http://mitsuhiko.pocoo.org/flask-pycon-2011.pdf)): "Wordplay
on Bottle, probably a mistake".

------
hermanradtke
The (admittedly biased) history of bottle and flask from the author was
fascinating
[https://github.com/bottlepy/bottle/issues/1158#issuecomment-...](https://github.com/bottlepy/bottle/issues/1158#issuecomment-526602488)

~~~
fermigier
I think one of the most important technical statements in this comment is
this: "even the same design errors (global request/response objects)".

(Note to outsiders: we're not talking globals in the usual sense here, they
are very carefully crafted proxies on top of thread-locals that behave like
globals, i.e. can be imported at module level, but behave correctly in terms
of concurrency.)

As a long-time Flask developer, this is the biggest source of frustration when
trying to do anything "frameworky" around Flask (see
[http://mitsuhiko.pocoo.org/AdvFlaskPatterns.pdf](http://mitsuhiko.pocoo.org/AdvFlaskPatterns.pdf)
for some technical explanations).

There are a lot of emerging web frameworks in Python-land these last years,
hopefully some of them will get the success they deserve.

To displace the current dominant players (arguably: Django, Flask and
Pyramid), one needs a paradigm shift, just as Django displaced the dominant
player (Zope) in 2005 when "MVC frameworks" became the dominant paradigm, or
when Flask and Bottle rode the "microframeworks" wave in 2010.

This current paradigm shift is probably spelled ASGI. For more read
[https://dev.to/florimondmanca/introduction-to-asgi-
emergence...](https://dev.to/florimondmanca/introduction-to-asgi-emergence-of-
an-async-python-web-ecosystem-38oi)

BTW: here's a fresh take from a friend who did exactly that last year:
[https://florimond.dev/blog/articles/2018/12/how-i-built-a-
we...](https://florimond.dev/blog/articles/2018/12/how-i-built-a-web-
framework-and-became-an-open-source-maintainer/) (with limited success,
despite a lot of talent and dedication).

I'm curious to know which of the "new" (ASGI or not) Python frameworks are
using these days.

~~~
rapfaria
FastAPI -
[https://github.com/tiangolo/fastapi](https://github.com/tiangolo/fastapi)

------
timeattack
I don't quite get those arguments [1]:

> The single-file restriction is an effective save-guard against feature
> creep. [...] We are able reject pull requests simply because they would add
> too many lines or add too much complexity, and that's nice.

bottle.py is already 4425 lines of code [2].

So, despite they say they reject pull requests because they add too many
lines, it's also can be said that they reject pull requests because file is
too big already and merging PR makes it even harder to maintain.

> A single file is easier to debug and understand. [...] You can read the
> entirety of bottle.py in an hour or so. Most of it is pretty easy to
> understand. Try that with flask/pyramid/django [...]

Why not at least to just split it to 1 section per 1 file, which they emulate
right now by using section-delimiting comments [3]? Will it make code much
harder to understand? Or will it make code easier to understand?

Also, from a developer perspective, what about navigation to the correct
section quickly. E.g. if developer wants to add a new Exception, how he can
efficiently go to the specific section? If it wouldn't be single file,
developer could jump to required file by typing couple of letters in fuzzy
file opener it his editor of choice.

No one forces developers plunge into a feature creep apart from their own, and
it actually doesn't matter is there a single file or not. Some prefer to write
as many code as they can in one single line. I doubt that they do it to ease
maintenance and to avoid feature creep.

[1]:
[https://github.com/bottlepy/bottle/issues/1158#issuecomment-...](https://github.com/bottlepy/bottle/issues/1158#issuecomment-526602488)
[2]:
[https://github.com/bottlepy/bottle/blob/master/bottle.py](https://github.com/bottlepy/bottle/blob/master/bottle.py)
[3]:
[https://github.com/bottlepy/bottle/blob/332215b2b1b3de5a321b...](https://github.com/bottlepy/bottle/blob/332215b2b1b3de5a321ba9f3497777fc93662893/bottle.py#L262-L264)

~~~
DougBTX
I recon it is similar to the desire for header-only C libs, package management
isn’t great, so single files are easier to deal with. I wonder if anyone has
made a Python package manager based on the idea of bundling up all the files
in a Python project into a single .py file that can just be downloaded and
copied around as-is? No requirements.txt, just vendored .py files in a libs
dir.

~~~
asveikau
Header only c libs also emerge from c and c++ realities that are less true
elsewhere.

The biggest one is the optimizer does a better job when stuff is in the same
compilation unit. (Inlining and stuff.) A good example to look at is sqlite,
which is developed as multiple files but they concatenate them together for
distribution.

Another reason is a C++ one, and C is not C++ but the community has enough
overlap that I think it tends to spill over. And that is templates. Templates
need to be in the header so a lot of c++ libraries have complicated, slow to
compile headers.

I do think, for example when any given person on HN links to a header only C
or C++ library, that a lot of these people are overdoing it. They have likely
not measured it and found it to be better like the sqlite people. They are
fighting, perhaps even misunderstanding the tooling, at the cost of compile
times and large binary sizes. And for what? Makefiles are not really that hard
to write.

------
QuadrupleA
Title is confusing, maybe "keep to a single file"? Had to read a while before
I realized it wasn't some kind of file-clinging code barnacle.

First I've heard about bottle, I'm intrigued - as a longtime Flask user who is
occasionally frustrated with its OOP-zeal it might be up my alley.

------
dlbucci
Back in college, Bottle was where I started to understand how web servers
worked. I had written PHP but had zero idea how it worked (or how to write it)
and eventually I tried Django, but couldn't configure it to send static files.
Once I tried bottle though, it just clicked. It was so simple and I loved that
there was little magic. It being a single file that I just dragged into a
folder was a BIG part of the simplicity for me, too. I mostly write nodejs
these days (express sorta scratches the same itch), but I have fond memories
of bottle. In fact, my old personal site
([http://www.superftc.com/](http://www.superftc.com/)) is still a bottle app
to this day.

~~~
moreira
Wow the comic is great I’ve just spent so long enjoying it. Thank you so much!
How come you’ve stopped doing it?

~~~
dlbucci
Well thank you for the compliment, I'm glad you enjoyed it! I can't really
recall why I stopped, but I think I just got tired of doing it and no one was
reading it. It was just hard to make time for it I guess :/

------
oefrha
I have a relatively popular single-Python-file, no-dependency-beyond-PSL
project myself (slightly smaller than bottle.py, ~3.5k lines). It would
certainly be shorter and easier to develop if I added a bunch of dependencies,
but I’ve resisted the urge over time, and being able to just download a single
file and use it with any Python installation that’s less than five years old
is ultimately worth the effort IMO. Also got me closer to some lower level PSL
modules.

~~~
guggle
Care to share what lib it is ?

------
rs23296008n1
Bottlepy is like one of those plucky little mammals living amongst giants. It
is nimble and quick with small footprints.

I use it for quickly deploying endpoints for specific tasks such as a
miniscule CDN and the obligatory blog. Great for home automation as well.

------
umvi
I don't blame him. I made the same design choice for my python library[1]. I
also don't use any non-native libraries in the single file. I'm sorry to say
it's a much better experience for users if they don't have to deal with pip if
they don't want to. I myself have been burned many times trying to install
some python module for hours and failing because of some dependency issue.

[1]
[https://github.com/RPGillespie6/fastcov](https://github.com/RPGillespie6/fastcov)

------
INTPenis
Bottle was my 3rd python web framework that I tried and I really liked the
idea.

But then I learned about Flask blueprints and now I can't live without them.

Imagine that I can make a lambda function that only loads selective
blueprints, selective parts of my API, into AWS.

That's very powerful for off loading bottlenecks.

Is it possible to do blueprints with bottle? Because seeing as they've
cemented the single file policy, it would make sense to have some sort of
plugin system.

~~~
rambojazz
Bottle doesn't have "blueprints" but I've used it for something like that in
the past. IIRC you can "mount" another bottle app object into a URL path [1].

[1]
[https://bottlepy.org/docs/dev/api.html#bottle.Bottle.mount](https://bottlepy.org/docs/dev/api.html#bottle.Bottle.mount)

~~~
e12e
Hm:

> app – an _instance_ of Bottle or a WSGI application.

So, that means a require to load the code, and the some kind of app = my
app.default_app()?

Do you have an example of how you create an instance of a wsgi app? For bottle
it looks like that's normally something bottle.run() does?

~~~
rambojazz
wsgi is an interface; if you implement it then you are compatible. The
simplest way is to just use another Bottle app, or maybe subclass the Bottle
class like this `class MyClass (Bottle):`, otherwise you can create a new
class by implementing the interface methods (many tutorials online for this).
Then you "mount" that wsgi object into a specific path using the `.mount()`
function of your main Bottle app.

~~~
e12e
Right, I get that part (sort of).

But looking at the quickstart, you typically don't subclass Bottle, you
require it, and call run()? So your typical bottle app won't be a subclass of
bottle? At which point you can't make an instance of it, to pass to mount()?

Looking at the documentation for mount() it looks like you should be able to
mount the quick-start app - but it's not clear of you create an instance to
pass to mount?

That is if I have example.py:

    
    
      from bottle import route, run, template
    
      @route('/hello/<name>')
      def index(name):
        return template('<b>Hello {{name}}</b>!', name=name)
    
      run(host='localhost', port=8080)
    

How do I write example_mount.py that mounts example.py under /hello1 and
/hello2?

~~~
rambojazz

        test.py
        =======
        import bottle
        
        @bottle.get('/')
        def index():
            return 'INDEX'
        
        # a new wsgi app
        hello = bottle.Bottle()
        
        @hello.get('/<name>')
        def greet(name):
            return 'Hello, ' + name
        
        bottle.app().mount('/hello', hello)
    

Test like this: python3 -m bottle --bind :8080 test

You should have 2 working routes, "/" and "/hello/<name>".

~~~
e12e
Thanks, that makes sense.

But I'm still not clear on how I require "example.py" and mount it (ie how do
I make an wsgi instance from a python file that is a bottle app)?

------
rcarmo
I love Bottle because it’s a single file and I can read through all of it to
(re)understand it. It has all the essentials and nearly zero cruft, and I’ve
been using it for ages - it runs my personal website, many API servers I
wrote, and the odd minimal system daemon[0].

I rather like the single-file approach for small, focused software -
imapbackup[1] and piku[2] were written to be easy to deploy and customize, and
there certainly is a place for drop-in-and-forget libraries like bottle (I
wish there were more like it, in other fields).

[0]:
[https://github.com/rcarmo/azure-k3s-cluster/blob/master/clou...](https://github.com/rcarmo/azure-k3s-cluster/blob/master/cloud-
config/master.yml#L15)

[1]:
[https://github.com/rcarmo/imapbackup](https://github.com/rcarmo/imapbackup)

[2]: [https://github.com/piku](https://github.com/piku)

------
kerkeslager
Kudos to Bottle for keeping their code simple and auditable. I've been writing
small projects in Bottle for a while.

------
mirkonasato
I never used Bottle and don't doubt the comments saying it's a great
framework, but I'm more interested on the "single file" approach in general.

bottle.py may be a single file but it's 4,425 lines long. That doesn't strike
me as particularly simple. Any project could potentially be "a single file" if
it's a very long file.

Why would splitting it into a few separate files make it more complex? There's
a reason why every modern programming language supports modules, and that's
precisely to break down complexity into smaller, simpler units.

~~~
fock
because it's extremely easy to "deploy" on old-fashioned cgi-hosts. You need a
python-cgi configured, drop 2 files with ftp and your app is up and running.
Also auditing is a lot easier (and you only have to trust a handful of
maintainers).

~~~
kingosticks
Why does a single file mean you only have a trust a handful of maintainers?
Each line of the ~4k lines could have been written by a different person and
merged by a different maintainer.

~~~
fock
because generally projects like these have some opinionated person who is
maintaining it, which actively blocks "get the ssh-keys"-patches. compare that
to the 100+ dependencies of your typical node-package.

~~~
kingosticks
Having many dependencies can be a problem. And the node ecosystem is a total
joke.

But if the question was why not split it up into smaller files (which it was),
then that's nothing to do with the number of dependencies or the number
maintainers. My point is that auditing a program with no external dependences
doesn't get any easier if the code is contained in one file or across ten.

------
jrockway
Arguments like this are why I gave up on scripting languages. "go build"
generates one statically-linked binary that you can send anywhere. You can
build a Windows binary from your Raspberry Pi or vice-versa. It is how
everything should work, because distributing (and cross-compiling) is
something you should have to spend 0 time thinking about. Now I spend 0 time
thinking about it.

~~~
gridlockd
The argument has pretty much nothing to do with dynamic languages.

Single file dependencies are getting more and more popular because integrating
with build systems is a major pain, particularly in _static_ languages like C
and C++, but also in the mess that is modern Javascript.

Some languages do manage to do this better than others, but the type system
has nothing to do with it.

Lastly, having one single statically-linked binary isn't the norm even for
static languages.

~~~
jrockway
I'm just saying that's why I stopped using them, not that it's a
differentiating factor between static and dynamic languages. I wrote books
about Perl, I taught classes on Perl. Having to install stuff from CPAN was
probably the most common complaint. I wrote a new CPAN client at some point,
and it didn't help. Enough was enough; the approach failed and I moved on to
writing my code some other way (as did all of those students, probably). npm
seems to have gotten something that mostly works; I use that and tolerate it
because I have no other choice for client-side stuff, but I don't really do
enough browser programming to call myself a Javascript user.

Yes, static languages have dependency hell. I've ./configure'd a million times
and have wanted to die about 80% of the time. I've used buildroot and have
wanted to die 100% of the time. I never seriously programmed in C or C++ to
say that I've stopped using them, so while they're broken... you can't stop
using something that you haven't started using. (I still cringe when I see
that some C-based project I want to use doesn't have binaries. It's especially
annoying because I mostly use Windows now. I want "dc" in Powershell because
that is my preferred calculator app... but can't compile it, and apparently
neither can anyone else. Wow.)

Python is a pretty great programming language... but it's just not worth the
tradeoffs. It is nice that bottle is one file; that is going to increase its
adoption. It is not a solution to any general problem, though; the next
library you depend on is going to require virtualenv and 100 dependencies, and
they will fail to install.

~~~
gridlockd
I guess we're on the same page then and I wouldn't have written the reply had
you been more specific.

Having said that, I do find the situation with Python/PIP/PyPI today is far
better than a few years ago, especially concerning binary packages and the
Python2/Python3 divide.

I'm not experiencing a substantial quality difference between pip and npm, at
the same time I prefer venv over node_modules (while detesting both).

------
jc_sec
Huge fan of bottle. I use it for a lot of things.

------
Too
If this is a design decision for a library it means the package and
distribution manager for the language is broken.

~~~
andybak
Which package manager do you regard as non-broken?

------
sandGorgon
The singlefile-ness of Bottle is no longer unique. For example, the modern day
Fastapi built on async ASGI (and ranks top in framework benchmarks) is also
single file.

[https://github.com/tiangolo/fastapi](https://github.com/tiangolo/fastapi)

or Fastapi for that matter -
[https://github.com/encode/starlette](https://github.com/encode/starlette)

However, it is a bit tricky to run Bottle with production grade servers like
gunicorn/uwsgi.

~~~
oefrha
What? FastAPI is certainly not single file:
[https://github.com/tiangolo/fastapi/tree/master/fastapi](https://github.com/tiangolo/fastapi/tree/master/fastapi)

The issue is talking about Bottle the framework itself being a single file,
bottle.py.

~~~
sandGorgon
oh my bad. That's interesting, but im not sure about utility.

Because everyone literally uses package management to install and maintain
bottle.

Bottle has code like this -
[https://github.com/bottlepy/bottle/blob/master/bottle.py#L81...](https://github.com/bottlepy/bottle/blob/master/bottle.py#L81-L84)
\- which does check for external dependencies (which predicate a package
manager)

~~~
jwkane
not really.. it uses ujson if it is installed and falls back to the built in
json. Hard to call something a dependency when you don't depend on it.

~~~
rejschaap
Those are often called optional dependencies. Which is of course an oxymoron.

