Added better error handling and coroutine checking
This commit is contained in:
parent
e0b9260644
commit
b59dc2729f
28
sanic/exceptions.py
Normal file
28
sanic/exceptions.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
from .response import html
|
||||||
|
|
||||||
|
class NotFound(Exception):
|
||||||
|
status_code = 404
|
||||||
|
class InvalidUsage(Exception):
|
||||||
|
status_code = 400
|
||||||
|
class ServerError(Exception):
|
||||||
|
status_code = 500
|
||||||
|
|
||||||
|
class Handler:
|
||||||
|
handlers = None
|
||||||
|
def __init__(self):
|
||||||
|
self.handlers = {}
|
||||||
|
|
||||||
|
def add(self, exception_type, handler):
|
||||||
|
self.handlers[exception_type] = handler
|
||||||
|
|
||||||
|
def response(self, request, exception):
|
||||||
|
handler = self.handlers.get(type(exception))
|
||||||
|
if handler:
|
||||||
|
response = handler(request, exception)
|
||||||
|
else:
|
||||||
|
response = Handler.default(request, exception)
|
||||||
|
return response
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def default(request, exception):
|
||||||
|
return html("Error: {}".format(exception), status=getattr(exception, 'status_code', 500))
|
|
@ -2,7 +2,17 @@ import ujson
|
||||||
|
|
||||||
STATUS_CODES = {
|
STATUS_CODES = {
|
||||||
200: 'OK',
|
200: 'OK',
|
||||||
404: 'Not Found'
|
400: 'Bad Request',
|
||||||
|
401: 'Unauthorized',
|
||||||
|
402: 'Payment Required',
|
||||||
|
403: 'Forbidden',
|
||||||
|
404: 'Not Found',
|
||||||
|
400: 'Method Not Allowed',
|
||||||
|
500: 'Internal Server Error',
|
||||||
|
501: 'Not Implemented',
|
||||||
|
502: 'Bad Gateway',
|
||||||
|
503: 'Service Unavailable',
|
||||||
|
504: 'Gateway Timeout',
|
||||||
}
|
}
|
||||||
class HTTPResponse:
|
class HTTPResponse:
|
||||||
__slots__ = ('body', 'status', 'content_type')
|
__slots__ = ('body', 'status', 'content_type')
|
||||||
|
@ -36,12 +46,9 @@ class HTTPResponse:
|
||||||
#b'\r\n'
|
#b'\r\n'
|
||||||
])
|
])
|
||||||
|
|
||||||
|
def json(body, status=200):
|
||||||
def error_404(request, *args):
|
return HTTPResponse(ujson.dumps(body), status=status, content_type="application/json")
|
||||||
return HTTPResponse("404!", status=404)
|
def text(body, status=200):
|
||||||
error_404.is_async = False
|
return HTTPResponse(body, status=status, content_type="text/plain")
|
||||||
|
def html(body, status=200):
|
||||||
def json(input):
|
return HTTPResponse(body, status=status, content_type="text/html")
|
||||||
return HTTPResponse(ujson.dumps(input), content_type="application/json")
|
|
||||||
def text(input):
|
|
||||||
return HTTPResponse(input, content_type="text/plain")
|
|
|
@ -1,19 +1,19 @@
|
||||||
from .log import log
|
from .log import log
|
||||||
|
from .exceptions import NotFound
|
||||||
|
|
||||||
class Router():
|
class Router():
|
||||||
routes = None
|
routes = None
|
||||||
default = None
|
|
||||||
|
|
||||||
def __init__(self, default=None):
|
def __init__(self):
|
||||||
self.routes = {}
|
self.routes = {}
|
||||||
self.default=default
|
|
||||||
|
|
||||||
def add(self, route, handler):
|
def add(self, uri, handler):
|
||||||
self.routes[route] = handler
|
self.routes[uri] = handler
|
||||||
|
|
||||||
def get(self, uri):
|
def get(self, request):
|
||||||
handler = self.routes.get(uri.decode('utf-8'), self.default)
|
uri_string = request.url.decode('utf-8')
|
||||||
|
handler = self.routes.get(uri_string)
|
||||||
if handler:
|
if handler:
|
||||||
return handler
|
return handler
|
||||||
else:
|
else:
|
||||||
return self.default
|
raise NotFound("Requested URL {} not found".format(uri_string))
|
|
@ -1,24 +1,29 @@
|
||||||
import inspect
|
|
||||||
from .router import Router
|
from .router import Router
|
||||||
from .response import HTTPResponse, error_404
|
from .exceptions import Handler
|
||||||
|
from .response import HTTPResponse
|
||||||
from .server import serve
|
from .server import serve
|
||||||
from .log import log
|
from .log import log
|
||||||
|
|
||||||
class Sanic:
|
class Sanic:
|
||||||
name = None
|
name = None
|
||||||
|
router = None
|
||||||
|
error_handler = None
|
||||||
routes = []
|
routes = []
|
||||||
|
|
||||||
def __init__(self, name, router=None):
|
def __init__(self, name, router=None, error_handler=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.router = router or Router(default=error_404)
|
self.router = router or Router()
|
||||||
|
self.error_handler = error_handler or Handler()
|
||||||
|
|
||||||
def route(self, *args, **kwargs):
|
def route(self, *args, **kwargs):
|
||||||
def response(handler):
|
def response(handler):
|
||||||
handler.is_async = inspect.iscoroutinefunction(handler)
|
self.add_route(handler, *args, **kwargs)
|
||||||
self.router.add(*args, **kwargs, handler=handler)
|
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
def add_route(self, handler, *args, **kwargs):
|
||||||
|
self.router.add(*args, **kwargs, handler=handler)
|
||||||
|
|
||||||
def run(self, host="127.0.0.1", port=8000, debug=False):
|
def run(self, host="127.0.0.1", port=8000, debug=False):
|
||||||
return serve(router=self.router, host=host, port=port, debug=debug)
|
return serve(sanic=self, host=host, port=port, debug=debug)
|
|
@ -5,6 +5,7 @@ import signal
|
||||||
import functools
|
import functools
|
||||||
import httptools
|
import httptools
|
||||||
import logging
|
import logging
|
||||||
|
from inspect import iscoroutine
|
||||||
from ujson import loads as json_loads
|
from ujson import loads as json_loads
|
||||||
from urllib.parse import parse_qs
|
from urllib.parse import parse_qs
|
||||||
|
|
||||||
|
@ -18,6 +19,7 @@ from socket import *
|
||||||
|
|
||||||
from .log import log
|
from .log import log
|
||||||
from .config import LOGO
|
from .config import LOGO
|
||||||
|
from .exceptions import ServerError
|
||||||
from .response import HTTPResponse
|
from .response import HTTPResponse
|
||||||
|
|
||||||
PRINT = 0
|
PRINT = 0
|
||||||
|
@ -65,16 +67,16 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
|
|
||||||
__slots__ = ('loop',
|
__slots__ = ('loop',
|
||||||
'transport', 'request', 'parser',
|
'transport', 'request', 'parser',
|
||||||
'url', 'headers', 'router')
|
'url', 'headers', 'sanic')
|
||||||
|
|
||||||
def __init__(self, *, router, loop):
|
def __init__(self, *, sanic, loop):
|
||||||
self.loop = loop
|
self.loop = loop
|
||||||
self.transport = None
|
self.transport = None
|
||||||
self.request = None
|
self.request = None
|
||||||
self.parser = None
|
self.parser = None
|
||||||
self.url = None
|
self.url = None
|
||||||
self.headers = None
|
self.headers = None
|
||||||
self.router = router
|
self.sanic = sanic
|
||||||
|
|
||||||
# -------------------------------------------- #
|
# -------------------------------------------- #
|
||||||
# Connection
|
# Connection
|
||||||
|
@ -140,14 +142,22 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
# -------------------------------------------- #
|
# -------------------------------------------- #
|
||||||
|
|
||||||
async def get_response(self, request):
|
async def get_response(self, request):
|
||||||
handler = self.router.get(request.url)
|
|
||||||
try:
|
try:
|
||||||
if handler.is_async:
|
handler = self.sanic.router.get(request)
|
||||||
response = await handler(request)
|
if handler is None:
|
||||||
else:
|
raise ServerError("'None' was returned while requesting a handler from the router")
|
||||||
|
|
||||||
response = handler(request)
|
response = handler(request)
|
||||||
|
|
||||||
|
# Check if the handler is asynchronous
|
||||||
|
if iscoroutine(response):
|
||||||
|
response = await response
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
response = HTTPResponse("Error: {}".format(e))
|
try:
|
||||||
|
response = self.sanic.error_handler.response(request, e)
|
||||||
|
except Exception as e:
|
||||||
|
response = HTTPResponse("Error while handling error: {}".format(e))
|
||||||
|
|
||||||
self.write_response(request, response)
|
self.write_response(request, response)
|
||||||
|
|
||||||
|
@ -184,7 +194,7 @@ def abort(msg):
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def serve(router, host, port, debug=False):
|
def serve(sanic, host, port, debug=False):
|
||||||
# Create Event Loop
|
# Create Event Loop
|
||||||
loop = async_loop.new_event_loop()
|
loop = async_loop.new_event_loop()
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
|
@ -205,7 +215,7 @@ def serve(router, host, port, debug=False):
|
||||||
# Serve
|
# Serve
|
||||||
log.info('Goin\' Fast @ {}:{}'.format(host, port))
|
log.info('Goin\' Fast @ {}:{}'.format(host, port))
|
||||||
|
|
||||||
server_coroutine = loop.create_server(lambda: HttpProtocol(loop=loop, router=router), host, port)
|
server_coroutine = loop.create_server(lambda: HttpProtocol(loop=loop, sanic=sanic), host, port)
|
||||||
server_loop = loop.run_until_complete(server_coroutine)
|
server_loop = loop.run_until_complete(server_coroutine)
|
||||||
try:
|
try:
|
||||||
loop.run_forever()
|
loop.run_forever()
|
||||||
|
|
9
test.py
9
test.py
|
@ -1,5 +1,6 @@
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
from sanic.response import json, text
|
from sanic.response import json, text
|
||||||
|
from sanic.exceptions import ServerError
|
||||||
|
|
||||||
app = Sanic("test")
|
app = Sanic("test")
|
||||||
|
|
||||||
|
@ -11,6 +12,14 @@ async def test(request):
|
||||||
def test(request):
|
def test(request):
|
||||||
return text('hi')
|
return text('hi')
|
||||||
|
|
||||||
|
@app.route("/exception")
|
||||||
|
def test(request):
|
||||||
|
raise ServerError("yep")
|
||||||
|
|
||||||
|
@app.route("/exception/async")
|
||||||
|
async def test(request):
|
||||||
|
raise ServerError("asunk")
|
||||||
|
|
||||||
@app.route("/post_json")
|
@app.route("/post_json")
|
||||||
def test(request):
|
def test(request):
|
||||||
return json({ "received": True, "message": request.json })
|
return json({ "received": True, "message": request.json })
|
||||||
|
|
Loading…
Reference in New Issue
Block a user