Skip to content

Commit 0619dfd

Browse files
committed
refactor: 插件机制
1 parent 27eba5e commit 0619dfd

11 files changed

Lines changed: 36 additions & 233 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,4 +197,5 @@ flask run
197197

198198
## 后续开发计划
199199

200-
- [ ] 重构插件机制
200+
- [X] 重构插件机制
201+
- [ ] 七牛上传插件

app/api/cms/admin.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@
3030
admin_api = Redprint("admin")
3131

3232

33-
@admin_api.route("/permission", methods=["GET"])
33+
@admin_api.route("/permission")
3434
@permission_meta(name="查询所有可分配的权限", module="管理员", mount=False)
3535
@admin_required
3636
def permissions():
3737
return get_ep_infos()
3838

3939

40-
@admin_api.route("/users", methods=["GET"])
40+
@admin_api.route("/users")
4141
@permission_meta(name="查询所有用户", module="管理员", mount=False)
4242
@admin_required
4343
def get_admin_users():
@@ -185,7 +185,7 @@ def update_user(uid):
185185
return Success("操作成功")
186186

187187

188-
@admin_api.route("/group", methods=["GET"])
188+
@admin_api.route("/group")
189189
@permission_meta(name="查询所有分组及其权限", module="管理员", mount=False)
190190
@admin_required
191191
def get_admin_groups():
@@ -227,7 +227,7 @@ def get_admin_groups():
227227
}
228228

229229

230-
@admin_api.route("/group/all", methods=["GET"])
230+
@admin_api.route("/group/all")
231231
@permission_meta(name="查询所有分组", module="管理员", mount=False)
232232
@admin_required
233233
def get_all_group():
@@ -240,7 +240,7 @@ def get_all_group():
240240
return groups
241241

242242

243-
@admin_api.route("/group/<int:gid>", methods=["GET"])
243+
@admin_api.route("/group/<int:gid>")
244244
@permission_meta(name="查询一个分组及其权限", module="管理员", mount=False)
245245
@admin_required
246246
def get_group(gid):

app/api/cms/log.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def get_logs():
5454
)
5555

5656

57-
@log_api.route("/users", methods=["GET"])
57+
@log_api.route("/users")
5858
@permission_meta(name="查询日志记录的用户", module="日志")
5959
@group_required
6060
@api.validate(

app/api/cms/user.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,15 @@ def change_password():
106106
return Failed("修改密码失败")
107107

108108

109-
@user_api.route("/information", methods=["GET"])
109+
@user_api.route("/information")
110110
@permission_meta(name="查询自己信息", module="用户", mount=False)
111111
@login_required
112112
def get_information():
113113
current_user = get_current_user()
114114
return current_user
115115

116116

117-
@user_api.route("/refresh", methods=["GET"])
117+
@user_api.route("/refresh")
118118
@permission_meta(name="刷新令牌", module="用户", mount=False)
119119
def refresh():
120120
try:
@@ -131,7 +131,7 @@ def refresh():
131131
return NotFound("refresh_token未被识别")
132132

133133

134-
@user_api.route("/permissions", methods=["GET"])
134+
@user_api.route("/permissions")
135135
@permission_meta(name="查询自己拥有的权限", module="用户", mount=False)
136136
@login_required
137137
def get_allowed_apis():

app/api/v1/book.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@
2525
book_api = Redprint("book")
2626

2727

28-
@book_api.route("/<int:id>", methods=["GET"])
28+
@book_api.route("/<int:id>")
2929
@api.validate(
3030
resp=DocResponse(BookNotFound, r=BookOutSchema),
3131
tags=["图书"],
3232
)
33-
def get_book(id: int):
33+
def get_book(id):
3434
"""
3535
获取id指定图书的信息
3636
"""
@@ -40,7 +40,7 @@ def get_book(id: int):
4040
raise BookNotFound
4141

4242

43-
@book_api.route("", methods=["GET"])
43+
@book_api.route("")
4444
@api.validate(
4545
resp=DocResponse(r=BookSchemaList),
4646
tags=["图书"],
@@ -53,7 +53,7 @@ def get_books():
5353
return BookSchemaList.parse_obj(books)
5454

5555

56-
@book_api.route("/search", methods=["GET"])
56+
@book_api.route("/search")
5757
@api.validate(
5858
query=BookQuerySearchSchema,
5959
resp=DocResponse(BookNotFound, r=BookSchemaList),
@@ -96,7 +96,7 @@ def create_book():
9696
resp=DocResponse(Success(13)),
9797
tags=["图书"],
9898
)
99-
def update_book(id: int):
99+
def update_book(id):
100100
"""
101101
更新图书信息
102102
"""
@@ -120,7 +120,7 @@ def update_book(id: int):
120120
resp=DocResponse(BookNotFound, Success(14)),
121121
tags=["图书"],
122122
)
123-
def delete_book(id: int):
123+
def delete_book(id):
124124
"""
125125
传入id删除对应图书
126126
"""

app/cli/plugin/generator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
{0}_api = Redprint("{0}")
1818
1919
20-
@{0}_api.route("/", methods=["GET"])
20+
@{0}_api.route("/")
2121
def test():
2222
return "hi, guy!"
2323
"""

app/cli/plugin/init.py

Lines changed: 14 additions & 187 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
:copyright: © 2020 by the Lin team.
33
:license: MIT, see LICENSE for more details.
44
"""
5-
import json
65
import os
76
import re
87
import subprocess
@@ -13,8 +12,8 @@
1312
"""
1413
插件初始化流程:
1514
1、输入要初始化的插件名称。(多个用空格隔开,*表示初始化所有)
16-
2、python依赖的自动检测和安装
17-
2、将插件的配置写入到项目setting.py中
15+
2、python依赖的安装
16+
2、将插件的配置写入到项目app/config/__init__.py中
1817
3、将model中的模型插入到数据库中
1918
4、如果有需要,将初始数据插入到数据表中
2019
"""
@@ -55,12 +54,6 @@ def generate_path(self):
5554
}
5655

