Fast and simple WSGI micro web-framework for small web applications with no dependencies other than the Python Standard Library.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Plugin architecture for extending Bottle functionality with custom middleware, request/response processing, hooks, and built-in plugins for JSON handling and template rendering.
Install, uninstall, and manage plugins for applications.
def install(plugin):
"""
Install a plugin on the default application.
Parameters:
- plugin: plugin instance, class, or callable
"""
def uninstall(plugin):
"""
Uninstall plugins from the default application.
Parameters:
- plugin: plugin instance, class, type, or string name
"""
class Bottle:
def install(self, plugin):
"""
Install a plugin on this application.
Parameters:
- plugin: plugin instance, class, or callable
Returns:
plugin: installed plugin instance
"""
def uninstall(self, plugin):
"""
Uninstall plugins from this application.
Parameters:
- plugin: plugin instance, class, type, or string name
Returns:
list: list of uninstalled plugins
"""Usage:
from bottle import install, uninstall, Bottle
# Install plugin on default app
install(MyPlugin())
# Install by class (will instantiate)
install(MyPlugin)
# Uninstall by instance
plugin = MyPlugin()
install(plugin)
uninstall(plugin)
# Uninstall by type
uninstall(MyPlugin)
# Uninstall by name
uninstall('myplugin')
# Application-specific plugins
app = Bottle()
app.install(MyPlugin())
app.uninstall(MyPlugin)Register callbacks for application lifecycle events.
def hook(name):
"""
Hook decorator for the default application.
Parameters:
- name: str, hook name ('before_request', 'after_request', 'app_reset', 'config')
Returns:
function: decorator function
"""
class Bottle:
def hook(self, name):
"""Hook decorator for this application."""
def add_hook(self, name, func):
"""
Add hook callback directly.
Parameters:
- name: str, hook name
- func: callable, hook function
"""
def remove_hook(self, name, func):
"""
Remove hook callback.
Parameters:
- name: str, hook name
- func: callable, hook function to remove
Returns:
bool: True if hook was removed
"""
def trigger_hook(self, name, *args, **kwargs):
"""
Trigger hook callbacks.
Parameters:
- name: str, hook name
- *args: hook arguments
- **kwargs: hook keyword arguments
Returns:
list: list of hook return values
"""Available hooks:
before_request: Called before each request is processedafter_request: Called after each request is processedapp_reset: Called when application is resetconfig: Called when configuration changesUsage:
@hook('before_request')
def before_request():
print(f"Processing request: {request.method} {request.path}")
@hook('after_request')
def after_request():
print(f"Request completed: {response.status}")
@hook('config')
def config_changed(key, old_value, new_value):
print(f"Config {key} changed from {old_value} to {new_value}")
# Direct hook management
app = Bottle()
def log_request():
print(f"Request: {request.path}")
app.add_hook('before_request', log_request)
app.remove_hook('before_request', log_request)Create custom plugins by implementing the plugin interface.
class Plugin:
"""Base plugin interface."""
name = 'plugin' # Plugin name for identification
api = 2 # Plugin API version
def apply(self, callback, route):
"""
Apply plugin to route callback.
Parameters:
- callback: function, route callback function
- route: Route, route instance
Returns:
function: wrapped callback function
"""
def setup(self, app):
"""
Setup plugin when installed.
Parameters:
- app: Bottle, application instance
"""
def close(self):
"""Cleanup when plugin is uninstalled."""Simple plugin example:
class TimingPlugin:
name = 'timing'
api = 2
def apply(self, callback, route):
import time
def wrapper(*args, **kwargs):
start = time.time()
try:
return callback(*args, **kwargs)
finally:
duration = time.time() - start
print(f"Route {route.rule} took {duration:.3f}s")
return wrapper
# Install timing plugin
install(TimingPlugin())Bottle includes several built-in plugins for common functionality.
Automatic JSON serialization for dictionary return values.
class JSONPlugin:
name = 'json'
api = 2
def __init__(self, json_dumps=json.dumps):
"""
JSON serialization plugin.
Parameters:
- json_dumps: function, JSON serialization function
"""
def apply(self, callback, route):
"""Apply JSON serialization to route."""Configuration:
import json
from bottle import JSONPlugin, install
# Custom JSON encoder
def custom_dumps(obj):
return json.dumps(obj, indent=2, sort_keys=True)
# Install with custom encoder
install(JSONPlugin(json_dumps=custom_dumps))
# Configure via app config
app.config['json.enable'] = True
app.config['json.ascii_only'] = False
app.config['json.dumps'] = custom_dumpsUsage:
@route('/api/data')
def api_data():
# Automatically serialized to JSON
return {
'status': 'success',
'data': ['item1', 'item2', 'item3'],
'count': 3
}Automatic template rendering for dictionary return values.
class TemplatePlugin:
name = 'template'
api = 2
def apply(self, callback, route):
"""Apply template rendering to route."""Usage with template decorator:
@route('/user/<id:int>')
@view('user.html')
def show_user(id):
# Return dict is passed to template
return {
'user': get_user(id),
'title': f'User {id}'
}Configure plugin behavior and route-specific settings.
@route('/api/raw', apply=['json']) # Only apply json plugin
def raw_api():
return {'raw': True}
@route('/api/skip', skip=['json']) # Skip json plugin
def skip_json():
return "Raw string response"
@route('/template', template='page.html') # Template plugin config
def templated():
return {'content': 'Hello World'}Control plugin execution order:
class HighPriorityPlugin:
name = 'high'
api = 2
def apply(self, callback, route):
def wrapper(*args, **kwargs):
print("Before high priority")
result = callback(*args, **kwargs)
print("After high priority")
return result
return wrapper
class LowPriorityPlugin:
name = 'low'
api = 2
def apply(self, callback, route):
def wrapper(*args, **kwargs):
print("Before low priority")
result = callback(*args, **kwargs)
print("After low priority")
return result
return wrapper
# Install in order (first installed wraps innermost)
install(LowPriorityPlugin())
install(HighPriorityPlugin())Plugins that provide request context:
class DatabasePlugin:
name = 'db'
api = 2
def __init__(self, database_url):
self.database_url = database_url
self.pool = None
def setup(self, app):
self.pool = create_connection_pool(self.database_url)
def apply(self, callback, route):
def wrapper(*args, **kwargs):
with self.pool.get_connection() as db:
# Add db to request context
bottle.request.db = db
try:
return callback(*args, **kwargs)
finally:
# Clean up
del bottle.request.db
return wrapper
def close(self):
if self.pool:
self.pool.close()
# Usage in routes
@route('/users')
def list_users():
users = request.db.query('SELECT * FROM users')
return {'users': users}class AuthPlugin:
name = 'auth'
api = 2
def __init__(self, auth_func, skip_paths=None):
self.auth_func = auth_func
self.skip_paths = skip_paths or []
def apply(self, callback, route):
# Skip authentication for certain paths
if route.rule in self.skip_paths:
return callback
def wrapper(*args, **kwargs):
if not self.auth_func(request):
abort(401, 'Authentication required')
return callback(*args, **kwargs)
return wrapper
def check_auth(request):
token = request.get_header('Authorization')
return token and validate_token(token)
# Install auth plugin
install(AuthPlugin(check_auth, skip_paths=['/login', '/public']))class CachePlugin:
name = 'cache'
api = 2
def __init__(self, cache_store):
self.cache = cache_store
def apply(self, callback, route):
# Only cache GET requests
if getattr(route, 'method', 'GET') != 'GET':
return callback
def wrapper(*args, **kwargs):
cache_key = f"{route.rule}:{request.query_string}"
# Try cache first
cached = self.cache.get(cache_key)
if cached:
return cached
# Generate response
result = callback(*args, **kwargs)
# Cache result
self.cache.set(cache_key, result, timeout=300)
return result
return wrapperHandle errors in plugin processing:
class PluginError(BottleException):
def __init__(self, message):
"""Plugin-specific error exception."""Error handling in plugins:
class SafePlugin:
name = 'safe'
api = 2
def apply(self, callback, route):
def wrapper(*args, **kwargs):
try:
return callback(*args, **kwargs)
except Exception as e:
print(f"Plugin error in {route.rule}: {e}")
# Could log, send to monitoring, etc.
raise # Re-raise or return error response
return wrapperInstall with Tessl CLI
npx tessl i tessl/pypi-bottle