formatting fixes

This commit is contained in:
iLLiCiTiT 2019-11-25 10:49:52 +01:00
parent 4201ba173d
commit 343cdf55c1
5 changed files with 72 additions and 34 deletions

View file

@ -1,4 +1,3 @@
from functools import wraps
from http import HTTPStatus from http import HTTPStatus
from .lib import ( from .lib import (
@ -15,9 +14,12 @@ def route(path, url_prefix="", methods=[], strict_match=False):
:type path: str :type path: str
:param url_prefix: Specify prefix of path, defaults to "/". :param url_prefix: Specify prefix of path, defaults to "/".
:type url_prefix: str, list, optional :type url_prefix: str, list, optional
:param methods: Specify request method (GET, POST, PUT, etc.) when callback will be triggered, defaults to ["GET"] :param methods: Specify request method (GET, POST, PUT, etc.) when
callback will be triggered, defaults to ["GET"]
:type methods: list, str, optional :type methods: list, str, optional
:param strict_match: Decides if callback can handle both single and multiple entities (~/projects/<project_name> && ~/projects/), defaults to False. :param strict_match: Decides if callback can handle both single and
multiple entities (~/projects/<project_name> && ~/projects/),
defaults to False.
:type strict_match: bool :type strict_match: bool
`path` may include dynamic keys that will be stored to object which can `path` may include dynamic keys that will be stored to object which can
@ -36,6 +38,7 @@ def route(path, url_prefix="", methods=[], strict_match=False):
In this case request path must be "/avalon/projects" to trigger registered In this case request path must be "/avalon/projects" to trigger registered
callback. callback.
""" """
def decorator(callback): def decorator(callback):
RestApiFactory.register_route( RestApiFactory.register_route(
path, callback, url_prefix, methods, strict_match path, callback, url_prefix, methods, strict_match
@ -49,26 +52,32 @@ def register_statics(url_prefix, dir_path):
"""Decorator that register callback and all its attributes. """Decorator that register callback and all its attributes.
Callback is registered to Singleton RestApiFactory. Callback is registered to Singleton RestApiFactory.
:param url_prefix: Specify prefix of path, defaults to "/". (Example: "/resources") :param url_prefix: Specify prefix of path, defaults to "/".
(Example: "/resources")
:type url_prefix: str :type url_prefix: str
:param dir_path: Path to file folder where statics are located. :param dir_path: Path to file folder where statics are located.
:type dir_path: str :type dir_path: str
""" """
RestApiFactory.register_statics((url_prefix, dir_path)) RestApiFactory.register_statics((url_prefix, dir_path))
def abort(status_code=HTTPStatus.NOT_FOUND, message=None): def abort(status_code=HTTPStatus.NOT_FOUND, message=None):
"""Should be used to stop registered callback """Should be used to stop registered callback.
`abort` raise AbortException that is handled with request Handler which `abort` raise AbortException that is handled with request Handler which
returns entered status and may send optional message in body. returns entered status and may send optional message in body.
:param status_code: Status that will be send in reply of request, defaults to 404 :param status_code: Status that will be send in reply of request,
defaults to 404
:type status_code: int :type status_code: int
:param message: Message to send in body, default messages are based on statuc_code in Handler, defaults to None :param message: Message to send in body, default messages are based on
statuc_code in Handler, defaults to None
:type message: str, optional :type message: str, optional
... ...
:raises AbortException: This exception is handled in Handler to know about launched `abort` :raises AbortException: This exception is handled in Handler to know
about launched `abort`
""" """
items = [] items = []
items.append(str(status_code)) items.append(str(status_code))
if not message: if not message:
@ -85,9 +94,10 @@ class RestApi:
Use this class is required when it is necessary to have class for handling Use this class is required when it is necessary to have class for handling
requests and want to use decorators for registering callbacks. requests and want to use decorators for registering callbacks.
It is possible to use decorators in another class only when object, of class It is possible to use decorators in another class only when object,
where decorators are, is registered to RestApiFactory. of class where decorators are, is registered to RestApiFactory.
""" """
def route(path, url_prefix="", methods=[], strict_match=False): def route(path, url_prefix="", methods=[], strict_match=False):
return route(path, url_prefix, methods, strict_match) return route(path, url_prefix, methods, strict_match)

View file

@ -8,4 +8,5 @@ class ObjAlreadyExist(Exception):
super().__init__(message) super().__init__(message)
class AbortException(Exception): pass class AbortException(Exception):
pass

View file

@ -116,7 +116,8 @@ def prepare_methods(methods, callback=None):
:param methods: Contain rest api methods, when callback is called. :param methods: Contain rest api methods, when callback is called.
:type methods: str, list :type methods: str, list
:param callback: Registered callback, helps to identify where is invalid method. :param callback: Registered callback, helps to identify where is
invalid method.
:type callback: function, method, optional :type callback: function, method, optional
:return: Valid methods :return: Valid methods
:rtype: list :rtype: list
@ -138,7 +139,7 @@ def prepare_methods(methods, callback=None):
for method in methods: for method in methods:
found = False found = False
_method = RestMethods.get(method) _method = RestMethods.get(method)
if _method == None: if _method is None:
invalid_methods[methods].append(callback) invalid_methods[methods].append(callback)
continue continue
@ -169,6 +170,7 @@ def prepare_methods(methods, callback=None):
return _methods return _methods
def prepare_callback_info(callback): def prepare_callback_info(callback):
"""Prepare data for callback handling when should be triggered.""" """Prepare data for callback handling when should be triggered."""
callback_info = inspect.getfullargspec(callback) callback_info = inspect.getfullargspec(callback)
@ -228,7 +230,9 @@ class _RestApiFactory:
self.unprocessed_statics.index(item) self.unprocessed_statics.index(item)
) )
def register_route(self, path, callback, url_prefix, methods, strict_match): def register_route(
self, path, callback, url_prefix, methods, strict_match
):
log.debug("Registering callback for item \"{}\"".format( log.debug("Registering callback for item \"{}\"".format(
callback.__qualname__ callback.__qualname__
)) ))
@ -242,7 +246,7 @@ class _RestApiFactory:
self.unprocessed_routes.append(route) self.unprocessed_routes.append(route)
def register_obj(self, obj): def register_obj(self, obj):
"""Register object for decorated methods in class definition""" """Register object for decorated methods in class definition."""
self.registered_objs.append(obj) self.registered_objs.append(obj)
def register_statics(self, item): def register_statics(self, item):
@ -255,7 +259,8 @@ class _RestApiFactory:
Registration info are prepared to easy filter during handling Registration info are prepared to easy filter during handling
of requests. of requests.
:param route: Contain all necessary info for filtering and handling callback for registered route. :param route: Contain all necessary info for filtering and
handling callback for registered route.
:type route: dict :type route: dict
""" """
callback = route["callback"] callback = route["callback"]
@ -278,7 +283,7 @@ class _RestApiFactory:
}) })
def prepare_registered(self): def prepare_registered(self):
"""Iterate through all registered callbacks and statics and prepare them """Iter through all registered callbacks and statics to prepare them.
First are processed callbacks registered with decorators in classes by First are processed callbacks registered with decorators in classes by
registered objects. Remaining callbacks are filtered, it is checked if registered objects. Remaining callbacks are filtered, it is checked if
@ -314,7 +319,9 @@ class _RestApiFactory:
if not ( if not (
callback.__qualname__ == method.__qualname__ and callback.__qualname__ == method.__qualname__ and
callback.__module__ == method.__module__ and callback.__module__ == method.__module__ and
callback.__globals__["__file__"] == method.__globals__["__file__"] callback.__globals__["__file__"] == (
method.__globals__["__file__"]
)
): ):
continue continue