5756
def auto_install_rely(self):
58-
try:
59-
DependenciesResolve(app, self.path_info)
60-
except Exception as e:
61-
raise Exception("安装插件依赖时发生错误!\nError:" + str(e))
62-
from subprocess import CalledProcessError
63-
6457
for name in self.path_info:
6558
print("正在初始化插件" + name + "...")
6659
filename = "requirements.txt"
@@ -70,31 +63,14 @@ def auto_install_rely(self):
7063
if os.path.exists(file_path):
7164
if (os.path.getsize(file_path)) == 0:
7265
continue
73-
print("正在安装" + name + "插件的依赖...")
74-
75-
try:
76-
# 使用try except来判断使用pip管理包还是pipenv管理包,首选pipenv
77-
ret = self.__execute_cmd(cmd="pipenv install -r " + file_path)
78-
79-
if ret:
80-
print(success_msg)
81-
else:
82-
exit(fail_msg)
83-
84-
except CalledProcessError:
85-
try:
86-
ret = self.__execute_cmd(cmd="pip install -r " + file_path)
66+
print("正在安装" + name + "插件的依赖,请耐心等待...")
8767

88-
if ret:
89-
print(success_msg)
90-
else:
91-
exit(fail_msg)
68+
ret = self.__execute_cmd(cmd="pip install -r " + file_path)
9269

93-
except Exception as e:
94-
exit((str(e)) + "\n" + fail_msg)
95-
96-
except Exception as e:
97-
exit((str(e)) + "\n" + fail_msg)
70+
if ret:
71+
print(success_msg)
72+
else:
73+
exit(fail_msg)
9874

9975
def auto_write_setting(self):
10076
print("正在自动写入配置文件...")
@@ -146,15 +122,19 @@ def _generate_setting(self, name, info_mod):
146122

147123
def __update_setting(self, new_setting):
148124
# 得到现存的插件配置
149-
old_setting = self.app.config.get("PLUGIN_PATH")
125+
old_setting = self.app.config.get("PLUGIN_PATH", dict())
150126
final_setting = self.__cal_setting(new_setting, old_setting)
151127

152128
sub_str = "PLUGIN_PATH = " + self.__format_setting(final_setting)
153129

154-
setting_path = self.app.config.root_path + "/config/setting.py"
130+
setting_path = self.app.config.root_path + "/config/__init__.py"
155131
with open(setting_path, "r", encoding="UTF-8") as f:
156132
content = f.read()
157133
pattern = "PLUGIN_PATH = \{([\s\S]*)\}+.*?"
134+
if len(re.findall(pattern, content)) == 0:
135+
content += """
136+
PLUGIN_PATH = {}
137+
"""
158138
result = re.sub(pattern, sub_str, content)
159139

160140
with open(setting_path, "w+", encoding="UTF-8") as f:
@@ -218,159 +198,6 @@ def __cal_setting(new_setting, old_setting):
218198

219199
return final_setting
220200

