Files
controller-wisetopdataserver/python/biopro/util/router.py
T
2021-12-20 14:52:55 +08:00

231 lines
6.4 KiB
Python

from collections import namedtuple
from typing import Tuple, Optional, Type
from biopro.util.json import *
RouterRule = namedtuple('RouterRule',
('path',
'method',
'func_name',
'func_para',
'cookie'))
class RouterHandle:
def __init__(self):
self.rule: List[RouterRule] = []
def append_rule(self,
path: str,
method: str,
name: str,
para: Dict[str, str],
cookie: Optional[Dict[str, str]] = None) -> RouterRule:
ret = RouterRule(path, method, name, para, cookie)
self.rule.append(ret)
return ret
def append_doc(self, path: str, method: str, doc: Optional[str]) -> Optional[str]:
if doc is None:
return doc
doc_line: List[str] = doc.split('\n')
for i, line in enumerate(doc_line):
if '**Request Url**' in line:
break
else:
return
leading = line[:len(line) - len(line.lstrip())]
doc_line.insert(i + 2, leading + '* ``' + method + '`` : ``' + path + '``')
doc_line.insert(i + 3, '')
return '\n'.join(doc_line)
def append_cookie_doc(self, doc: Optional[str], cookie: Tuple[str, ...]) -> Optional[str]:
if doc is None:
return doc
doc_line: List[str] = doc.split('\n')
for i, line in enumerate(doc_line):
if '**Used Cookie**' in line:
break
else:
return
leading = line[:len(line) - len(line.lstrip())]
doc_line.insert(i + 2, '')
for j, c in enumerate(cookie):
doc_line.insert(i + 2 + j, leading + '* ``' + c + '`` ')
return '\n'.join(doc_line)
# noinspection PyPep8Naming
def router(path: str,
GET: Dict[str, str] = None,
PUT: Dict[str, str] = None,
POST: Dict[str, str] = None,
DEL: Dict[str, str] = None,
COOKIE: Tuple[str, ...] = None):
"""**decorator** router mapper.
*method declaration*
for simplest GET mapping ::
@router('hello')
def hello(self):
\"""hello document
\"""
return 'hello'
# localhost/hello -> hello
for GET mapping with parameter in path ::
@router('hello/<str:name>')
def hello(self, name: str):
return 'hello ' + name
# localhost/hello/world -> hello world
for GET mapping with default parameter::
@router('hello/', GET=dict(name='guest'))
@router('hello/<str:name>')
def hello(self, name: str):
return 'hello ' + name
# localhost/hello -> hello guest
# localhost/hello/world -> hello world
for GET and POST mapping with restrict parameter in options. ::
@router('hello')
def hello(self, *, name: str = 'guest'):
return 'hello ' + name
# localhost/hello?name=world -> hello world
for GET and POST mapping with wired parameter in options. ::
@router('hello/')
def hello(self, **options: str):
return 'hello ' + str(options)
# localhost/hello?name=world -> hello {'name': 'world'}
for other mapping ::
@router('hello/',
GET=dict(name='get'),
PUT=dict(name='put'),
POST=dict(name='post'),
DEL=dict(name='delete'))
def hello(self, name: str):
return 'hello ' + name
for request body (for PUT, POST, DEL) ::
@router('hello/', PUT={})
def hello(self, content: str):
return 'hello ' + content
for cookie use ::
@router('hello', COOKIE=('user_session',))
def hello(self, *, user_session: Optional[str]):
return 'hello ' + str(user_session)
for cookie use and set ::
@router('hello', COOKIE=('user_session',))
def hello(self, *, user_session: Optional[str]):
if user_session is None:
user_session = 'guest'
response = ComplexResponse('hello ' + str(user_session))
response.set_cookie(user_session=user_session)
return response
*method document field*
* ``**Request Url**``
* ``**Request Body**``
* ``**Response Specification**``
* ``**Used Cookie**``
*special cookie*
* ``user_session``
:param path: router path
:param GET: method GET options
:param PUT: method PUT options
:param POST: method POST options
:param DEL: method DELETE options
:param COOKIE: cookie map
"""
def inner(f):
h = getattr(f, '_bps_router_', None)
if h is None:
h = RouterHandle()
setattr(f, '_bps_router_', h)
if GET is None and PUT is None and POST is None and DEL is None:
h.append_rule(path, 'GET', f.__name__, {}, COOKIE)
f.__doc__ = h.append_doc(path, 'GET', f.__doc__)
else:
if DEL is not None:
h.append_rule(path, 'DELETE', f.__name__, DEL, COOKIE)
f.__doc__ = h.append_doc(path, 'DELETE', f.__doc__)
if POST is not None:
h.append_rule(path, 'POST', f.__name__, POST, COOKIE)
f.__doc__ = h.append_doc(path, 'POST', f.__doc__)
if PUT is not None:
h.append_rule(path, 'PUT', f.__name__, PUT, COOKIE)
f.__doc__ = h.append_doc(path, 'PUT', f.__doc__)
if GET is not None:
h.append_rule(path, 'GET', f.__name__, GET, COOKIE)
f.__doc__ = h.append_doc(path, 'GET', f.__doc__)
if COOKIE is not None:
f.__doc__ = h.append_cookie_doc(f.__doc__, COOKIE)
return f
return inner
class Router(type):
def __new__(mcs, define_class_name: str, super_class: Tuple[Type, ...], class_attr: Dict[str, Any]):
router = []
for attr_name, attr_value in class_attr.items():
if callable(attr_value):
router_handle: RouterHandle = getattr(attr_value, '_bps_router_', None)
if router_handle is not None:
router.extend(router_handle.rule)
class_attr['_bps_router_rules_'] = router
return type(define_class_name, super_class, class_attr)