Looking for a fast, simple, and truly lightweight way to turn your Python scripts into web applications? Meet Bottle – the minimalist web framework that lets you build everything from a quick prototype to a production‑ready API with just a single file. In this guide we’ll explore what makes Bottle unique, walk through a basic “Hello, World!” app, dive into routing, templates, plugins, and deployment options, and give you practical tips to keep your code clean and SEO‑friendly.
What Is Bottle and Why Choose It?
Bottle is a micro‑framework for Python that follows the “single‑file” philosophy: the entire framework (including its core, routing engine, and built‑in server) fits into a single .py file with no external dependencies other than the Python standard library. This makes it ideal for:
- Rapid prototyping and proof‑of‑concept projects.
- Embedding a web interface into existing desktop or IoT applications.
- Learning the fundamentals of web development without the overhead of larger frameworks like Django or Flask.
Despite its tiny footprint, Bottle supports all the essential web features – routing, request/response handling, templating, static file serving, and a flexible plugin system – while staying fast and easy to understand.
Getting Started: Installation and First App
Installation
Because Bottle has no external dependencies, installation is a one‑liner:
pip install bottle
If you prefer to keep everything in a single file, you can even download the bottle.py source and place it alongside your script.
Hello, World! – Your First Bottle Application
Below is a minimal, fully functional Bottle app that listens on localhost:8080 and returns “Hello, World!” when you visit the root URL.
from bottle import route, run
@route('/')
def hello():
return 'Hello, World!'
run(host='localhost', port=8080, debug=True)
Save this as app.py and run python app.py. Open http://localhost:8080 in your browser to see the result.
Core Concepts and Features
1. Routing – Mapping URLs to Functions
Routing is the heart of any web framework. Bottle’s @route decorator lets you define URL patterns with placeholders for dynamic data:
@route('/user/<id:int>')
def show_user(id):
return f'User ID: {id}'
Supported converters include int, float, path, and custom regular expressions, giving you fine‑grained control over URL matching.
2. Request and Response Objects
Inside a route function you can access the global request and response objects to read query parameters, form data, headers, and set cookies or status codes:
from bottle import request, response
@route('/search')
def search():
q = request.query.q or 'none'
response.set_header('X-Search-Query', q)
return f'Search term: {q}'
3. Templating – Rendering HTML Dynamically
Bottle ships with a lightweight built‑in template engine that uses {% %} and {{ }} syntax. Create a file templates/hello.tpl:
<html>
<head><title>{{title}}</title></head>
<body>
<h1>Hello, {{name}}!</h1>
</body>
</html>
Render it from a route:
from bottle import template
@route('/greet/<name>')
def greet(name):
return template('hello', title='Greeting', name=name)
4. Static Files – Serving CSS, JS, and Images
Static assets are served with the static_file helper. A common pattern is to map a URL prefix to a directory:
from bottle import static_file
@route('/static/<filename:path>')
def serve_static(filename):
return static_file(filename, root='static')
This keeps your HTML clean and lets search engines index the content correctly.
5. Plugins – Extending Bottle’s Core
Bottle’s plugin architecture allows you to add features such as database connections, authentication, or caching without cluttering your route code. A popular plugin is bottle-sqlite for SQLite integration:
pip install bottle-sqlite
from bottle import Bottle, request
from bottle_sqlite import SQLitePlugin
app = Bottle()
app.install(SQLitePlugin(dbfile='app.db'))
@app.route('/users')
def list_users(db):
rows = db.execute('SELECT * FROM users')
return {'users': [dict(row) for row in rows]}
The db argument is injected automatically, keeping your functions tidy.
Best Practices for a Production‑Ready Bottle App
- Separate configuration: Store settings (host, port, debug mode) in a
config.pyfile or environment variables. - Use a WSGI server: While Bottle’s built‑in server is great for development, deploy with
gunicorn,uwsgi, orwaitressfor better performance and reliability. - Enable logging: Configure the
loggingmodule to capture request details and errors, which helps SEO tools crawl your site without hitting hidden bugs. - Apply security headers: Set
Content‑Security‑Policy,X‑Content‑Type‑Options, andStrict‑Transport‑Securityvia theresponseobject. - Cache static assets: Use
Cache‑Controlheaders or a CDN to reduce load times – a key factor for SEO rankings.
Example: Deploying with Gunicorn
Create an app.wsgi file that exposes the Bottle instance:
from bottle import Bottle, default_app
app = Bottle()
# ... define routes ...
application = default_app()
Then launch with:
gunicorn -w 4 -b 0.0.0.0:8000 app:application
This command starts four worker processes, handling concurrent requests efficiently.
SEO Considerations When Using Bottle
Search engine optimization isn’t just about keywords; it’s also about site performance, clean URLs, and proper markup. Here’s how to make a Bottle‑powered site SEO‑friendly:
- Human‑readable URLs: Use descriptive route patterns (e.g.,
/blog/<slug>) instead of numeric IDs. - Meta tags and Open Graph: Include
<title>,<meta name="description">, and OG tags in your templates. - Sitemap generation: Provide a dynamic
/sitemap.xmlroute that lists all public URLs. - Robots.txt: Serve a static
robots.txtfile from the/staticfolder or via a dedicated route. - Fast page loads: Leverage Bottle’s ability to serve compressed (gzip) responses by adding a simple middleware.
Sample Sitemap Route
@route('/sitemap.xml')
def sitemap():
urls = [
{'loc': 'https://example.com/', 'priority': 1.0},
{'loc': 'https://example.com/about', 'priority': 0.8},
# Add more URLs dynamically from a database if needed
]
xml = '\n'
xml += '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n'
for u in urls:
xml += f' <url>\n <loc>{u["loc"]}\n <priority>{u["priority"]}\n </url>\n'
xml += '</urlset>'
response.content_type = 'application/xml'
return xml
Common Pitfalls and How to Avoid Them
- Mixing development and production settings: Always keep
debug=Falsein production to prevent stack traces from leaking. - Blocking I/O in routes: Long‑running tasks (e.g., image processing) should be offloaded to background workers like Celery; otherwise they will block the single‑threaded Bottle server.
- Neglecting input validation: Use libraries such as
cerberusorpydanticto validate JSON payloads before processing. - Serving large static files directly: For big media assets, consider a dedicated static server (Nginx
Leave a Reply