221-
222-
class DependenciesResolve:
223-
def __init__(self, app_obj, path):
224-
self.app = app_obj
225-
self.path_info = path
226-
# 主项目的依赖关系列表
227-
self.root_graph = []
228-
# 所有插件的依赖关系列表
229-
self.plugin_graph = []
230-
# 生成主项目和插件依赖关系列表
231-
self.generate_graph()
232-
self.check_dependencies()
233-
234-
def generate_graph(self):
235-
try:
236-
p = subprocess.Popen(["pipenv", "graph", "--json"], stdout=subprocess.PIPE)
237-
238-
r = p.communicate()[0].decode("utf-8")
239-
self.root_graph = json.loads(r)
240-
241-
except subprocess.CalledProcessError as e:
242-
exit("pipenv指令未安装,请先使用pip安装命令\nError" + str(e))
243-
244-
except Exception as e:
245-
exit("pipenv 错误,请检测你的pipenv配置!\nError:" + str(e))
246-
247-
self.__generate_plugin_graph()
248-
249-
def check_dependencies(self):
250-
for package in self.root_graph:
251-
# 验证顶级包是否符合规范
252-
self.__check_top_dependencies(package["package"])
253-
254-
# 验证子包是否符合规范
255-
self.__check_sub_dependencies(package["dependencies"])
256-
257-
def __generate_plugin_graph(self):
258-
for name, val in self.path_info.items():
259-
# 首先去校验插件依赖于住项目的依赖是否存在冲突
260-
plugin_path = (
261-
self.path_info[name]["plugin_path"].replace(".", "/").replace("app", "")
262-
)
263-
requirements_path = (
264-
self.app.config.root_path + plugin_path + "/requirements.txt"
265-
)
266-
with open(requirements_path, "r", encoding="UTF-8") as f:
267-
while True:
268-
269-
# 正则匹配requirements的每一行的信息
270-
line = f.readline()
271-
if not line:
272-
break
273-
274-
pattern = "(.*?)(==|<=|>=|!=|>|<)(.*)"
275-
search_obj = re.search(pattern, line)
276-
if search_obj:
277-
278-
package_name = search_obj.group(1)
279-
condition = search_obj.group(2)
280-
version = search_obj.group(3)
281-
key = search_obj.group(1).lower()
282-
283-
plugin_package = dict({"package": {}})
284-
plugin_package["package"]["key"] = key
285-
plugin_package["package"]["package_name"] = package_name
286-
plugin_package["package"]["version"] = version
287-
plugin_package["package"]["condition"] = condition
288-
plugin_package["package"]["plugin_name"] = name
289-
self.plugin_graph.append(plugin_package)
290-
291-
def __check_top_dependencies(self, top_package):
292-
for plugin_package in self.plugin_graph:
293-
top_version = top_package["installed_version"]
294-
plugin_version = plugin_package["package"]["version"]
295-
if (
296-
top_package["key"] == plugin_package["package"]["key"]
297-
and top_version != plugin_version
298-
):
299-
err_msg = (
300-
"由于项目主目录已经存在在包"
301-
""
302-
+ top_package["package_name"]
303-
+ ",但 "
304-
+ plugin_package["package"]["plugin_name"]
305-
+ " 插件尝试重复安装不同版本,请尝试手动去掉该插件的requirements.txt中的包"
306-
)
307-
raise Exception(err_msg)
308-
309-
def __check_sub_dependencies(self, dep_package):
310-
# 判断插件中要安装的依赖,是否符合主项目已安装的依赖规定的范围
311-
for dependence in dep_package:
312-
required_version = dependence["required_version"]
313-
dep_name = dependence["key"]
314-
315-
if required_version != None:
316-
version_infos = required_version.split(",")
317-
for version_info in version_infos:
318-
pattern = "(>=|<=|!=|==|<|>)(.*)"
319-
search_obj = re.search(pattern, version_info)
320-
condition = search_obj.group(1)
321-
version = int(search_obj.group(2).replace(".", ""))
322-
323-
for plugin_package in self.plugin_graph:
324-
name = plugin_package["package"]["key"]
325-
if dep_name == name:
326-
plugin_package_version = int(
327-
plugin_package["package"]["version"].replace(".", "")
328-
)
329-
err_msg = (
330-
plugin_package["package"]["plugin_name"]
331-
+ "插件的依赖 "
332-
+ name
333-
+ " 与主项目依赖版本发生冲突! 请自行手动解决"
334-
)
335-
if condition == ">=":
336-
if plugin_package_version >= version:
337-
pass
338-
else:
339-
raise Exception(err_msg)
340-
341-
elif condition == "==":
342-
if plugin_package_version == version:
343-
pass
344-
else:
345-
raise Exception(err_msg)
346-
347-
elif condition == "!=":
348-
if plugin_package_version != version:
349-
pass
350-
else:
351-
raise Exception(err_msg)
352-
353-
elif condition == "<=":
354-
if plugin_package_version <= version:
355-
pass
356-
else:
357-
raise Exception(err_msg)
358-
359-
elif condition == "<":
360-
if plugin_package_version < version:
361-
pass
362-
else:
363-
raise Exception(err_msg)
364-
365-
elif condition == ">":
366-
if plugin_package_version > version:
367-
pass
368-
else:
369-
raise Exception(err_msg)
370-
else:
371-
pass
372-
373-
374201
def init():
375202
plugin_name = input("请输入要初始化的插件名,如果多个插件请使用空格分隔插件名,输入*表示初始化所有插件:\n")
376203
PluginInit(plugin_name)

0 commit comments

Comments
 (0)