Python Bottle Lightweight Web Framework

Written by

in

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.py file or environment variables.
  • Use a WSGI server: While Bottle’s built‑in server is great for development, deploy with gunicorn, uwsgi, or waitress for better performance and reliability.
  • Enable logging: Configure the logging module 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, and Strict‑Transport‑Security via the response object.
  • Cache static assets: Use Cache‑Control headers 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:

  1. Human‑readable URLs: Use descriptive route patterns (e.g., /blog/<slug>) instead of numeric IDs.
  2. Meta tags and Open Graph: Include <title>, <meta name="description">, and OG tags in your templates.
  3. Sitemap generation: Provide a dynamic /sitemap.xml route that lists all public URLs.
  4. Robots.txt: Serve a static robots.txt file from the /static folder or via a dedicated route.
  5. 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=False in 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 cerberus or pydantic to validate JSON payloads before processing.
  • Serving large static files directly: For big media assets, consider a dedicated static server (Nginx

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *