Skip to content

Commit

Permalink
Release V1.0a6
Browse files Browse the repository at this point in the history
  • Loading branch information
GT-ZhangAcer committed Jun 19, 2021
1 parent a8f9896 commit de7a636
Show file tree
Hide file tree
Showing 10 changed files with 334 additions and 280 deletions.
2 changes: 1 addition & 1 deletion README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ EAP邀测版本:当前版本为尝鲜版本,仅具备基本功能,而且
## 快速使用QPT
#### 安装QPT到当前环境
> 当前版本号为 1.0a4
> 当前版本号为 1.0a*
`pip install qpt -i https://pypi.tuna.tsinghua.edu.cn/simple -U` or `pip install qpt -U`

Expand Down
14 changes: 14 additions & 0 deletions ToDo.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# 细节性ToDO

## 高优优先级
- [ ] 针对Python37暴露的兼容性问题
- [ ] 针对Flask暴露的依赖问题
- [ ] 对邀测用户进行本地适配
- [ ] 增加CUDA支持
## 一般优先级
- [ ] 重写依赖搜索
- [ ] 杀进程
- [ ] 控制台程序
- [ ] 排除VENV文件夹
- [ ] 要不要加个环境变量PYTHONPATH
- [ ] 图标问题可参考
6 changes: 3 additions & 3 deletions examples/paddle_sandbox/paddle_program/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def __init__(self):
QMessageBox.Yes)


app = QApplication(sys.argv)
ex = Example()

if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
input("PaddlePaddle测试成功!")
94 changes: 58 additions & 36 deletions qpt/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,20 @@
from qpt._compatibility import com_configs
from qpt.modules.base import SubModule
from qpt.modules.python_env import BasePythonEnv, AutoPythonEnv
from qpt.modules.package import AutoRequirementsPackage, QPTDependencyPackage, DEFAULT_DEPLOY_MODE
from qpt.modules.package import AutoRequirementsPackage, QPTDependencyPackage, DEFAULT_DEPLOY_MODE, \
set_default_deploy_mode, BatchInstallation

from qpt.kernel.tools.log_op import Logging
from qpt.kernel.tools.os_op import clean_qpt_cache
from qpt.kernel.tools.os_op import clean_qpt_cache, copytree
from qpt.kernel.tools.terminal import AutoTerminal


