blackbull.router¶
blackbull.router
¶
URL routing for BlackBull.
Router maps (path, method, scheme) triples to handler chains. Paths
support exact strings, regex patterns, and {name} / {name:converter}
parameter syntax; ErrorRouter does the same for HTTPStatus codes and
exception classes. _register_chain composes per-route middlewares with
functools.partial so each middleware receives call_next bound to the
next link.
RouteGroup is defined in :mod:blackbull.app to avoid a circular
import; this module re-exports it lazily through __getattr__.
ConfigurationError
¶
Bases: Exception
Raised by Router.validate() when route definitions are inconsistent.
ErrorRouter
¶
Maps HTTP error statuses and exception classes to ASGI error-handler functions.
Keys accepted by setitem / getitem: - HTTPStatus value (e.g. HTTPStatus.NOT_FOUND) - Exception class (e.g. ValueError)
Lookup rules
- HTTPStatus key: exact match only.
- Exception class: walks the MRO so a handler registered for a base class (e.g. Exception) catches all unhandled subclasses.
- Returns None when no handler is found (caller decides the fallback).
Usage::
errors = ErrorRouter()
@errors[HTTPStatus.NOT_FOUND]
async def handle_404(scope, receive, send):
...
@errors[ValueError]
async def handle_value_error(scope, receive, send):
...
handler = errors[HTTPStatus.NOT_FOUND] # → handle_404
handler = errors[KeyError()] # → handle_value_error via MRO (if registered)
handler = errors[KeyError] # same, accepting the class directly
MethodNotApplicable
¶
Bases: Exception
Raised when the path exists but the HTTP method is not allowed.
PathNotRegistered
¶
Bases: KeyError
Raised when no registered path matches the requested path.
Router
¶
Bases: UserDict, BaseRouter
This class has 2 dictionaries: self.data and self.regex_. key: str or re.Pattern value: (function, methods, scheme)
__contains__(item)
¶
Accept either a plain str (path only) or a (path, method, scheme) tuple. Search both self.data and self.regex_.
__getitem__(key)
¶
key: (path: str, method: HTTPMethod, scheme: Scheme)
Uses the routing trie for O(path-depth) lookup of string-path routes, then falls back to a linear scan of raw re.Pattern routes.
Results are cached in _lookup_cache (up to _LOOKUP_CACHE_MAX
entries) so repeated requests to the same (path, method, scheme) skip
the trie traversal entirely after the first hit.
__setitem__(key, value)
¶
If key[0] is a str: - Store it in self.data under the normalised (path, methods, scheme) key. - Also compile a regex from {param} / {param:converter} placeholders and store it in self.regex_ together with per-param converter functions.
If key[0] is a re.Pattern: - Store it in self.regex_ only.
When scheme is omitted it is stored as _ANY_SCHEME, which matches any scheme at lookup time.
route(methods=[HTTPMethod.GET], path='/', scheme=Scheme.http, functions=[], middlewares=[], name=None)
¶
Register a function or middleware chain in the routing table.
Three calling conventions:
1. Decorator with no extra middlewares (functions and middlewares
both empty) — returns a decorator via route_fn.
2. functions=[...] — registers a pre-built chain immediately;
returns None (same as before).
3. middlewares=[...] — returns a decorator; the decorated handler is
appended to the middleware list before the chain is registered.
name registers the route for use with url_path_for().
url_path_for(name, /, **params)
¶
Return the path for the named route with params substituted.
Raises KeyError if the name is unknown, ValueError if required params are missing.
validate()
¶
Check all route definitions for consistency, then freeze the router.
Checks performed:
- Every converter spec names a known converter.
- Every path param appears in the handler signature (simplified handlers).
- Converter output type matches the handler's annotation.
Raises :class:ConfigurationError listing all violations found.
Sets self._frozen = True on success so no further routes can
be added. Called once at app boot from :meth:BlackBull.run /
:meth:BlackBull.serve — handler bugs that violate the contract
surface before the first request is served, not after.