Routing with Radix Trees§
This project includes an AsgiRouter
class which can be used to construct a routing tree.
A routing tree can be constructed of a combination static paths and regex patterns.
Here's an example of some paths:
"/doc/"
"/doc/code_faq.html"
"/info/{user}"
"/info/{user}/project"
"/info/{user}/project/{project}"
In addition, a named regex pattern may be used:
"/regex/{name:[a-zA-Z]+}/test"
"/optional/{name:[a-zA-Z]+}/{word}/plus/"
"{uid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}}"
However, the following pattern will not match:
"/repos/{owner}/{repo}/{archive}_format/{ref}"
The reason is {archive}_format
includes a greedy match {archive}
which will match everything up to the following slash.
Constructing a Router§
An AsgiRouter is a collection of routes:
In [3]: from tokamak.router import AsgiRouter, Route
In [4]: async def some_handler(*args, **kwargs):
... return "ok"
In [5]: router = AsgiRouter(routes=[
... Route("/", handler=some_handler, methods=["GET"]),
... ])
Note: the handler
can be any callable.
It's also possible to use the method add_route
to add more routes:
In [6]: router.add_route(
... Route("/files/{dir}/{filepath:*}", handler=some_handler, methods=["POST"])
... )
After that, we can search for a route:
In [7]: router.get_route("/files/home/sshconfig")
Out[7]: (<tokamak.router.Route at 0x11009f940>,
{'dir': 'home', 'filepath': 'sshconfig'})
A matching path from a call to get_path
will return a tuple
of a Route
and the matched context:
In [8]: route, context = router.get_route("/files/home/sshconfig")
In [9]: route.handler
Out[9]: <function __main__.some_handler(*args, **kwargs)>
In [11]: await route.handler()
Out[11]: 'ok'
In [12]: context
Out[12]: {'dir': 'home', 'filepath': 'sshconfig'}
Note: UnknownEndpoint
will be returned for any route that doesn't match.
In [13]: router.get_route("/files/home/sshconfig/unknown")
---------------------------------------------------------------------------
UnknownEndpoint Traceback (most recent call last)
Input In [13], in <cell line: 1>()
----> 1 router.get_route("/files/home/sshconfig/unknown")
File ~/open_source/tokamak/tokamak/router.py:135, in AsgiRouter.get_route(self, path)
133 route, context = self.tree.get_handler(path)
134 if not route:
--> 135 raise UnknownEndpoint(f"Unknown path: {path}")
136 return route, context
UnknownEndpoint: Unknown path: /files/home/sshconfig/unknown