class CreateExecutableModule:
def __init__(self,
work_dir,
launcher_py_path,
save_path,
ignore_dirs: list = None,
requirements_file="auto",
deploy_mode=DEFAULT_DEPLOY_MODE,
sub_modules: List[SubModule] = None,
Expand All @@ -32,13 +35,20 @@ def __init__(self,
none_gui: bool = False,
with_debug: bool = True):
# 初始化路径成员变量
self.launcher_py_path = os.path.abspath(launcher_py_path).replace(os.path.abspath(work_dir) + "\\", "./")
self.launcher_py_path = os.path.abspath(launcher_py_path).replace(os.path.abspath(work_dir), "")
if self.launcher_py_path[:1] == "\\":
self.launcher_py_path = self.launcher_py_path[1:]
self.work_dir = work_dir
assert os.path.abspath(work_dir) not in os.path.abspath(save_path), \
"打包后的保存路径不能在被打包的文件夹中,这样会打包了打包后的文件^n (,,•́ . •̀,,)"
self.save_path = save_path
self.module_path = os.path.join(save_path, "Release")
self.debug_path = os.path.join(save_path, "Debug")
self.interpreter_path = os.path.join(self.module_path, "Python")
if ignore_dirs is None:
self.ignore_dirs = list()

set_default_deploy_mode(deploy_mode)
self.with_debug = with_debug

# 新建配置信息
Expand All @@ -63,13 +73,22 @@ def __init__(self,
# 设置全局下载的Python包默认解释器版本号 - 更换兼容性方案
# set_default_package_for_python_version(interpreter_module.python_version)

# 避免打包虚拟环境
for root, dirs, files in os.walk(self.work_dir):
if files == "pyvenv.cfg":
self.ignore_dirs.append(root)
Logging.warning(f"检测到{files},推测出{root}为Python虚拟环境主目录,在打包时会忽略该目录")

# 获取SubModule列表
self.lazy_module = [interpreter_module, QPTDependencyPackage()]

if none_gui is False:
pass
self.sub_module = sub_modules if sub_modules is not None else list()

# 放入增强包
self.sub_module.append(BatchInstallation())

# 解析依赖
if requirements_file == "auto":
auto_dependency_module = AutoRequirementsPackage(path=self.work_dir,
Expand Down Expand Up @@ -105,10 +124,9 @@ def _solve_module(self, lazy=False):
# lazy mode 不支持terminal
terminal = None
else:
from qpt.kernel.tools.qpt_qt import QTerminal, MessageBoxTerminalCallback
self.terminal = QTerminal()
self.terminal = AutoTerminal()
modules = self.sub_module
terminal = self.terminal.shell_func(callback=MessageBoxTerminalCallback())
terminal = self.terminal.shell_func()
# 依靠优先级进行排序
modules.sort(key=lambda m: m.level, reverse=True)
for sub in modules:
Expand Down Expand Up @@ -144,8 +162,20 @@ def make(self):

# 复制资源文件
assert os.path.exists(self.work_dir), f"{os.path.abspath(self.work_dir)}不存在,请检查该路径是否正确"
shutil.copytree(self.work_dir, self.resources_path)

copytree(self.work_dir, self.resources_path, ignore_dirs=self.ignore_dirs)

# 避免出现if __name__ == '__main__':
with open(os.path.join(self.resources_path, self.launcher_py_path), "r", encoding="utf-8") as lf:
lf_codes = lf.readlines()
for lf_code_id, lf_code in enumerate(lf_codes):
if "if" in lf_code and "__name__" in lf_code and "__main__" in lf_code:
# 懒得写正则了嘿嘿嘿
Logging.warning(f"{self.launcher_py_path}中包含if __name__ == '__main__'语句,"
f"由于用户使用时QPT成为了主程序,故此处代码块会被Python忽略。"
f"为保证可以正常执行,当前已自动修复该问题")
with open(os.path.join(self.resources_path, self.launcher_py_path), "w", encoding="utf-8") as new_lf:
lf_codes[lf_code_id] = lf_code[:lf_code.index("if ")] + "if 'qpt':\n"
new_lf.writelines(lf_codes)
# 创建配置文件
os.makedirs(self.config_path, exist_ok=True)
with open(self.config_file_path, "w", encoding="utf-8") as config_file:
Expand All @@ -154,31 +184,22 @@ def make(self):
# 复制Debug所需文件
if self.with_debug:
debug_dir = os.path.join(os.path.split(qpt.__file__)[0], "ext/launcher_debug")
try:
shutil.copytree(debug_dir, dst=self.debug_path, dirs_exist_ok=True)
shutil.copytree(self.module_path, dst=self.debug_path, dirs_exist_ok=True)
except TypeError:
shutil.copytree(debug_dir, dst=self.debug_path)
shutil.copytree(self.module_path, dst=self.debug_path)
Logging.debug("打包策略将对Python3.7版本进行支持")
finally:
# 生成Debug标识符
unlock_file_path = os.path.join(self.debug_path, "configs/unlock.cache")
with open(unlock_file_path, "w", encoding="utf-8") as unlock_file:
unlock_file.write(str(datetime.datetime.now()))

# 重命名兼容模式文件
compatibility_mode_file = os.path.join(self.debug_path, "compatibility_mode.cmd")
if os.path.exists(compatibility_mode_file):
os.rename(compatibility_mode_file,
os.path.join(self.debug_path, "使用兼容模式运行.cmd"))
copytree(debug_dir, dst=self.debug_path)
copytree(self.module_path, dst=self.debug_path)
# 生成Debug标识符
unlock_file_path = os.path.join(self.debug_path, "configs/unlock.cache")
with open(unlock_file_path, "w", encoding="utf-8") as unlock_file:
unlock_file.write(str(datetime.datetime.now()))

# 重命名兼容模式文件
compatibility_mode_file = os.path.join(self.debug_path, "compatibility_mode.cmd")
if os.path.exists(compatibility_mode_file):
os.rename(compatibility_mode_file,
os.path.join(self.debug_path, "使用兼容模式运行.cmd"))

# 复制Release启动器文件
launcher_dir = os.path.join(os.path.split(qpt.__file__)[0], "ext/launcher")
try:
shutil.copytree(launcher_dir, dst=self.module_path, dirs_exist_ok=True)
except TypeError:
shutil.copytree(launcher_dir, dst=self.module_path)
copytree(launcher_dir, dst=self.module_path)
# 重命名兼容模式文件
compatibility_mode_file = os.path.join(self.module_path, "compatibility_mode.cmd")
if os.path.exists(compatibility_mode_file):
Expand Down Expand Up @@ -207,8 +228,10 @@ def make(self):
f"| 文件或重新打包,以避免因执行“启动程序.exe”后丢失“一次性部署模块”,从而无法被他人使用。\n"
f"| ----------------------------------------------------------------------------- |\n")

sys.stdout.flush()
Logging.info("是否需要保留QPT在打包时产生的缓存文件?若不清空则可能会在下次使用QPT时复用缓存以提升打包速度")
clear_key = input("[保留(Y)/清空(N)]:_")
sys.stdout.flush()
if clear_key.lower() == "n":
clean_qpt_cache()
Logging.info("QPT缓存已全部清空")
Expand Down Expand Up @@ -240,12 +263,11 @@ def solve_qpt_env(self):

def _solve_module(self):
modules = self.lazy_module + self.sub_module
from qpt.kernel.tools.qpt_qt import QTerminal, MessageBoxTerminalCallback
from qpt.gui.qpt_unzip import Unzip
from PyQt5.QtWidgets import QApplication
from PyQt5.QtGui import QIcon
self.terminal = QTerminal()
terminal = self.terminal.shell_func(callback=MessageBoxTerminalCallback())
self.terminal = AutoTerminal()
terminal = self.terminal.shell_func()
app = QApplication(sys.argv)
unzip_bar = Unzip()
unzip_bar.setWindowIcon(QIcon(os.path.join(self.base_dir, "Logo.ico")))
Expand All @@ -257,7 +279,7 @@ def _solve_module(self):
module_path=self.base_dir,
terminal=terminal)
sub_module.unpack()
unzip_bar.update_value(sub_module_id / len(self.sub_module) * 100)
unzip_bar.update_value(min(sub_module_id / len(self.sub_module) * 100, 99))
unzip_bar.update_title(f"正在初始化:{sub_name}")
app.processEvents()

Expand Down Expand Up @@ -309,8 +331,8 @@ def run(self):
self.solve_work_dir()
# 执行主程序
main_lib_path = self.configs["launcher_py_path"].replace(".py", "")
assert "." not in main_lib_path[1:], "封装Module时需要避免路径中带有'.'字符,该字符将影响执行程序"
main_lib_path = main_lib_path[2:]. \
assert "." not in main_lib_path, "需要避免路径中带有'.'字符,该字符将严重影响程序执行"
main_lib_path = main_lib_path. \
replace(".py", ""). \
replace(r"\\", "."). \
replace("\\", "."). \
Expand Down
2 changes: 1 addition & 1 deletion qpt/gui/GUI_module_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from qpt.kernel.qt_ui import GUIML
from qpt.kernel.tools.log_op import Logging
from qpt.kernel.tools.qpt_qt import QTCondaVenv, QTerminal
from qpt.kernel.tools.terminal import QTCondaVenv, QTerminal
from qpt.kernel.tools.os_op import SysInfo

"""
Expand Down
29 changes: 29 additions & 0 deletions qpt/kernel/tools/os_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,35 @@ def flush(self):
self.buff = ''


def copytree(src, dst, ignore_dirs: list = None):
"""
复制整个目录树
最开始是用shutil.copytree(),但奈何Python3.7和3.8差别挺大,算了忽略这点效率吧,反正是在打包过程中,不影响用户
:param src: 源路径
:param dst: 目标路径
:param ignore_dirs: 忽略的文件夹名
"""
if ignore_dirs is None:
ignore_dirs = list()
else:
ignore_dirs = [os.path.abspath(d) for d in ignore_dirs]
if not os.path.exists(dst):
os.makedirs(dst)

if os.path.exists(src):
for root, dirs, files in os.walk(src):
if os.path.abspath(root) in ignore_dirs:
continue
dst_root = os.path.join(os.path.abspath(dst),
os.path.abspath(root).replace(os.path.abspath(src), "").strip("\\"))
for file in files:
src_file = os.path.join(root, file)
dst_file = os.path.join(dst_root, file)
if not os.path.exists(dst_root):
os.makedirs(dst_root, exist_ok=True)
shutil.copy(src_file, dst_file)


class FileSerialize:
def __init__(self, file_path):
with open(file_path, "r", encoding="utf-8")as file:
Expand Down
Loading

0 comments on commit de7a636

Please sign in to comment.