The comprehensive WSGI web application library providing essential utilities and components for building Python web applications.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Collection of WSGI middleware components for common functionality including static file serving, proxy handling, profiling, and application dispatching. These middleware classes provide reusable functionality that can be wrapped around WSGI applications.
Serves static files during development or for simple deployments without requiring a separate web server.
class SharedDataMiddleware:
def __init__(self, app, exports, disallow=None, cache=True, cache_timeout=43200, fallback_mimetype="text/plain"):
"""
WSGI middleware for serving static files.
Parameters:
- app: WSGI application to wrap
- exports: Dict mapping URL paths to filesystem paths or package data
- disallow: List of fnmatch patterns for files to deny access to
- cache: Whether to send caching headers
- cache_timeout: Cache timeout in seconds (default: 12 hours)
- fallback_mimetype: Default MIME type for unknown files
"""
def is_allowed(self, filename):
"""
Check if a filename is allowed to be served.
Parameters:
- filename: Filename to check against disallow patterns
Returns:
True if file is allowed, False if blocked by disallow patterns
"""
def __call__(self, environ, start_response):
"""
WSGI application callable.
Serves static files if path matches exports, otherwise forwards
to the wrapped application.
"""Handles X-Forwarded headers when the application is behind a proxy server.
class ProxyFix:
def __init__(self, app, x_for=1, x_proto=1, x_host=0, x_port=0, x_prefix=0):
"""
Adjust WSGI environ based on X-Forwarded headers from proxies.
Parameters:
- app: WSGI application to wrap
- x_for: Number of X-Forwarded-For values to trust (sets REMOTE_ADDR)
- x_proto: Number of X-Forwarded-Proto values to trust (sets wsgi.url_scheme)
- x_host: Number of X-Forwarded-Host values to trust (sets HTTP_HOST, SERVER_NAME, SERVER_PORT)
- x_port: Number of X-Forwarded-Port values to trust (sets HTTP_HOST, SERVER_PORT)
- x_prefix: Number of X-Forwarded-Prefix values to trust (sets SCRIPT_NAME)
Note: Only trust headers from the number of proxies you expect.
Trusting wrong values is a security vulnerability.
"""
def __call__(self, environ, start_response):
"""
WSGI application callable.
Processes X-Forwarded headers and updates environ accordingly.
Original values are preserved in environ['werkzeug.proxy_fix.orig'].
"""Combines multiple WSGI applications into a single application based on URL paths.
class DispatcherMiddleware:
def __init__(self, app, mounts=None):
"""
Dispatch requests to different applications based on path prefixes.
Parameters:
- app: Default WSGI application for unmatched paths
- mounts: Dict mapping path prefixes to WSGI applications
"""
def __call__(self, environ, start_response):
"""
WSGI application callable.
Dispatches request to appropriate mounted application or default app.
Updates SCRIPT_NAME and PATH_INFO appropriately for mounted apps.
"""Profiles each request using Python's cProfile module to identify performance bottlenecks.
class ProfilerMiddleware:
def __init__(self, app, stream=sys.stdout, sort_by=("time", "calls"), restrictions=(), profile_dir=None, filename_format="{method}.{path}.{elapsed:.0f}ms.{time:.0f}.prof"):
"""
Profile WSGI application performance.
Parameters:
- app: WSGI application to wrap
- stream: Stream to write profiling stats to (None to disable)
- sort_by: Tuple of columns to sort stats by
- restrictions: Tuple of restrictions to filter stats
- profile_dir: Directory to save profile data files
- filename_format: Format string for profile filenames or callable
"""
def __call__(self, environ, start_response):
"""
WSGI application callable.
Profiles the wrapped application and outputs stats according to configuration.
"""Validates WSGI compliance of applications and middleware for debugging.
class LintMiddleware:
def __init__(self, app):
"""
WSGI compliance checking middleware.
Parameters:
- app: WSGI application to validate
Note: This middleware checks for WSGI spec compliance and will
raise warnings or errors for violations. Use during development only.
"""
def __call__(self, environ, start_response):
"""
WSGI application callable.
Validates environ dict, start_response callback, and application
return value for WSGI spec compliance.
"""Provides HTTP proxy functionality for forwarding requests to remote servers.
class ProxyMiddleware:
def __init__(self, app, targets, chunk_size=2 << 13, timeout=10):
"""
HTTP proxy middleware for forwarding requests.
Parameters:
- app: WSGI application to wrap (fallback for non-proxied requests)
- targets: Dict mapping path prefixes to target URLs
- chunk_size: Size of chunks when streaming responses
- timeout: Timeout for proxy requests
"""
def __call__(self, environ, start_response):
"""
WSGI application callable.
Forwards requests matching target paths to remote servers,
otherwise passes to wrapped application.
"""from werkzeug.middleware.shared_data import SharedDataMiddleware
from werkzeug.wrappers import Request, Response
import os
def app(environ, start_response):
request = Request(environ)
response = Response(f'API endpoint: {request.path}')
return response(environ, start_response)
# Serve static files from multiple locations
wrapped_app = SharedDataMiddleware(app, {
'/static': os.path.join(os.path.dirname(__file__), 'static'),
'/uploads': '/var/www/uploads',
'/assets': ('mypackage', 'assets'), # Package data
})
# With custom configuration
wrapped_app = SharedDataMiddleware(
app,
{'/static': './static'},
disallow=['*.secret', '*.key', '.htaccess'], # Block sensitive files
cache=True,
cache_timeout=3600, # 1 hour cache
fallback_mimetype='application/octet-stream'
)
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 8000, wrapped_app, use_reloader=True)from werkzeug.middleware.proxy_fix import ProxyFix
def app(environ, start_response):
# Access real client information after proxy processing
remote_addr = environ.get('REMOTE_ADDR')
scheme = environ.get('wsgi.url_scheme')
host = environ.get('HTTP_HOST')
response = Response(f'Client: {remote_addr}, Scheme: {scheme}, Host: {host}')
return response(environ, start_response)
# Configure for deployment behind nginx
# nginx sets X-Forwarded-For and X-Forwarded-Proto
proxy_app = ProxyFix(app, x_for=1, x_proto=1)
# Configure for deployment behind multiple proxies
# Load balancer -> nginx -> app
multi_proxy_app = ProxyFix(app, x_for=2, x_proto=1, x_host=1)
# Check original values if needed
def debug_app(environ, start_response):
orig = environ.get('werkzeug.proxy_fix.orig', {})
current_addr = environ.get('REMOTE_ADDR')
original_addr = orig.get('REMOTE_ADDR')
response = Response(f'Current: {current_addr}, Original: {original_addr}')
return response(environ, start_response)
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 8000, proxy_app)from werkzeug.middleware.dispatcher import DispatcherMiddleware
from werkzeug.wrappers import Request, Response
# API application
def api_app(environ, start_response):
request = Request(environ)
if request.path == '/users':
response = Response('{"users": []}', mimetype='application/json')
else:
response = Response('{"error": "Not found"}', status=404)
return response(environ, start_response)
# Admin application
def admin_app(environ, start_response):
request = Request(environ)
response = Response(f'<h1>Admin Panel</h1><p>Path: {request.path}</p>',
mimetype='text/html')
return response(environ, start_response)
# Frontend application (catch-all)
def frontend_app(environ, start_response):
request = Request(environ)
# Serve SPA for all frontend routes
if request.path.startswith('/app/'):
html = '''
<html>
<head><title>My App</title></head>
<body>
<div id="app">Single Page Application</div>
<script>console.log("Route: " + location.pathname)</script>
</body>
</html>
'''
response = Response(html, mimetype='text/html')
else:
response = Response('Welcome! Try /app/, /api/, or /admin/')
return response(environ, start_response)
# Combine applications
combined_app = DispatcherMiddleware(frontend_app, {
'/api': api_app,
'/admin': admin_app,
})
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 8000, combined_app, use_reloader=True)from werkzeug.middleware.profiler import ProfilerMiddleware
from werkzeug.wrappers import Request, Response
import time
import random
def slow_app(environ, start_response):
request = Request(environ)
# Simulate different performance characteristics
if request.path == '/slow':
time.sleep(0.5) # Simulate slow operation
elif request.path == '/compute':
# CPU intensive operation
result = sum(i**2 for i in range(10000))
elif request.path == '/random':
# Variable performance
time.sleep(random.uniform(0.1, 0.3))
response = Response(f'Processed {request.path}')
return response(environ, start_response)
# Profile to stdout with custom sorting
profiled_app = ProfilerMiddleware(
slow_app,
sort_by=('cumulative', 'calls'),
restrictions=(30,) # Show top 30 functions
)
# Save profile data to files
import os
profile_dir = './profiles'
os.makedirs(profile_dir, exist_ok=True)
file_profiled_app = ProfilerMiddleware(
slow_app,
stream=None, # Don't print to stdout
profile_dir=profile_dir,
filename_format='{method}-{path}-{elapsed:.0f}ms.prof'
)
# Custom filename generator
def custom_filename(environ):
profiler_info = environ['werkzeug.profiler']
path = environ.get('PATH_INFO', 'root').replace('/', '-')
elapsed = profiler_info['elapsed']
return f'profile-{path}-{elapsed:.1f}ms.prof'
custom_profiled_app = ProfilerMiddleware(
slow_app,
profile_dir=profile_dir,
filename_format=custom_filename
)
if __name__ == '__main__':
from werkzeug.serving import run_simple
print("Visit /slow, /compute, or /random to see profiling output")
run_simple('localhost', 8000, profiled_app, use_reloader=True)from werkzeug.middleware.lint import LintMiddleware
from werkzeug.wrappers import Response
# Application with potential WSGI violations
def problematic_app(environ, start_response):
# This app has some WSGI compliance issues
# Issue 1: Not checking if start_response was called
response = Response('Hello')
# Issue 2: Returning string instead of bytes iterable
# return 'This would cause a lint error' # Wrong!
# Correct WSGI return
return response(environ, start_response)
# Wrap with lint middleware for development
linted_app = LintMiddleware(problematic_app)
# This will catch and warn about WSGI spec violations
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 8000, linted_app, use_reloader=True)from werkzeug.middleware.http_proxy import ProxyMiddleware
def local_app(environ, start_response):
request = Request(environ)
response = Response(f'Local app handling: {request.path}')
return response(environ, start_response)
# Proxy certain paths to remote services
proxied_app = ProxyMiddleware(local_app, {
'/api/external': 'https://api.example.com',
'/cdn': 'https://cdn.example.com',
})
if __name__ == '__main__':
from werkzeug.serving import run_simple
# Requests to /api/external/* will be forwarded to api.example.com
# Other requests handled by local_app
run_simple('localhost', 8000, proxied_app)from werkzeug.middleware.shared_data import SharedDataMiddleware
from werkzeug.middleware.proxy_fix import ProxyFix
from werkzeug.middleware.profiler import ProfilerMiddleware
from werkzeug.middleware.dispatcher import DispatcherMiddleware
def api_app(environ, start_response):
response = Response('{"api": "v1"}', mimetype='application/json')
return response(environ, start_response)
def web_app(environ, start_response):
response = Response('<h1>Web Application</h1>', mimetype='text/html')
return response(environ, start_response)
# Layer middleware (applied inside-out)
app = DispatcherMiddleware(web_app, {'/api': api_app})
# Add static file serving
app = SharedDataMiddleware(app, {
'/static': './static',
'/favicon.ico': './static/favicon.ico'
})
# Add proxy fix for production deployment
app = ProxyFix(app, x_for=1, x_proto=1)
# Add profiling for development
if __name__ == '__main__':
# Only add profiler in development
app = ProfilerMiddleware(app, sort_by=('cumulative',))
from werkzeug.serving import run_simple
run_simple('localhost', 8000, app, use_reloader=True)import os
from werkzeug.middleware.shared_data import SharedDataMiddleware
from werkzeug.middleware.proxy_fix import ProxyFix
from werkzeug.middleware.profiler import ProfilerMiddleware
def create_app():
def app(environ, start_response):
response = Response('Hello World!')
return response(environ, start_response)
return app
def wrap_app_for_environment(app):
environment = os.getenv('ENVIRONMENT', 'development')
if environment == 'development':
# Development middleware
app = SharedDataMiddleware(app, {'/static': './static'})
app = ProfilerMiddleware(app)
elif environment == 'production':
# Production middleware
app = ProxyFix(app, x_for=1, x_proto=1, x_host=1)
# Note: Use proper web server for static files in production
return app
if __name__ == '__main__':
from werkzeug.serving import run_simple
app = create_app()
app = wrap_app_for_environment(app)
run_simple('localhost', 8000, app, use_reloader=True)from werkzeug.wrappers import Request, Response
class RequestTimingMiddleware:
"""Custom middleware to add request timing headers."""
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
import time
start_time = time.time()
def timing_start_response(status, headers, exc_info=None):
# Add timing header
elapsed = time.time() - start_time
headers.append(('X-Response-Time', f'{elapsed:.3f}s'))
return start_response(status, headers, exc_info)
return self.app(environ, timing_start_response)
def app(environ, start_response):
import time
time.sleep(0.1) # Simulate processing
response = Response('Timed response')
return response(environ, start_response)
# Apply custom middleware
timed_app = RequestTimingMiddleware(app)
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 8000, timed_app)Install with Tessl CLI
npx tessl i tessl/pypi-werkzeug