View file

@ -32,6 +32,7 @@ class Handler(http.server.SimpleHTTPRequestHandler):
"NO_CONTENT": 204 "NO_CONTENT": 204
} }
} }
def do_GET(self): def do_GET(self):
return self._handle_request(RestMethods.GET) return self._handle_request(RestMethods.GET)
@ -156,7 +157,6 @@ class Handler(http.server.SimpleHTTPRequestHandler):
self.wfile.write(message.encode()) self.wfile.write(message.encode())
return message return message
def _handle_callback_result(self, result, rest_method): def _handle_callback_result(self, result, rest_method):
"""Send response to request based on result of callback. """Send response to request based on result of callback.
:param result: Result returned by callback. :param result: Result returned by callback.
@ -232,7 +232,7 @@ class Handler(http.server.SimpleHTTPRequestHandler):
:param item: Item stored during callback registration with all info. :param item: Item stored during callback registration with all info.
:type item: dict :type item: dict
:param parsed_url: Url parsed with urllib (separated path, query, etc.). :param parsed_url: Url parsed with urllib (separated path, query, etc).
:type parsed_url: ParseResult :type parsed_url: ParseResult
:param rest_method: Rest api method (GET, POST, etc.). :param rest_method: Rest api method (GET, POST, etc.).
:type rest_method: RestMethods :type rest_method: RestMethods
@ -284,7 +284,7 @@ class Handler(http.server.SimpleHTTPRequestHandler):
return callback(*args, **kwargs) return callback(*args, **kwargs)
def _handle_statics(self, dirpath, path): def _handle_statics(self, dirpath, path):
"""Return static file in response when file exist in registered destination.""" """Return static file in response when file exist in destination."""
path = os.path.normpath(dirpath + path) path = os.path.normpath(dirpath + path)
ctype = self.guess_type(path) ctype = self.guess_type(path)
@ -327,12 +327,17 @@ class Handler(http.server.SimpleHTTPRequestHandler):
self.send_response(HTTPStatus.OK) self.send_response(HTTPStatus.OK)
self.send_header("Content-type", ctype) self.send_header("Content-type", ctype)
self.send_header("Content-Length", str(file_stat[6])) self.send_header("Content-Length", str(file_stat[6]))
self.send_header("Last-Modified", self.send_header(
self.date_time_string(file_stat.st_mtime)) "Last-Modified",
self.date_time_string(file_stat.st_mtime)
)
self.end_headers() self.end_headers()
self.wfile.write(file_obj.read()) self.wfile.write(file_obj.read())
return file_obj return file_obj
except: except Exception:
self.log.error("Failed to read data from file \"{}\"".format(path)) log.error(
"Failed to read data from file \"{}\"".format(path),
exc_info=True
)
finally: finally:
file_obj.close() file_obj.close()

