sanic-api 0.2.5__py3-none-any.whl → 0.2.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
sanic_api/__init__.py CHANGED
@@ -1,21 +1,21 @@
1
- from sanic import Sanic
2
- from sanic_ext import Extend
3
-
4
- from sanic_api import api, openapi
5
-
6
- from .api import ApiExtend
7
- from .logger import LoggerExtend
8
-
9
-
10
- def init_api(app: Sanic):
11
- """
12
- 初始化API
13
- Args:
14
- app: Sanic的APP
15
-
16
- Returns:
17
-
18
- """
19
- Extend.register(LoggerExtend)
20
- Extend.register(ApiExtend)
21
- openapi.init(app)
1
+ from sanic import Sanic
2
+ from sanic_ext import Extend
3
+
4
+ from sanic_api import api, openapi
5
+
6
+ from .api import ApiExtend
7
+ from .logger import LoggerExtend
8
+
9
+
10
+ def init_api(app: Sanic):
11
+ """
12
+ 初始化API
13
+ Args:
14
+ app: Sanic的APP
15
+
16
+ Returns:
17
+
18
+ """
19
+ Extend.register(LoggerExtend)
20
+ Extend.register(ApiExtend)
21
+ openapi.init(app)
sanic_api/api/__init__.py CHANGED
@@ -1,2 +1,2 @@
1
- from .api import API
2
- from .extend import ApiExtend
1
+ from .api import API
2
+ from .extend import ApiExtend
sanic_api/api/api.py CHANGED
@@ -1,177 +1,177 @@
1
- import functools
2
- from abc import ABCMeta
3
- from typing import Any, Callable, Dict, Optional
4
-
5
- from pydantic import BaseModel, Field
6
- from pydantic import ValidationError as PyDanticValidationError
7
- from sanic import HTTPResponse, Sanic
8
- from sanic.response import json
9
-
10
- from sanic_api.api.exception import ValidationError
11
- from sanic_api.api.model import ListRespModel, ResponseModel
12
- from sanic_api.enum import EnumBase, ParamEnum, RespCodeEnum
13
- from sanic_api.utils import json_dumps
14
-
15
-
16
- def get_resp_tmp(key: str, default):
17
- """
18
- 获取响应字段模板
19
- """
20
- app = Sanic.get_app()
21
- sanic_api: dict = app.config.get("sanic_api", {})
22
- return sanic_api.get(key, default)
23
-
24
-
25
- class Response:
26
- def __init__(
27
- self,
28
- http_code: int = 200,
29
- headers: Optional[Dict[str, str]] = None,
30
- server_code: EnumBase = RespCodeEnum.SUCCESS,
31
- message: str = "Success",
32
- data: Any = None,
33
- response_type: Optional[type] = None,
34
- ):
35
- """
36
- 初始化函数
37
- Args:
38
- http_code: http响应码 默认是200
39
- headers: 自定义的响应头
40
- server_code: 自定义的服务码
41
- message: 返回的消息
42
- data: 返回的数据
43
- """
44
-
45
- self.http_code = http_code
46
- self.headers = headers
47
- self.message = message
48
- response_type = response_type or Response.get_response_type(type(data))
49
-
50
- if isinstance(data, ListRespModel):
51
- data = data.to_list()
52
- elif isinstance(data, BaseModel):
53
- data = data.dict()
54
-
55
- resp: ResponseModel = response_type()
56
- code_tmp = get_resp_tmp("code_tmp", "code")
57
- msg_tmp = get_resp_tmp("msg_tmp", "msg")
58
- data_tmp = get_resp_tmp("data_tmp", "data")
59
-
60
- setattr(resp, code_tmp, server_code)
61
- setattr(resp, msg_tmp, message)
62
- setattr(resp, data_tmp, data)
63
-
64
- data = resp.dict()
65
-
66
- self.data = data
67
-
68
- def json_resp(self, default: Optional[Callable[[Any], Any]] = None) -> HTTPResponse:
69
- """
70
- 返回json格式的响应
71
- Args:
72
-
73
- """
74
- return json(
75
- body=self.data,
76
- status=self.http_code,
77
- headers=self.headers,
78
- dumps=functools.partial(json_dumps, default=default),
79
- )
80
-
81
- @classmethod
82
- def get_response_type(cls, data_type: type):
83
- """
84
- 获取响应的类型
85
- Args:
86
- data_type: 响应中data的类型
87
-
88
- Returns:
89
-
90
- """
91
- code_tmp = get_resp_tmp("code_tmp", "code")
92
- msg_tmp = get_resp_tmp("msg_tmp", "msg")
93
- data_tmp = get_resp_tmp("data_tmp", "data")
94
-
95
- attr_dict = {
96
- code_tmp: Field(title="业务响应码"),
97
- msg_tmp: Field(title="响应消息"),
98
- data_tmp: Field(title="响应数据"),
99
- "__annotations__": {code_tmp: int, msg_tmp: str, data_tmp: data_type},
100
- }
101
- resp_name = data_type.__name__ if data_type.__name__ != "NoneType" else "Response"
102
- response_type = type(resp_name, (ResponseModel,), attr_dict)
103
- return response_type
104
-
105
- @staticmethod
106
- def _get_tmp(key: str, default):
107
- """
108
- 获取字段模板
109
- """
110
- app = Sanic.get_app()
111
- sanic_api: dict = app.config.get("sanic_api", {})
112
- return sanic_api.get(key, default)
113
-
114
-
115
- class API(metaclass=ABCMeta):
116
- json_req: Optional[BaseModel] = None
117
- form_req: Optional[BaseModel] = None
118
- query_req: Optional[BaseModel] = None
119
- resp: Optional[Any] = None
120
- tags: list = []
121
- description: str = ""
122
-
123
- def __init__(self):
124
- response_type = self.__class__.__annotations__.get("resp")
125
- self.json_req_type = self.__class__.__annotations__.get("json_req")
126
- self.form_req_type = self.__class__.__annotations__.get("form_req")
127
- self.query_req_type = self.__class__.__annotations__.get("query_req")
128
- self.response_type = Response.get_response_type(response_type)
129
- self.resp = response_type()
130
-
131
- def validate_params(self, req_data: dict, param_enum: ParamEnum):
132
- """
133
- 验证参数,参数有问题抛出异常
134
- Args:
135
- req_data: 数据
136
- param_enum: 参数类型
137
-
138
- Returns:
139
-
140
- """
141
- try:
142
- if param_enum == ParamEnum.JSON:
143
- self.json_req = self.json_req_type(**req_data)
144
- elif param_enum == ParamEnum.QUERY:
145
- self.query_req = self.query_req_type(**req_data)
146
- elif param_enum == ParamEnum.FORM:
147
- self.form_req = self.form_req_type(**req_data)
148
- except PyDanticValidationError as e:
149
- raise ValidationError(e.errors()) from e
150
-
151
- def json_resp(
152
- self,
153
- http_code: int = 200,
154
- headers: Optional[Dict[str, str]] = None,
155
- server_code: RespCodeEnum = RespCodeEnum.SUCCESS,
156
- message: str = "Success",
157
- ):
158
- """
159
- 根据响应数据返回json格式的响应
160
- Args:
161
- http_code: http协议响应码
162
- headers: 响应头
163
- server_code: 应用协议响应码
164
- message: 响应消息
165
-
166
- Returns:
167
- 返回一个sanic的HTTPResponse
168
- """
169
-
170
- return Response(
171
- data=self.resp,
172
- http_code=http_code,
173
- headers=headers,
174
- server_code=server_code,
175
- message=message,
176
- response_type=self.response_type,
177
- ).json_resp()
1
+ import functools
2
+ from abc import ABCMeta
3
+ from typing import Any, Callable, Dict, Optional
4
+
5
+ from pydantic import BaseModel, Field
6
+ from pydantic import ValidationError as PyDanticValidationError
7
+ from sanic import HTTPResponse, Sanic
8
+ from sanic.response import json
9
+
10
+ from sanic_api.api.exception import ValidationError
11
+ from sanic_api.api.model import ListRespModel, ResponseModel
12
+ from sanic_api.enum import EnumBase, ParamEnum, RespCodeEnum
13
+ from sanic_api.utils import json_dumps
14
+
15
+
16
+ def get_resp_tmp(key: str, default):
17
+ """
18
+ 获取响应字段模板
19
+ """
20
+ app = Sanic.get_app()
21
+ sanic_api: dict = app.config.get("sanic_api", {})
22
+ return sanic_api.get(key, default)
23
+
24
+
25
+ class Response:
26
+ def __init__(
27
+ self,
28
+ http_code: int = 200,
29
+ headers: Optional[Dict[str, str]] = None,
30
+ server_code: EnumBase = RespCodeEnum.SUCCESS,
31
+ message: str = "Success",
32
+ data: Any = None,
33
+ response_type: Optional[type] = None,
34
+ ):
35
+ """
36
+ 初始化函数
37
+ Args:
38
+ http_code: http响应码 默认是200
39
+ headers: 自定义的响应头
40
+ server_code: 自定义的服务码
41
+ message: 返回的消息
42
+ data: 返回的数据
43
+ """
44
+
45
+ self.http_code = http_code
46
+ self.headers = headers
47
+ self.message = message
48
+ response_type = response_type or Response.get_response_type(type(data))
49
+
50
+ if isinstance(data, ListRespModel):
51
+ data = data.to_list()
52
+ elif isinstance(data, BaseModel):
53
+ data = data.dict()
54
+
55
+ resp: ResponseModel = response_type()
56
+ code_tmp = get_resp_tmp("code_tmp", "code")
57
+ msg_tmp = get_resp_tmp("msg_tmp", "msg")
58
+ data_tmp = get_resp_tmp("data_tmp", "data")
59
+
60
+ setattr(resp, code_tmp, server_code)
61
+ setattr(resp, msg_tmp, message)
62
+ setattr(resp, data_tmp, data)
63
+
64
+ data = resp.dict()
65
+
66
+ self.data = data
67
+
68
+ def json_resp(self, default: Optional[Callable[[Any], Any]] = None) -> HTTPResponse:
69
+ """
70
+ 返回json格式的响应
71
+ Args:
72
+
73
+ """
74
+ return json(
75
+ body=self.data,
76
+ status=self.http_code,
77
+ headers=self.headers,
78
+ dumps=functools.partial(json_dumps, default=default),
79
+ )
80
+
81
+ @classmethod
82
+ def get_response_type(cls, data_type: type):
83
+ """
84
+ 获取响应的类型
85
+ Args:
86
+ data_type: 响应中data的类型
87
+
88
+ Returns:
89
+
90
+ """
91
+ code_tmp = get_resp_tmp("code_tmp", "code")
92
+ msg_tmp = get_resp_tmp("msg_tmp", "msg")
93
+ data_tmp = get_resp_tmp("data_tmp", "data")
94
+
95
+ attr_dict = {
96
+ code_tmp: Field(title="业务响应码"),
97
+ msg_tmp: Field(title="响应消息"),
98
+ data_tmp: Field(title="响应数据"),
99
+ "__annotations__": {code_tmp: int, msg_tmp: str, data_tmp: data_type},
100
+ }
101
+ resp_name = data_type.__name__ if data_type.__name__ != "NoneType" else "Response"
102
+ response_type = type(resp_name, (ResponseModel,), attr_dict)
103
+ return response_type
104
+
105
+ @staticmethod
106
+ def _get_tmp(key: str, default):
107
+ """
108
+ 获取字段模板
109
+ """
110
+ app = Sanic.get_app()
111
+ sanic_api: dict = app.config.get("sanic_api", {})
112
+ return sanic_api.get(key, default)
113
+
114
+
115
+ class API(metaclass=ABCMeta):
116
+ json_req: Optional[BaseModel] = None
117
+ form_req: Optional[BaseModel] = None
118
+ query_req: Optional[BaseModel] = None
119
+ resp: Optional[Any] = None
120
+ tags: list = []
121
+ description: str = ""
122
+
123
+ def __init__(self):
124
+ response_type = self.__class__.__annotations__.get("resp")
125
+ self.json_req_type = self.__class__.__annotations__.get("json_req")
126
+ self.form_req_type = self.__class__.__annotations__.get("form_req")
127
+ self.query_req_type = self.__class__.__annotations__.get("query_req")
128
+ self.response_type = Response.get_response_type(response_type)
129
+ self.resp = response_type()
130
+
131
+ def validate_params(self, req_data: dict, param_enum: ParamEnum):
132
+ """
133
+ 验证参数,参数有问题抛出异常
134
+ Args:
135
+ req_data: 数据
136
+ param_enum: 参数类型
137
+
138
+ Returns:
139
+
140
+ """
141
+ try:
142
+ if param_enum == ParamEnum.JSON:
143
+ self.json_req = self.json_req_type(**req_data)
144
+ elif param_enum == ParamEnum.QUERY:
145
+ self.query_req = self.query_req_type(**req_data)
146
+ elif param_enum == ParamEnum.FORM:
147
+ self.form_req = self.form_req_type(**req_data)
148
+ except PyDanticValidationError as e:
149
+ raise ValidationError(e.errors()) from e
150
+
151
+ def json_resp(
152
+ self,
153
+ http_code: int = 200,
154
+ headers: Optional[Dict[str, str]] = None,
155
+ server_code: RespCodeEnum = RespCodeEnum.SUCCESS,
156
+ message: str = "Success",
157
+ ):
158
+ """
159
+ 根据响应数据返回json格式的响应
160
+ Args:
161
+ http_code: http协议响应码
162
+ headers: 响应头
163
+ server_code: 应用协议响应码
164
+ message: 响应消息
165
+
166
+ Returns:
167
+ 返回一个sanic的HTTPResponse
168
+ """
169
+
170
+ return Response(
171
+ data=self.resp,
172
+ http_code=http_code,
173
+ headers=headers,
174
+ server_code=server_code,
175
+ message=message,
176
+ response_type=self.response_type,
177
+ ).json_resp()
@@ -1,46 +1,46 @@
1
- from typing import Any, Optional
2
-
3
- from sanic.exceptions import SanicException
4
-
5
- from sanic_api.enum import EnumBase, RespCodeEnum
6
- from sanic_api.utils import get_current_request
7
-
8
-
9
- class ServerException(SanicException):
10
- def __init__(
11
- self,
12
- message: Optional[str] = None,
13
- status_code: Optional[int] = None,
14
- server_code: Optional[EnumBase] = None,
15
- quiet: Optional[bool] = None,
16
- context: Any = None,
17
- extra=None,
18
- ):
19
- super().__init__(
20
- message=message,
21
- status_code=status_code,
22
- quiet=quiet,
23
- context=context,
24
- extra=extra,
25
- )
26
- self.server_code = server_code or RespCodeEnum.FAILED
27
- self.message = message or self.server_code.desc
28
- self.status_code = status_code or 200
29
- req = get_current_request()
30
- if req:
31
- req.ctx.exception = self
32
-
33
-
34
- class ValidationInitError(ServerException):
35
- """
36
- 验证器初始化失败
37
- """
38
-
39
-
40
- class ValidationError(ServerException):
41
- """
42
- 参数验证失败
43
- """
44
-
45
- def __init__(self, errors: list, *args, **kwargs):
46
- super().__init__(server_code=RespCodeEnum.PARAM_FAILED, context=errors)
1
+ from typing import Any, Optional
2
+
3
+ from sanic.exceptions import SanicException
4
+
5
+ from sanic_api.enum import EnumBase, RespCodeEnum
6
+ from sanic_api.utils import get_current_request
7
+
8
+
9
+ class ServerException(SanicException):
10
+ def __init__(
11
+ self,
12
+ message: Optional[str] = None,
13
+ status_code: Optional[int] = None,
14
+ server_code: Optional[EnumBase] = None,
15
+ quiet: Optional[bool] = None,
16
+ context: Any = None,
17
+ extra=None,
18
+ ):
19
+ super().__init__(
20
+ message=message,
21
+ status_code=status_code,
22
+ quiet=quiet,
23
+ context=context,
24
+ extra=extra,
25
+ )
26
+ self.server_code = server_code or RespCodeEnum.FAILED
27
+ self.message = message or self.server_code.desc
28
+ self.status_code = status_code or 200
29
+ req = get_current_request()
30
+ if req:
31
+ req.ctx.exception = self
32
+
33
+
34
+ class ValidationInitError(ServerException):
35
+ """
36
+ 验证器初始化失败
37
+ """
38
+
39
+
40
+ class ValidationError(ServerException):
41
+ """
42
+ 参数验证失败
43
+ """
44
+
45
+ def __init__(self, errors: list, *args, **kwargs):
46
+ super().__init__(server_code=RespCodeEnum.PARAM_FAILED, context=errors)
sanic_api/api/extend.py CHANGED
@@ -1,25 +1,25 @@
1
- from sanic.exceptions import NotFound
2
- from sanic_ext import Extension
3
-
4
- from sanic_api.api.exception import ServerException
5
-
6
- from .handle_exception import not_found, other_exception, server_exception
7
- from .validators import validators
8
-
9
-
10
- class ApiExtend(Extension):
11
- """
12
- 接口处理扩展
13
- 参数校验、异常拦截等功能
14
- """
15
-
16
- name = "ApiExtend"
17
-
18
- def startup(self, bootstrap) -> None:
19
- if not self.included():
20
- return
21
-
22
- self.app.error_handler.add(ServerException, server_exception)
23
- self.app.error_handler.add(Exception, other_exception)
24
- self.app.error_handler.add(NotFound, not_found)
25
- self.app.on_request(validators, priority=998)
1
+ from sanic.exceptions import NotFound
2
+ from sanic_ext import Extension
3
+
4
+ from sanic_api.api.exception import ServerException
5
+
6
+ from .handle_exception import not_found, other_exception, server_exception
7
+ from .validators import validators
8
+
9
+
10
+ class ApiExtend(Extension):
11
+ """
12
+ 接口处理扩展
13
+ 参数校验、异常拦截等功能
14
+ """
15
+
16
+ name = "ApiExtend"
17
+
18
+ def startup(self, bootstrap) -> None:
19
+ if not self.included():
20
+ return
21
+
22
+ self.app.error_handler.add(ServerException, server_exception)
23
+ self.app.error_handler.add(Exception, other_exception)
24
+ self.app.error_handler.add(NotFound, not_found)
25
+ self.app.on_request(validators, priority=998)
@@ -1,61 +1,61 @@
1
- from sanic import Request
2
- from sanic.log import logger
3
-
4
- from sanic_api.api.api import Response
5
- from sanic_api.api.exception import ServerException
6
- from sanic_api.enum import RespCodeEnum
7
-
8
-
9
- def server_exception(request: Request, e: ServerException):
10
- """
11
- 处理业务异常
12
- Args:
13
- request: 请求
14
- e: 异常信息
15
-
16
- Returns:
17
- 返回处理异常后的响应
18
- """
19
-
20
- return Response(
21
- http_code=e.status_code,
22
- server_code=e.server_code,
23
- message=e.message,
24
- data=e.context,
25
- ).json_resp()
26
-
27
-
28
- def not_found(request: Request, e):
29
- """
30
- 处理 NotFound 异常
31
- Args:
32
- request: 请求信息
33
- e: 异常信息
34
-
35
- Returns:
36
-
37
- """
38
- if "/favicon.ico" == request.path:
39
- return
40
-
41
- logger.warning(f"找不到路由:{request.path}")
42
-
43
-
44
- def other_exception(request: Request, e):
45
- """
46
- 处理其他异常
47
- Args:
48
- request:
49
- e:
50
-
51
- Returns:
52
-
53
- """
54
- logger.exception(e)
55
-
56
- error_name = e.__class__.__name__
57
- return Response(
58
- http_code=500,
59
- server_code=RespCodeEnum.FAILED,
60
- message=f"服务端业务发生未知异常:[{error_name}] {e}",
61
- ).json_resp()
1
+ from sanic import Request
2
+ from sanic.log import logger
3
+
4
+ from sanic_api.api.api import Response
5
+ from sanic_api.api.exception import ServerException
6
+ from sanic_api.enum import RespCodeEnum
7
+
8
+
9
+ def server_exception(request: Request, e: ServerException):
10
+ """
11
+ 处理业务异常
12
+ Args:
13
+ request: 请求
14
+ e: 异常信息
15
+
16
+ Returns:
17
+ 返回处理异常后的响应
18
+ """
19
+
20
+ return Response(
21
+ http_code=e.status_code,
22
+ server_code=e.server_code,
23
+ message=e.message,
24
+ data=e.context,
25
+ ).json_resp()
26
+
27
+
28
+ def not_found(request: Request, e):
29
+ """
30
+ 处理 NotFound 异常
31
+ Args:
32
+ request: 请求信息
33
+ e: 异常信息
34
+
35
+ Returns:
36
+
37
+ """
38
+ if "/favicon.ico" == request.path:
39
+ return
40
+
41
+ logger.warning(f"找不到路由:{request.path}")
42
+
43
+
44
+ def other_exception(request: Request, e):
45
+ """
46
+ 处理其他异常
47
+ Args:
48
+ request:
49
+ e:
50
+
51
+ Returns:
52
+
53
+ """
54
+ logger.exception(e)
55
+
56
+ error_name = e.__class__.__name__
57
+ return Response(
58
+ http_code=500,
59
+ server_code=RespCodeEnum.FAILED,
60
+ message=f"服务端业务发生未知异常:[{error_name}] {e}",
61
+ ).json_resp()