Last active
December 17, 2015 10:59
-
-
Save jdp/5598897 to your computer and use it in GitHub Desktop.
prototype for Vial, a small web framework
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import re | |
from werkzeug.exceptions import NotFound, MethodNotAllowed | |
from werkzeug.wrappers import Request, Response | |
class RouteNode(object): | |
@property | |
def converter(self): | |
return None | |
class PathNode(RouteNode): | |
def __init__(self, path): | |
super(PathNode, self).__init__() | |
self.path = path.strip().strip('/') | |
@property | |
def pattern(self): | |
return re.escape(self.path) | |
class ParamNode(RouteNode): | |
def __init__(self, pattern, cls): | |
super(ParamNode, self).__init__() | |
self.cls = cls | |
self._pattern = pattern | |
@property | |
def pattern(self): | |
return r'(' + self._pattern + r')' | |
@property | |
def converter(self): | |
return self.cls | |
class Vial(object): | |
def __init__(self): | |
self.stack = [] | |
self.handlers = {} | |
def __enter__(self): | |
return self | |
def __exit__(self, typ, value, traceback): | |
if typ: | |
return False | |
self.stack.pop() | |
return self | |
def route(self, node): | |
self.stack.append(node) | |
def path(self, pathname=''): | |
self.route(PathNode(pathname)) | |
return self | |
def param(self, pattern=r'\w+', cls=str): | |
self.route(ParamNode(pattern, cls)) | |
return self | |
def handle(self, method, func): | |
def compile(nodes): | |
return r'^/' + r'/'.join(n.pattern for n in nodes if n.pattern) + r'/?$' | |
pattern = compile(self.stack) | |
if pattern not in self.handlers: | |
self.handlers[pattern] = {} | |
converters = [n.converter for n in self.stack if n.converter] | |
self.handlers[pattern][method.lower()] = (func, converters) | |
def get(self, func): | |
return self.handle('get', func) | |
def post(self, func): | |
return self.handle('post', func) | |
def put(self, func): | |
return self.handle('put', func) | |
def delete(self, func): | |
return self.handle('delete', func) | |
def dispatch_request(self, request): | |
for pattern, handlers in self.handlers.iteritems(): | |
match = re.match(pattern, request.path) | |
if not match: | |
continue | |
method = request.method.lower() | |
if method not in handlers: | |
raise MethodNotAllowed() | |
func, converters = handlers[method] | |
params = [c(*p) for c, p in zip(converters, match.groups())] | |
return func(request, *params) | |
raise NotFound() | |
def wsgi_app(self, environ, start_response): | |
request = Request(environ) | |
response = self.dispatch_request(request) | |
return response(environ, start_response) | |
def __call__(self, environ, start_response): | |
return self.wsgi_app(environ, start_response) | |
def run(self, address='127.0.0.1', port=5000): | |
from werkzeug.serving import run_simple | |
run_simple(address, port, self.wsgi_app, use_debugger=True, use_reloader=True) | |
if __name__ == '__main__': | |
def debug_handler(request, *args): | |
return Response("\n".join(str((k, v)) for (k, v) in request.environ.iteritems())) | |
r = Vial() | |
# Creates routes for: | |
# GET / | |
# GET, POST /posts | |
# GET, PUT, DELETE /posts/:id | |
# GET, POST /posts/:id/comments | |
# GET, PUT, DELETE /posts/:pid/comments/:cid | |
with r.path(): | |
r.get(lambda r: Response("index")) | |
with r.path('posts'): | |
r.get(lambda r: Response("get all posts")) | |
r.post(lambda r: Response("create post")) | |
with r.param(r'\d+', cls=int): | |
r.get(lambda r, p: Response("get post #%d" % p)) | |
r.put(lambda r, p: Response("upsert post #%d" % p)) | |
r.delete(lambda r, p: Response("delete post #%d" % p)) | |
with r.path('comments'): | |
r.get(lambda r, p: Response("get all post #%d comments" % p)) | |
r.post(lambda r, p: Response("create comment on post #%d" % p)) | |
with r.param(r'\d+', cls=int): | |
r.get(lambda r, p, c: Response("get comment #%d on post #%d " % (c, p))) | |
r.put(lambda r, p, c: Response("upsert comment #%d on post #%d" % (c, p))) | |
r.delete(lambda r, p, c: Response("delete comment #%d on post #%d" % (c, p))) | |
r.run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment