sanic-api 0.3.0a6__py3-none-any.whl → 0.4.0__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 +1 -1
- sanic_api/api/__init__.py +0 -16
- sanic_api/api/error.py +19 -0
- sanic_api/api/request.py +38 -3
- sanic_api/app.py +23 -5
- sanic_api/config/setting.py +38 -1
- sanic_api/openapi/extension.py +41 -0
- {sanic_api-0.3.0a6.dist-info → sanic_api-0.4.0.dist-info}/METADATA +2 -4
- sanic_api-0.4.0.dist-info/RECORD +19 -0
- sanic_api/api/response.py +0 -34
- sanic_api-0.3.0a6.dist-info/RECORD +0 -19
- {sanic_api-0.3.0a6.dist-info → sanic_api-0.4.0.dist-info}/WHEEL +0 -0
- {sanic_api-0.3.0a6.dist-info → sanic_api-0.4.0.dist-info}/licenses/LICENSE.txt +0 -0
sanic_api/__init__.py
CHANGED
sanic_api/api/__init__.py
CHANGED
@@ -1,17 +1 @@
|
|
1
1
|
from sanic_api.api.request import Request
|
2
|
-
from sanic_api.api.response import BaseResp, BaseRespTml, TempModel
|
3
|
-
|
4
|
-
"""
|
5
|
-
class BaseResponseModel(BaseModel):
|
6
|
-
"直接返回str、int、float、bool等类型"
|
7
|
-
...
|
8
|
-
|
9
|
-
class JsonResponseModel(BaseModel):
|
10
|
-
"返回JSON的Pydantic结构"
|
11
|
-
...
|
12
|
-
|
13
|
-
class ListResponseModel(BaseModel):
|
14
|
-
"返回JSON的Pydantic结构的列表"
|
15
|
-
...
|
16
|
-
|
17
|
-
"""
|
sanic_api/api/error.py
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
from sanic.errorpages import JSONRenderer
|
2
|
+
|
3
|
+
from sanic_api.config.setting import JsonRespSettings
|
4
|
+
|
5
|
+
|
6
|
+
class ErrorJSONRenderer(JSONRenderer):
|
7
|
+
json_resp_setting: JsonRespSettings | None = None
|
8
|
+
|
9
|
+
def _generate_output(self, *, full: bool) -> dict:
|
10
|
+
data = super()._generate_output(full=full)
|
11
|
+
if not self.json_resp_setting or not self.json_resp_setting.use_tml:
|
12
|
+
return data
|
13
|
+
|
14
|
+
data = {
|
15
|
+
self.json_resp_setting.code_field_name: self.json_resp_setting.error_code,
|
16
|
+
self.json_resp_setting.msg_field_name: data["message"],
|
17
|
+
self.json_resp_setting.data_field_name: data,
|
18
|
+
}
|
19
|
+
return data
|
sanic_api/api/request.py
CHANGED
@@ -4,8 +4,12 @@ from typing import get_origin
|
|
4
4
|
|
5
5
|
from pydantic import BaseModel
|
6
6
|
from sanic import Request as SanicRequest
|
7
|
+
from sanic.compat import Header
|
8
|
+
from sanic.response import JSONResponse
|
7
9
|
from sanic_ext.extensions.openapi.builders import OperationStore
|
8
10
|
|
11
|
+
from sanic_api.config.setting import JsonRespSettings
|
12
|
+
|
9
13
|
|
10
14
|
class Request(SanicRequest):
|
11
15
|
"""
|
@@ -13,6 +17,8 @@ class Request(SanicRequest):
|
|
13
17
|
加入便于接口参数获取校验的方法
|
14
18
|
"""
|
15
19
|
|
20
|
+
json_resp_setting: JsonRespSettings
|
21
|
+
|
16
22
|
json_data: BaseModel
|
17
23
|
form_data: BaseModel
|
18
24
|
query_data: BaseModel
|
@@ -22,8 +28,38 @@ class Request(SanicRequest):
|
|
22
28
|
_form_data_type: type[BaseModel] | None
|
23
29
|
_query_data_type: type[BaseModel] | None
|
24
30
|
|
25
|
-
def
|
26
|
-
|
31
|
+
def json_resp(
|
32
|
+
self,
|
33
|
+
data: BaseModel | dict | str | int | float | list,
|
34
|
+
server_code: str | int = "",
|
35
|
+
server_msg: str = "",
|
36
|
+
status: int = 200,
|
37
|
+
headers: Header | dict[str, str] | None = None,
|
38
|
+
) -> JSONResponse:
|
39
|
+
"""
|
40
|
+
自定义返回 JSONResponse 的方法
|
41
|
+
如果使用了模板模式则返回模板模式的数据
|
42
|
+
不使用模版情况下:如果data是dict、list直接返回,否则返回{"data": data}
|
43
|
+
Args:
|
44
|
+
data: 内容
|
45
|
+
server_code: 接口服务码
|
46
|
+
server_msg: 接口服务消息
|
47
|
+
status: 状态码
|
48
|
+
headers: 请求头
|
49
|
+
|
50
|
+
Returns:
|
51
|
+
|
52
|
+
"""
|
53
|
+
data = data.model_dump(mode="json") if isinstance(data, BaseModel) else data
|
54
|
+
if self.json_resp_setting.use_tml:
|
55
|
+
data = {
|
56
|
+
self.json_resp_setting.code_field_name: server_code,
|
57
|
+
self.json_resp_setting.msg_field_name: server_msg,
|
58
|
+
self.json_resp_setting.data_field_name: data,
|
59
|
+
}
|
60
|
+
else:
|
61
|
+
data = {"data": data} if type(data) not in (dict, list) else data
|
62
|
+
return JSONResponse(data, status=status, headers=headers)
|
27
63
|
|
28
64
|
async def receive_body(self):
|
29
65
|
"""
|
@@ -35,7 +71,6 @@ class Request(SanicRequest):
|
|
35
71
|
await super().receive_body()
|
36
72
|
self._get_data_type()
|
37
73
|
self._load_data()
|
38
|
-
self._set_openapi()
|
39
74
|
|
40
75
|
def _get_type(self, name: str, base_type: type):
|
41
76
|
"""
|
sanic_api/app.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import sentry_sdk
|
2
2
|
from sanic import Sanic, text
|
3
|
+
from sanic.errorpages import RENDERERS_BY_CONTENT_TYPE
|
3
4
|
from sanic.log import logger
|
4
5
|
from sanic.worker.loader import AppLoader
|
5
6
|
from sanic_ext import Extend
|
@@ -7,6 +8,7 @@ from sentry_sdk.integrations.asyncio import AsyncioIntegration
|
|
7
8
|
|
8
9
|
from sanic_api import LoggerExtend
|
9
10
|
from sanic_api.api import Request
|
11
|
+
from sanic_api.api.error import ErrorJSONRenderer
|
10
12
|
from sanic_api.config import DefaultSettings, RunModeEnum
|
11
13
|
|
12
14
|
|
@@ -55,11 +57,13 @@ class BaseApp:
|
|
55
57
|
Returns:
|
56
58
|
|
57
59
|
"""
|
60
|
+
Request.json_resp_setting = self.settings.json_resp
|
58
61
|
app = Sanic(self.name, configure_logging=False, request_class=Request)
|
59
62
|
|
60
63
|
self._setup_logger(app)
|
61
64
|
self._setup_config(app)
|
62
65
|
self._setup_openai(app)
|
66
|
+
self._setup_error_handler(app)
|
63
67
|
|
64
68
|
app.main_process_stop(self._main_process_stop)
|
65
69
|
app.main_process_start(self._main_process_start)
|
@@ -153,11 +157,24 @@ class BaseApp:
|
|
153
157
|
Returns:
|
154
158
|
|
155
159
|
"""
|
156
|
-
app.config.LOGGING = True
|
160
|
+
# app.config.LOGGING = True
|
161
|
+
app.config.FALLBACK_ERROR_FORMAT = "json"
|
157
162
|
self._setup_cors(app)
|
158
163
|
|
159
164
|
def _setup_openai(self, app: Sanic): ...
|
160
165
|
|
166
|
+
def _setup_error_handler(self, _app: Sanic):
|
167
|
+
"""
|
168
|
+
设置错误处理器
|
169
|
+
Args:
|
170
|
+
_app: Sanic App
|
171
|
+
|
172
|
+
Returns:
|
173
|
+
|
174
|
+
"""
|
175
|
+
ErrorJSONRenderer.json_resp_setting = self.settings.json_resp
|
176
|
+
RENDERERS_BY_CONTENT_TYPE["application/json"] = ErrorJSONRenderer
|
177
|
+
|
161
178
|
def _setup_cors(self, app: Sanic):
|
162
179
|
"""
|
163
180
|
设置跨域: 开发模式下允许所有跨域,生产模式下使用配置中的跨域列表
|
@@ -167,12 +184,13 @@ class BaseApp:
|
|
167
184
|
Returns:
|
168
185
|
|
169
186
|
"""
|
170
|
-
cors = ",".join(self.settings.
|
187
|
+
cors = ",".join(self.settings.cors.origins)
|
171
188
|
if self.settings.mode == RunModeEnum.DEBNUG:
|
172
|
-
app.config.CORS_ORIGINS = cors or "*"
|
173
189
|
app.config.CORS_SEND_WILDCARD = True
|
190
|
+
app.config.CORS_SUPPORTS_CREDENTIALS = self.settings.cors.supports_credentials
|
191
|
+
app.config.CORS_ORIGINS = cors or "*"
|
174
192
|
else:
|
175
|
-
app.config.CORS_ORIGINS =
|
193
|
+
app.config.CORS_ORIGINS = cors
|
176
194
|
|
177
195
|
async def _setup_route(self, app: Sanic):
|
178
196
|
"""
|
@@ -220,7 +238,7 @@ class BaseApp:
|
|
220
238
|
return
|
221
239
|
|
222
240
|
sentry_sdk.init(
|
223
|
-
dsn=self.settings.sentry_dsn,
|
241
|
+
dsn=str(self.settings.sentry_dsn),
|
224
242
|
# Set traces_sample_rate to 1.0 to capture 100%
|
225
243
|
# of transactions for tracing.
|
226
244
|
traces_sample_rate=1.0,
|
sanic_api/config/setting.py
CHANGED
@@ -37,6 +37,40 @@ class LoggerSettings(BaseModel):
|
|
37
37
|
loki_url: HttpUrl | None = Field(default=None)
|
38
38
|
|
39
39
|
|
40
|
+
class JsonRespSettings(BaseModel):
|
41
|
+
"""
|
42
|
+
json 响应配置类
|
43
|
+
"""
|
44
|
+
|
45
|
+
# 是否使用 {"code": "0", "msg": "success", "data": {}} 的格式
|
46
|
+
use_tml: bool = Field(default=True)
|
47
|
+
|
48
|
+
error_code: str | int = Field(default="ERROR-1")
|
49
|
+
|
50
|
+
# code 字段的名字
|
51
|
+
code_field_name: str = Field(default="code")
|
52
|
+
|
53
|
+
# msg 字段的名字
|
54
|
+
msg_field_name: str = Field(default="msg")
|
55
|
+
|
56
|
+
# data 字段的名字
|
57
|
+
data_field_name: str = Field(default="data")
|
58
|
+
|
59
|
+
|
60
|
+
class CrosSettings(BaseModel):
|
61
|
+
"""
|
62
|
+
跨域设置
|
63
|
+
"""
|
64
|
+
|
65
|
+
# 地址、地址列表、*
|
66
|
+
# 当 credentials等于 'include' 时,origins必须是具体是地址不能是 “*”
|
67
|
+
origins: list[str] | None = Field(default_factory=list)
|
68
|
+
|
69
|
+
# 支持凭证。
|
70
|
+
# 当前端启用了withCredentials 后端需要设置这个值为True
|
71
|
+
supports_credentials: bool = Field(default=False)
|
72
|
+
|
73
|
+
|
40
74
|
class DefaultSettings(SettingsBase):
|
41
75
|
"""
|
42
76
|
配置类
|
@@ -62,10 +96,13 @@ class DefaultSettings(SettingsBase):
|
|
62
96
|
access_log: bool = Field(default=True)
|
63
97
|
|
64
98
|
# 跨域设置
|
65
|
-
|
99
|
+
cors: CrosSettings = Field(default_factory=CrosSettings)
|
66
100
|
|
67
101
|
# 哨兵连接dsn,如果存在则会把错误信息推送给哨兵
|
68
102
|
sentry_dsn: HttpUrl | None = Field(default=None)
|
69
103
|
|
70
104
|
# 日志配置
|
71
105
|
logger: LoggerSettings = Field(default_factory=LoggerSettings)
|
106
|
+
|
107
|
+
# json 响应配置
|
108
|
+
json_resp: JsonRespSettings = Field(default_factory=JsonRespSettings)
|
sanic_api/openapi/extension.py
CHANGED
@@ -0,0 +1,41 @@
|
|
1
|
+
from typing import TYPE_CHECKING
|
2
|
+
|
3
|
+
from sanic import Blueprint
|
4
|
+
from sanic_ext.extensions.openapi.blueprint import blueprint_factory
|
5
|
+
from sanic_ext.extensions.openapi.builders import SpecificationBuilder
|
6
|
+
from sanic_ext.extensions.openapi.extension import OpenAPIExtension as BaseExtension
|
7
|
+
|
8
|
+
if TYPE_CHECKING:
|
9
|
+
from sanic_ext import Extend
|
10
|
+
|
11
|
+
|
12
|
+
class OpenAPIExtension(BaseExtension):
|
13
|
+
name = "openapi"
|
14
|
+
bp: Blueprint
|
15
|
+
|
16
|
+
def startup(self, bootstrap: Extend) -> None:
|
17
|
+
if self.app.config.OAS:
|
18
|
+
self.bp = blueprint_factory(self.app.config)
|
19
|
+
self.app.blueprint(self.bp)
|
20
|
+
bootstrap._openapi = SpecificationBuilder()
|
21
|
+
|
22
|
+
|
23
|
+
"""
|
24
|
+
import inspect
|
25
|
+
import ast
|
26
|
+
|
27
|
+
def build_spec(app, loop):
|
28
|
+
...
|
29
|
+
|
30
|
+
source_code = inspect.getsource(blueprint_factory)
|
31
|
+
ast_tree = ast.parse(source_code)
|
32
|
+
|
33
|
+
my_source_code = inspect.getsource(build_spec)
|
34
|
+
my_func_ast = ast.parse(my_source_code)
|
35
|
+
|
36
|
+
ast_tree.body[0].body[6] = my_func_ast
|
37
|
+
modified_code = compile(ast_tree, filename="<ast>", mode="exec")
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
"""
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: sanic-api
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.4.0
|
4
4
|
Summary: Sanic 框架实用API工具集,拥有自动生成文档、参数校验、配置的导入、日志功能的优化等功能,更好的助力接口的开发
|
5
5
|
Project-URL: homepage, https://github.com/x-haose/sanic-api
|
6
6
|
Project-URL: repository, https://github.com/x-haose/sanic-api
|
@@ -23,12 +23,10 @@ Classifier: Topic :: Internet :: WWW/HTTP
|
|
23
23
|
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
24
24
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
25
25
|
Requires-Python: >=3.10
|
26
|
-
Requires-Dist: hs-config==0.1.
|
26
|
+
Requires-Dist: hs-config==0.1.3
|
27
27
|
Requires-Dist: loguru>=0.7.2
|
28
28
|
Requires-Dist: loki-logger-handler>=0.1.4
|
29
29
|
Requires-Dist: orjson>=3.8.6
|
30
|
-
Requires-Dist: pydantic-settings[toml,yaml]>=2.5.2
|
31
|
-
Requires-Dist: pydantic[dotenv]>=2.9.2
|
32
30
|
Requires-Dist: requests>=2.32.0
|
33
31
|
Requires-Dist: sanic-ext>=23.12.0
|
34
32
|
Requires-Dist: sanic>=24.12.0
|
@@ -0,0 +1,19 @@
|
|
1
|
+
sanic_api/__init__.py,sha256=xqHsD9gP9fSfHc_67d7H4sqy6TGv9Ygpp6_QAc84OTA,75
|
2
|
+
sanic_api/app.py,sha256=TwizAXRAR4E6U6E-HbSPe-UQBjmJ7uzWygVwHG1JXOk,8513
|
3
|
+
sanic_api/api/__init__.py,sha256=MxQ7A-ALJOwznJviVt6zjhcIikhEJCKoOF-7c75qtpk,42
|
4
|
+
sanic_api/api/error.py,sha256=RikYhTFe_z0tv4nfksH4G6lzcwp5fWjNBC36xhFj-R4,661
|
5
|
+
sanic_api/api/model.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
+
sanic_api/api/request.py,sha256=CzltvJ8NE4UmnUTqGXJmJ7oRtBBT5GRcDADEnf7dkUE,7017
|
7
|
+
sanic_api/config/__init__.py,sha256=DVqXHMXANl_xUwBsLVYisr7ZPdX2ubA2_WH3GUZC-Bs,80
|
8
|
+
sanic_api/config/setting.py,sha256=K4Z9oUHe4BN0tzJltmlRF5myPDHcIq8fQ0xgK19s3iw,3156
|
9
|
+
sanic_api/logger/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
+
sanic_api/logger/config.py,sha256=5C-kYrE13_cgw7vNxBwBLi4F8BijHakOKs6YSzxsEMA,3031
|
11
|
+
sanic_api/logger/extension.py,sha256=xj8DjGW3jN2M7MTu4mk65H1Qs-j5VwvgO0LGahoeQRU,4108
|
12
|
+
sanic_api/openapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
|
+
sanic_api/openapi/extension.py,sha256=HCrcEgrj63FXOhx6TJyJDaee6mDuVnuSMA5EtdYUVjc,1009
|
14
|
+
sanic_api/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
|
+
sanic_api/utils/enum.py,sha256=JGFh8o8kovcR4LF3ekuWJIiZINQspfeYrvVhmFvYK-I,1240
|
16
|
+
sanic_api-0.4.0.dist-info/METADATA,sha256=gGiiZ-SQnWgc_LDRi00mwpsa67UJsCViGBLupON1ObQ,4285
|
17
|
+
sanic_api-0.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
18
|
+
sanic_api-0.4.0.dist-info/licenses/LICENSE.txt,sha256=T20w-F8AfuFO9CHy2mSk_An6T9eV4X4rGA01i-gp4M4,1090
|
19
|
+
sanic_api-0.4.0.dist-info/RECORD,,
|
sanic_api/api/response.py
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
from typing import Any, ClassVar
|
2
|
-
|
3
|
-
from pydantic import BaseModel, Field, PrivateAttr
|
4
|
-
from sanic.compat import Header
|
5
|
-
from sanic.response import JSONResponse
|
6
|
-
|
7
|
-
|
8
|
-
class TempModel(BaseModel):
|
9
|
-
_data_field: str = PrivateAttr(default="data")
|
10
|
-
data: Any = Field(default=None, title="数据")
|
11
|
-
code: str = Field(default_factory=str, title="业务状态码")
|
12
|
-
msg: str = Field(default_factory=str, title="消息")
|
13
|
-
|
14
|
-
def get_data_field_name(self) -> str:
|
15
|
-
return self._data_field
|
16
|
-
|
17
|
-
|
18
|
-
class BaseResp(BaseModel):
|
19
|
-
temp_data: ClassVar = Ellipsis
|
20
|
-
|
21
|
-
def resp(self, status: int = 200, headers: Header | dict[str, str] | None = None) -> JSONResponse:
|
22
|
-
data = self.model_dump(mode="json")
|
23
|
-
return JSONResponse(data, status=status, headers=headers)
|
24
|
-
|
25
|
-
|
26
|
-
class BaseRespTml(BaseModel):
|
27
|
-
temp_data: TempModel | None = Field(default_factory=TempModel)
|
28
|
-
|
29
|
-
def resp(self, status: int = 200, headers: Header | dict[str, str] | None = None) -> JSONResponse:
|
30
|
-
tmp_data_field_name = self.temp_data.get_data_field_name()
|
31
|
-
self_data = self.model_dump(mode="json", exclude={"temp_data"})
|
32
|
-
setattr(self.temp_data, tmp_data_field_name, self_data)
|
33
|
-
tml_data = self.temp_data.model_dump(mode="json")
|
34
|
-
return JSONResponse(tml_data, status=status, headers=headers)
|
@@ -1,19 +0,0 @@
|
|
1
|
-
sanic_api/__init__.py,sha256=M5gj0GspFvurRNBBFwqA31zf1DUcjnHyfbSGQUHune4,77
|
2
|
-
sanic_api/app.py,sha256=t54MJ6fFtvjStpRQZaiFiSJcE_KLQZnDe8k9BJjjGCs,7884
|
3
|
-
sanic_api/api/__init__.py,sha256=tlfFmLNwI_Y5uQD4EN6h-XRr1YmoAktqEsjlo48kvX0,383
|
4
|
-
sanic_api/api/model.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
-
sanic_api/api/request.py,sha256=PN6_PEQRfM_-SkndTmAqNHawzPph8uN3QpkM5Mdegvs,5735
|
6
|
-
sanic_api/api/response.py,sha256=stQ6sVF_YiWM8cp4vr79w4HSD81zCHUxV8xAcx5Sk2o,1303
|
7
|
-
sanic_api/config/__init__.py,sha256=DVqXHMXANl_xUwBsLVYisr7ZPdX2ubA2_WH3GUZC-Bs,80
|
8
|
-
sanic_api/config/setting.py,sha256=qW8xp5alLmDcrZ3rXxQCVE8dsLFeLLfGvYJMM34uXiU,2183
|
9
|
-
sanic_api/logger/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
-
sanic_api/logger/config.py,sha256=5C-kYrE13_cgw7vNxBwBLi4F8BijHakOKs6YSzxsEMA,3031
|
11
|
-
sanic_api/logger/extension.py,sha256=xj8DjGW3jN2M7MTu4mk65H1Qs-j5VwvgO0LGahoeQRU,4108
|
12
|
-
sanic_api/openapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
|
-
sanic_api/openapi/extension.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
|
-
sanic_api/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
|
-
sanic_api/utils/enum.py,sha256=JGFh8o8kovcR4LF3ekuWJIiZINQspfeYrvVhmFvYK-I,1240
|
16
|
-
sanic_api-0.3.0a6.dist-info/METADATA,sha256=LacrqyuBdn3vdZBxqVhzNyFNLRjSCuGeF6JCisI26qc,4379
|
17
|
-
sanic_api-0.3.0a6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
18
|
-
sanic_api-0.3.0a6.dist-info/licenses/LICENSE.txt,sha256=T20w-F8AfuFO9CHy2mSk_An6T9eV4X4rGA01i-gp4M4,1090
|
19
|
-
sanic_api-0.3.0a6.dist-info/RECORD,,
|
File without changes
|
File without changes
|