Why should bottlepy stick to a file?
github.com
github.com
In Flask's case, it's indirect, as Armin admitted in the 2011 presentation (http://mitsuhiko.pocoo.org/flask-pycon-2011.pdf): "Wordplay on Bottle, probably a mistake".
(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 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...
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... (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.
starlette.io/
> 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-... [2]: https://github.com/bottlepy/bottle/blob/master/bottle.py [3]: https://github.com/bottlepy/bottle/blob/332215b2b1b3de5a321b...
Have you noticed how bloated the other web frameworks are?
Bottle avoided feature creep very well and it even provides one of the fastest templating engines around.
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.
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.
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.
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.
[1] https://bottlepy.org/docs/dev/api.html#bottle.Bottle.mount
> 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?
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? 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 testYou should have 2 working routes, "/" and "/hello/<name>".
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)?
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...
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.
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.
All the other reasons I heard, I don't really see what they have to do with having a single or multiple files.
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.
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.
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).
https://github.com/tiangolo/fastapi
or Fastapi for that matter - https://github.com/encode/starlette
However, it is a bit tricky to run Bottle with production grade servers like gunicorn/uwsgi.
The issue is talking about Bottle the framework itself being a single file, bottle.py.
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... - which does check for external dependencies (which predicate a package manager)
> However, it is a bit tricky to run Bottle with production grade servers like gunicorn/uwsgi.
Uh, no? Bottle has docs for deployment: https://bottlepy.org/docs/dev/deployment.html#switching-the-...
They have automated support for gunicorn. uWSGI requires adding one line (`app = bottle.default_app()`) and pointing uWSGI at that.