View file

@ -65,12 +65,22 @@ class HandlerDict(dict):
def __repr__(self): def __repr__(self):
return "<{}> {}".format(self.__class__.__name__, str(dict(self))) return "<{}> {}".format(self.__class__.__name__, str(dict(self)))
class Params(HandlerDict): pass
class UrlData(HandlerDict): pass class Params(HandlerDict):
class RequestData(HandlerDict): pass pass
class UrlData(HandlerDict):
pass
class RequestData(HandlerDict):
pass
class Query(HandlerDict): class Query(HandlerDict):
"""Class for url query convert to dict and string""" """Class for url query convert to dict and string."""
def __init__(self, query): def __init__(self, query):
if isinstance(query, dict): if isinstance(query, dict):
pass pass
@ -81,8 +91,10 @@ class Query(HandlerDict):
def get_string(self): def get_string(self):
return urlencode(dict(self), doseq=True) return urlencode(dict(self), doseq=True)
class Fragment(HandlerDict): class Fragment(HandlerDict):
"""Class for url fragment convert to dict and string""" """Class for url fragment convert to dict and string."""
def __init__(self, fragment): def __init__(self, fragment):
if isinstance(fragment, dict): if isinstance(fragment, dict):
_fragment = fragment _fragment = fragment
@ -112,13 +124,15 @@ class Fragment(HandlerDict):
) )
return "&".join(items) return "&".join(items)
class RequestInfo: class RequestInfo:
"""Object that can be passed to callback as argument. """Object that can be passed to callback as argument.
Contain necessary data for handling request. Contain necessary data for handling request.
Object is created to single use and can be used similar to dict. Object is created to single use and can be used similar to dict.
:param url_data: Data collected from path when path with dynamic keys is matching. :param url_data: Data collected from path when path with dynamic keys
is matching.
:type url_data: dict, None :type url_data: dict, None
:param request_data: Data of body from request. :param request_data: Data of body from request.
:type request_data: dict, None :type request_data: dict, None
@ -166,14 +180,15 @@ class CallbackResult:
"""Can be used as return value of callback. """Can be used as return value of callback.
It is possible to specify status code, success boolean, message and data It is possible to specify status code, success boolean, message and data
for specify head and body of request response. `abort` should be rather used for specify head and body of request response. `abort` should be rather
when result is error. used when result is error.
:param status_code: Status code of result. :param status_code: Status code of result.
:type status_code: int :type status_code: int
:param success: Success is key in body, may be used for handling response. :param success: Success is key in body, may be used for handling response.
:type success: bool :type success: bool
:param message: Similar to success, message is key in body and may be used for handling response. :param message: Similar to success, message is key in body and may
be used for handling response.
:type message: str, None :type message: str, None
:param data: Data is also key for body in response. :param data: Data is also key for body in response.
:type data: dict, None :type data: dict, None