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
Flexible URL routing system for mapping URLs to endpoints with support for URL variables, converters, HTTP methods, and URL generation. The routing system provides both URL-to-endpoint matching and endpoint-to-URL building capabilities.
The Map class manages a collection of URL rules and provides methods for binding to specific requests.
class Map:
def __init__(self, rules=None, default_subdomain="", charset="utf-8", strict_slashes=True, merge_slashes=True, redirect_defaults=True, converters=None, sort_parameters=True, sort_key=None, host_matching=False):
"""
Create a new URL map.
Parameters:
- rules: Initial list of Rule objects
- default_subdomain: Default subdomain for rules
- charset: Character encoding for URLs
- strict_slashes: Enforce trailing slash rules
- merge_slashes: Merge consecutive slashes in URLs
- redirect_defaults: Redirect to canonical URLs
- converters: Custom URL converters
- sort_parameters: Sort URL parameters when building
- sort_key: Custom sort key function
- host_matching: Enable host-based routing
"""
def add(self, rule_factory):
"""
Add a rule or rule factory to the map.
Parameters:
- rule_factory: Rule object or RuleFactory
"""
def bind(self, server_name, script_name="/", subdomain=None, url_scheme="http", path_info=None, method="GET", query_args=None):
"""
Create a MapAdapter for URL matching and building.
Parameters:
- server_name: Server hostname
- script_name: SCRIPT_NAME prefix
- subdomain: Subdomain to match
- url_scheme: URL scheme (http/https)
- path_info: PATH_INFO for matching
- method: HTTP method
- query_args: Query arguments dict
Returns:
MapAdapter instance
"""
def bind_to_environ(self, environ, server_name=None, subdomain=None):
"""
Create MapAdapter from WSGI environ.
Parameters:
- environ: WSGI environment dictionary
- server_name: Override server name
- subdomain: Override subdomain
Returns:
MapAdapter instance
"""
def update(self):
"""Update internal routing structures after adding rules."""The Rule class defines individual URL patterns with endpoints, methods, and variable conversion.
class Rule:
def __init__(self, string, defaults=None, subdomain=None, methods=None, build_only=False, endpoint=None, strict_slashes=None, merge_slashes=None, redirect_to=None, alias=False, host=None):
"""
Create a new URL rule.
Parameters:
- string: URL pattern (e.g., '/user/<int:id>')
- defaults: Default values for variables
- subdomain: Subdomain pattern
- methods: Allowed HTTP methods (defaults to GET)
- build_only: Only use for URL building, not matching
- endpoint: Endpoint name for this rule
- strict_slashes: Override map's strict_slashes setting
- merge_slashes: Override map's merge_slashes setting
- redirect_to: Redirect target (string or callable)
- alias: Whether this is an alias rule
- host: Host pattern for host-based routing
"""
def match(self, path, method="GET"):
"""
Test if this rule matches a path.
Parameters:
- path: URL path to test
- method: HTTP method
Returns:
Tuple of (values_dict, redirect_exception) or None
"""
def build(self, values, append_unknown=True):
"""
Build URL from endpoint values.
Parameters:
- values: Dictionary of variable values
- append_unknown: Append unknown values as query parameters
Returns:
Tuple of (path, domain)
"""The MapAdapter class provides URL matching and building functionality for a specific request context.
class MapAdapter:
def match(self, path_info=None, method=None, return_rule=False, query_args=None):
"""
Match a URL to an endpoint.
Parameters:
- path_info: URL path (uses bound path if None)
- method: HTTP method (uses bound method if None)
- return_rule: Return matched Rule object instead of endpoint
- query_args: Query arguments dictionary
Returns:
Tuple of (endpoint, values) or Rule object
Raises:
RequestRedirect: If redirect is required
MethodNotAllowed: If method is not allowed
NotFound: If no rule matches
"""
def test(self, path_info=None, method="GET"):
"""
Test if a URL matches any rule.
Parameters:
- path_info: URL path to test
- method: HTTP method
Returns:
True if URL matches, False otherwise
"""
def build(self, endpoint, values=None, method=None, force_external=False, append_unknown=True, url_scheme=None):
"""
Build URL for an endpoint.
Parameters:
- endpoint: Endpoint name
- values: Variable values dictionary
- method: HTTP method hint
- force_external: Force absolute URL
- append_unknown: Add unknown values as query parameters
- url_scheme: Override URL scheme
Returns:
Built URL string
Raises:
BuildError: If URL cannot be built
"""
def dispatch(self, view_func, path_info=None, method=None, catch_http_exceptions=False):
"""
Match URL and dispatch to view function.
Parameters:
- view_func: Function that takes (endpoint, values) and returns WSGI app
- path_info: URL path
- method: HTTP method
- catch_http_exceptions: Catch and return HTTP exceptions as responses
Returns:
WSGI application or Response
"""Built-in converters for URL variable types with validation and conversion.
class BaseConverter:
def __init__(self, map, *args, **kwargs):
"""
Base converter class.
Parameters:
- map: URL map instance
- args: Converter arguments
- kwargs: Converter keyword arguments
"""
def to_python(self, value):
"""
Convert URL value to Python object.
Parameters:
- value: URL string value
Returns:
Converted Python object
Raises:
ValidationError: If conversion fails
"""
def to_url(self, value):
"""
Convert Python object to URL string.
Parameters:
- value: Python object
Returns:
URL-safe string
"""
class UnicodeConverter(BaseConverter):
"""Default string converter, matches any text except '/'."""
class AnyConverter(BaseConverter):
"""Matches any of the specified strings."""
def __init__(self, map, *items):
"""
Parameters:
- items: Valid string values
"""
class PathConverter(BaseConverter):
"""Matches strings including '/' characters."""
class IntegerConverter(BaseConverter):
"""Matches integers."""
def __init__(self, map, fixed_digits=0, min=None, max=None):
"""
Parameters:
- fixed_digits: Exact number of digits required
- min: Minimum value
- max: Maximum value
"""
class FloatConverter(BaseConverter):
"""Matches floating point numbers."""
class UUIDConverter(BaseConverter):
"""Matches UUID strings."""Factory classes for creating multiple related rules.
class RuleFactory:
"""Base class for rule factories."""
def get_rules(self, map):
"""
Get list of rules from this factory.
Parameters:
- map: URL map instance
Returns:
List of Rule objects
"""
class RuleTemplate:
def __init__(self, template):
"""
Create rule template.
Parameters:
- template: Template Rule object
"""
class Subdomain:
def __init__(self, subdomain, rules):
"""
Create rules for specific subdomain.
Parameters:
- subdomain: Subdomain pattern
- rules: List of rules or rule factories
"""
class Submount:
def __init__(self, path, rules):
"""
Mount rules under a path prefix.
Parameters:
- path: Path prefix
- rules: List of rules or rule factories
"""
class EndpointPrefix:
def __init__(self, prefix, rules):
"""
Add prefix to rule endpoints.
Parameters:
- prefix: Endpoint prefix string
- rules: List of rules or rule factories
"""Exceptions raised during URL matching and building.
class RoutingException(Exception):
"""Base exception for routing errors."""
class RequestRedirect(HTTPException, RoutingException):
"""Raised when a redirect is required."""
code = 308
def __init__(self, new_url):
"""
Parameters:
- new_url: Target URL for redirect
"""
class BuildError(RoutingException, LookupError):
"""Raised when URL building fails."""
def __init__(self, endpoint, values, method, adapter=None):
"""
Parameters:
- endpoint: Endpoint that failed to build
- values: Values dictionary used
- method: HTTP method
- adapter: MapAdapter instance
"""
class NoMatch(RoutingException):
"""Raised when no rule matches."""
class MethodNotAllowed(HTTPException, RoutingException):
"""Raised when HTTP method is not allowed."""
code = 405from werkzeug.routing import Map, Rule
from werkzeug.wrappers import Request, Response
# Define URL rules
url_map = Map([
Rule('/', endpoint='index'),
Rule('/user/<username>', endpoint='user_profile'),
Rule('/post/<int:post_id>', endpoint='show_post'),
Rule('/api/users/<int:user_id>', endpoint='api_user', methods=['GET', 'POST']),
Rule('/download/<path:filename>', endpoint='download_file'),
])
def view_functions(endpoint, values):
"""Route endpoints to view functions."""
if endpoint == 'index':
return lambda environ, start_response: Response('Home Page')(environ, start_response)
elif endpoint == 'user_profile':
username = values['username']
return lambda environ, start_response: Response(f'User: {username}')(environ, start_response)
elif endpoint == 'show_post':
post_id = values['post_id']
return lambda environ, start_response: Response(f'Post #{post_id}')(environ, start_response)
# ... more endpoints
def application(environ, start_response):
request = Request(environ)
adapter = url_map.bind_to_environ(request.environ)
try:
endpoint, values = adapter.match()
wsgi_app = view_functions(endpoint, values)
return wsgi_app(environ, start_response)
except NotFound:
return Response('Not Found', status=404)(environ, start_response)
except MethodNotAllowed:
return Response('Method Not Allowed', status=405)(environ, start_response)from werkzeug.routing import BaseConverter, ValidationError
import re
class SlugConverter(BaseConverter):
"""Converter for URL-friendly slugs."""
def __init__(self, map, min_length=4, max_length=50):
super().__init__(map)
self.min_length = min_length
self.max_length = max_length
self.regex = rf'[a-z0-9-]{{{min_length},{max_length}}}'
def to_python(self, value):
if not re.match(r'^[a-z0-9-]+$', value):
raise ValidationError('Invalid slug format')
return value
def to_url(self, value):
return str(value).lower().replace(' ', '-')
# Register custom converter
url_map = Map([
Rule('/article/<slug:article_slug>', endpoint='article')
], converters={'slug': SlugConverter})from werkzeug.routing import Map, Rule
url_map = Map([
Rule('/', endpoint='index'),
Rule('/user/<username>', endpoint='user_profile'),
Rule('/post/<int:post_id>/edit', endpoint='edit_post'),
])
def application(environ, start_response):
adapter = url_map.bind_to_environ(environ)
# Build URLs in templates or redirects
home_url = adapter.build('index') # '/'
user_url = adapter.build('user_profile', {'username': 'john'}) # '/user/john'
edit_url = adapter.build('edit_post', {'post_id': 123}) # '/post/123/edit'
# Force external URLs
external_url = adapter.build('user_profile', {'username': 'john'}, force_external=True)
# 'http://example.com/user/john'
response_html = f'''
<a href="{home_url}">Home</a>
<a href="{user_url}">User Profile</a>
<a href="{edit_url}">Edit Post</a>
<a href="{external_url}">External Link</a>
'''
return Response(response_html, mimetype='text/html')(environ, start_response)from werkzeug.routing import Map, Rule, Subdomain, Submount, EndpointPrefix
# Complex routing with subdomains and submounts
url_map = Map([
# Main site routes
Rule('/', endpoint='index'),
Rule('/about', endpoint='about'),
# API routes with prefix
Submount('/api/v1', [
Rule('/users', endpoint='api.list_users'),
Rule('/users/<int:user_id>', endpoint='api.get_user'),
Rule('/posts', endpoint='api.list_posts'),
]),
# Admin subdomain
Subdomain('admin', [
Rule('/', endpoint='admin.dashboard'),
Rule('/users', endpoint='admin.users'),
Rule('/settings', endpoint='admin.settings'),
]),
# API subdomain with endpoint prefix
Subdomain('api', [
EndpointPrefix('api.', [
Rule('/health', endpoint='health'),
Rule('/status', endpoint='status'),
])
]),
])
def application(environ, start_response):
adapter = url_map.bind_to_environ(environ)
try:
endpoint, values = adapter.match()
if endpoint.startswith('api.'):
# Handle API endpoints
return handle_api(endpoint, values)(environ, start_response)
elif endpoint.startswith('admin.'):
# Handle admin endpoints
return handle_admin(endpoint, values)(environ, start_response)
else:
# Handle main site
return handle_main(endpoint, values)(environ, start_response)
except RequestRedirect as e:
return Response('', status=e.code, headers=[('Location', e.new_url)])(environ, start_response)from werkzeug.routing import Map, Rule
from werkzeug.wrappers import Request, Response
url_map = Map([
Rule('/users', endpoint='users', methods=['GET', 'POST']),
Rule('/users/<int:user_id>', endpoint='user_detail', methods=['GET', 'PUT', 'DELETE']),
])
def handle_users(request, user_id=None):
if user_id is None:
# /users endpoint
if request.method == 'GET':
return Response('List all users')
elif request.method == 'POST':
return Response('Create new user')
else:
# /users/<id> endpoint
if request.method == 'GET':
return Response(f'Get user {user_id}')
elif request.method == 'PUT':
return Response(f'Update user {user_id}')
elif request.method == 'DELETE':
return Response(f'Delete user {user_id}')
def application(environ, start_response):
request = Request(environ)
adapter = url_map.bind_to_environ(request.environ)
try:
endpoint, values = adapter.match()
if endpoint in ('users', 'user_detail'):
response = handle_users(request, **values)
else:
response = Response('Not Found', status=404)
return response(environ, start_response)
except MethodNotAllowed as e:
return Response('Method Not Allowed', status=405)(environ, start_response)Install with Tessl CLI
npx tessl i tessl/pypi-werkzeug