Skip to content

Commit

Permalink
bugfix : router function repeat exec (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
joway authored Sep 30, 2018
1 parent db020da commit 9050678
Show file tree
Hide file tree
Showing 15 changed files with 244 additions and 230 deletions.
84 changes: 27 additions & 57 deletions lemon/app.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import json
import logging.config
import traceback
import typing
from asyncio import get_event_loop
from functools import partial
from inspect import signature

from lemon.asgi import ASGIRequest
from lemon.config import settings
from lemon.const import MIME_TYPES
from lemon.context import Context
from lemon.exception import MiddlewareParamsError
from lemon.exception import LemonMiddlewareParamsError
from lemon.log import LOGGING_CONFIG_DEFAULTS, logger
from lemon.middleware import exception_middleware, cors_middleware
from lemon.server import serve
Expand All @@ -27,37 +24,27 @@ async def exec_middleware(ctx: Context, middleware_list: list, pos: int = 0):
:param ctx: Context instance
:param middleware_list: middleware registered on app
:param pos: position of the middleware in list
:param pos: current pos in middleware_list
"""
if pos >= len(middleware_list):
return

middleware = middleware_list[pos]
logger.debug(
'The No.{0} middleware : {1} started'.format(
pos,
middleware.__name__,
)
'middleware : %s started',
middleware.__name__,
)

try:
middleware_params = signature(middleware).parameters
if len(middleware_params) == 1:
return await middleware(ctx=ctx)
elif len(middleware_params) == 2:
return await middleware(
ctx=ctx,
nxt=partial(exec_middleware, ctx, middleware_list, pos + 1),
)
else:
raise MiddlewareParamsError
finally:
logger.debug(
'The No.{0} middleware : {1} finished'.format(
pos,
middleware.__name__,
)
middleware_params = signature(middleware).parameters
if len(middleware_params) == 1:
await middleware(ctx=ctx)
elif len(middleware_params) == 2:
return await middleware(
ctx=ctx,
nxt=partial(exec_middleware, ctx, middleware_list, pos + 1),
)
else:
raise LemonMiddlewareParamsError


class Lemon:
Expand All @@ -66,8 +53,7 @@ def __init__(self, config: dict = None, debug=False) -> None:
:param config: app config
:param debug: if debug == True , set log level to DEBUG , else is INFO
"""
self.config = config
settings.set_config(config=config)
settings.set_config(config=config or {})

self.middleware_list: list = []

Expand Down Expand Up @@ -103,35 +89,19 @@ async def _call(receive: typing.Callable, send: typing.Callable):
+ self.middleware_list \
+ self.post_process_middleware_list

try:
await exec_middleware(
ctx=ctx, middleware_list=middleware_chain
)
except Exception as e:
traceback.print_exc()
await send({
'type': 'http.response.start',
'status': 500,
'headers': [
['content-type', MIME_TYPES.APPLICATION_JSON, ]
],
})
await send({
'type': 'http.response.body',
'body': json.dumps({
'lemon': 'Internal Error',
}).encode(),
})
else:
await send({
'type': 'http.response.start',
'status': ctx.res.status,
'headers': ctx.res.raw_headers,
})
await send({
'type': 'http.response.body',
'body': ctx.res.raw_body,
})
await exec_middleware(
ctx=ctx, middleware_list=middleware_chain,
)

await send({
'type': 'http.response.start',
'status': ctx.res.status,
'headers': ctx.res.raw_headers,
})
await send({
'type': 'http.response.body',
'body': ctx.res.raw_body,
})

return _call

Expand Down
1 change: 1 addition & 0 deletions lemon/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def __getitem__(self, key: str):
# SERVER
'LEMON_SERVER_HOST': '127.0.0.1',
'LEMON_SERVER_PORT': '9999',
'LEMON_DEBUG': False,
# ROUTER
'LEMON_ROUTER_SLASH_SENSITIVE': False,
# CORS
Expand Down
4 changes: 2 additions & 2 deletions lemon/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def __setattr__(self, key, value) -> None:
# alias
if key == 'body':
self.res.body = value
if key == 'status':
elif key == 'status':
self.res.status = value
else:
self.__dict__[key] = value
Expand All @@ -30,7 +30,7 @@ def __getattr__(self, item) -> typing.Any:
# alias
if item == 'body':
return self.res.body
if item == 'status':
elif item == 'status':
return self.res.status
return self.__dict__[item]

Expand Down
58 changes: 38 additions & 20 deletions lemon/exception.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# ========== GeneralException ==========
import typing


Expand All @@ -8,43 +7,62 @@ def __init__(self, status=None, body: typing.Union[str, dict] = None) -> None:
self.body = body


# ========== RuntimeError - 500 ==========
class ServerError(GeneralException):
def __init__(self, *args, **kwargs) -> None:
super(ServerError, self).__init__(*args, **kwargs)
self.status = 500
# ========== RequestException 4xx ==========
class RequestBadError(GeneralException):
def __init__(self):
super().__init__(status=400, body={
'error': 'bad request'
})


class MiddlewareParamsError(ServerError):
pass
class RequestUnauthorizedError(GeneralException):
def __init__(self):
super().__init__(status=401, body={
'error': 'unauthorized'
})


class RouterRegisterError(ServerError):
pass
class RequestForbiddenError(GeneralException):
def __init__(self):
super().__init__(status=403, body={
'error': 'not found'
})


class RouterMatchError(ServerError):
class RequestNotFoundError(GeneralException):
def __init__(self):
super().__init__(status=404, body={
'error': 'not found'
})


class RequestHeadersParserError(RequestBadError):
pass


class ResponseFormatError(ServerError):
class RequestBodyParserError(RequestBadError):
pass


class LemonConfigKeyError(ServerError):
# ========== RuntimeError - 5xx ==========
class ServerError(GeneralException):
def __init__(self):
super().__init__(status=500, body={
'error': 'internal error'
})


class LemonMiddlewareParamsError(ServerError):
pass


# ========== BadRequestError - 400 ==========
class BadRequestError(GeneralException):
def __init__(self, *args, **kwargs) -> None:
super(BadRequestError, self).__init__(*args, **kwargs)
self.status = 400
class LemonRouterRegisterError(ServerError):
pass


class RequestHeadersParserError(BadRequestError):
class LemonRouterMatchError(ServerError):
pass


class RequestBodyParserError(BadRequestError):
class LemonConfigKeyError(ServerError):
pass
6 changes: 4 additions & 2 deletions lemon/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ async def exception_middleware(ctx: Context, nxt: typing.Callable) -> typing.Any
except GeneralException as e:
ctx.body = e.body
ctx.status = e.status
if settings.LEMON_DEBUG:
traceback.print_exc()
except Exception as e:
traceback.print_exc()
ctx.status = 500
ctx.body = ctx.body or {
'lemon': 'INTERNAL ERROR',
'error': 'unknown error',
}
traceback.print_exc()


async def cors_middleware(ctx: Context, nxt: typing.Callable):
Expand Down
14 changes: 5 additions & 9 deletions lemon/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
from werkzeug.http import parse_options_header
from werkzeug.urls import url_decode

import lemon.exception as exception
from lemon.const import MIME_TYPES
from lemon.exception import RequestHeadersParserError, RequestBodyParserError


def get_mimetype_and_options(headers: dict) -> typing.Tuple[str, dict]:
Expand All @@ -20,27 +20,23 @@ def get_mimetype_and_options(headers: dict) -> typing.Tuple[str, dict]:

def get_content_length(headers: dict) -> typing.Optional[int]:
if headers is None:
raise exception.RequestHeadersParserError
raise RequestHeadersParserError
content_length = headers.get('content-length')
if content_length is None:
return None
try:
return max(0, int(content_length))
except (ValueError, TypeError):
raise exception.RequestHeadersParserError
raise RequestHeadersParserError


def json_parser(body: bytes, *args) -> ImmutableMultiDict:
if not body:
raise exception.BadRequestError(body={
'error': 'Empty Body',
})
raise RequestBodyParserError
try:
return ImmutableMultiDict(json.loads(body.decode('utf-8')))
except json.JSONDecodeError:
raise exception.BadRequestError(body={
'error': 'Invalid JSON',
})
raise RequestBodyParserError


def url_encoded_parser(body: bytes, *args) -> dict:
Expand Down
1 change: 0 additions & 1 deletion lemon/response.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import json

from lemon.const import MIME_TYPES, CHARSETS
from lemon.exception import ResponseFormatError
from lemon.request import HttpHeaders


Expand Down
Loading

0 comments on commit 9050678

Please sign in to comment.