PyInstallerEx 0.1.6__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.
File without changes
@@ -0,0 +1,13 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ PyInstallerEx - Enhanced PyInstaller packaging tool
4
+ 扩展PyInstaller使其拥有单文件安装功能
5
+ """
6
+
7
+ __version__ = "0.1.6"
8
+ __author__ = "iiixxxiii"
9
+
10
+ from .core.packager import PackageBuilder
11
+ from .core.config import PackageConfig
12
+
13
+ __all__ = ["PackageBuilder", "PackageConfig"]
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ PyInstallerEx命令行入口点
5
+ """
6
+
7
+ from __future__ import print_function
8
+ import argparse
9
+ import sys
10
+ import os
11
+
12
+ from .core.packager import PackageBuilder
13
+ from .core.config import PackageConfig
14
+ from .utils.logging import setup_logging
15
+ from .utils.file_utils import FileUtils
16
+
17
+
18
+ def main():
19
+ """主函数 - 命令行接口"""
20
+ parser = argparse.ArgumentParser(
21
+ description="PyInstallerEx - Enhanced PyInstaller packaging tool",
22
+ epilog="Example: python -m PyInstallerEx main.py --cfg config.json"
23
+ )
24
+
25
+ parser.add_argument("script", nargs="?", default=None,
26
+ help="Python script to package (mutually exclusive with --folder)")
27
+ parser.add_argument("--cfg", "--config", dest="config",
28
+ help="Custom configuration file path")
29
+ parser.add_argument("--output", "-o", help="Output directory")
30
+ parser.add_argument("--verbose", "-v", action="store_true",
31
+ help="Verbose output")
32
+ parser.add_argument("--name", "-n", help="Output filename (default: script name)")
33
+ parser.add_argument("--extract-mode", dest="extract_mode", choices=["temp", "local"],
34
+ default="temp", help="Extraction mode: 'temp' (system temp dir, default) or 'local' (beside exe)")
35
+ parser.add_argument("--engine-mode", dest="engine_mode", choices=["pyinstaller", "nuitka", "pyoxidizer"],
36
+ default="pyinstaller", help="Packaging engine: 'pyinstaller' (default), 'nuitka', or 'pyoxidizer'")
37
+ parser.add_argument("--folder", dest="folder",
38
+ help="Package a local folder directly (skip engine build, mutually exclusive with script)")
39
+ parser.add_argument("--run", dest="run",
40
+ help="Main entry file or relative path to run after extraction (used with --folder)")
41
+
42
+ # 计算默认图标路径(包内的logo.ico)
43
+ _default_icon = os.path.join(os.path.dirname(os.path.abspath(__file__)), "logo.ico")
44
+ parser.add_argument("--icon", default=_default_icon,
45
+ help="Icon file path for the executable (default: package logo.ico)")
46
+
47
+ args = parser.parse_args()
48
+
49
+ # 设置日志
50
+ setup_logging(args.verbose)
51
+
52
+ try:
53
+ # 参数互斥校验:script 和 --folder 不能同时使用
54
+ if args.script and args.folder:
55
+ print("[FAIL] Error: 'script' and '--folder' are mutually exclusive. Please specify only one.", file=sys.stderr)
56
+ sys.exit(1)
57
+
58
+ # 必须提供 script 或 --folder 之一
59
+ if not args.script and not args.folder:
60
+ print("[FAIL] Error: either 'script' or '--folder' must be specified.", file=sys.stderr)
61
+ sys.exit(1)
62
+
63
+ # --folder 模式校验:目录必须存在
64
+ if args.folder:
65
+ if not os.path.isdir(args.folder):
66
+ print("[FAIL] Error: --folder path does not exist or is not a directory: {0}".format(args.folder), file=sys.stderr)
67
+ sys.exit(1)
68
+
69
+ # 加载配置
70
+ if args.config:
71
+ config = PackageConfig.from_file(args.config)
72
+ else:
73
+ # 使用默认配置
74
+ config = PackageConfig()
75
+
76
+ # 设置解压模式、引擎模式和图标
77
+ config.extract_mode = args.extract_mode
78
+ config.engine_mode = args.engine_mode
79
+
80
+ # 设置 --run 参数(main 字段)
81
+ if args.run:
82
+ config.main = args.run
83
+
84
+ # 验证图标参数
85
+ icon_path = args.icon
86
+ if icon_path and icon_path != _default_icon: # 用户指定了自定义图标
87
+ # 检查文件是否存在
88
+ if not os.path.exists(icon_path):
89
+ print("[WARNING] Icon file not found: {0}".format(icon_path), file=sys.stderr)
90
+ config.icon = None
91
+ # 检查文件格式是否为.ico
92
+ elif not icon_path.lower().endswith('.ico'):
93
+ print("[WARNING] Icon file must be in .ico format, got: {0}".format(icon_path), file=sys.stderr)
94
+ print("[WARNING] Please convert your image to .ico format before using --icon parameter", file=sys.stderr)
95
+ config.icon = None
96
+ else:
97
+ config.icon = icon_path
98
+ else:
99
+ # 使用默认图标(如果存在)
100
+ if os.path.exists(_default_icon):
101
+ config.icon = _default_icon
102
+ else:
103
+ config.icon = None
104
+
105
+ # 创建构建器
106
+ builder = PackageBuilder(config, verbose=args.verbose)
107
+
108
+ output_dir = args.output if args.output else None
109
+
110
+ # --folder 模式:直接打包文件夹
111
+ if args.folder:
112
+ folder_path = os.path.abspath(args.folder)
113
+
114
+ # 确定输出文件名:优先用 -n,否则用文件夹名
115
+ if args.name:
116
+ config.filename = args.name
117
+ elif config.filename == "{filename}":
118
+ config.filename = os.path.basename(folder_path)
119
+
120
+ result_path = builder.build_folder(folder_path, output_dir)
121
+ else:
122
+ # 常规脚本打包模式
123
+ # 如果通过-n指定了名称,则覆盖配置中的filename
124
+ if args.name:
125
+ config.filename = args.name
126
+ # 如果配置中filename仍然是默认值"{filename}",则使用脚本名
127
+ elif config.filename == "{filename}":
128
+ script_name = FileUtils.get_script_name(args.script)
129
+ config.filename = script_name
130
+
131
+ # 执行打包
132
+ script_path = args.script
133
+ result_path = builder.build(script_path, output_dir)
134
+
135
+ print("\n[OK] Packaging completed successfully!")
136
+ print("[Output] File: {0}".format(result_path))
137
+
138
+ except Exception as e:
139
+ print("\n[FAIL] Packaging failed: {0}".format(e), file=sys.stderr)
140
+ sys.exit(1)
141
+
142
+
143
+ if __name__ == "__main__":
144
+ main()
@@ -0,0 +1,9 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ PyInstallerEx核心功能模块
4
+ """
5
+
6
+ from .packager import PackageBuilder
7
+ from .config import PackageConfig
8
+
9
+ __all__ = ["PackageBuilder", "PackageConfig"]
@@ -0,0 +1,91 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ 配置管理模块
4
+ """
5
+
6
+ from __future__ import print_function
7
+ import json
8
+ import os
9
+ import hashlib
10
+
11
+
12
+ class PackageConfig(object):
13
+ """打包配置数据类"""
14
+
15
+ def __init__(self, filename="{filename}", version="1.0.0", installer=None,
16
+ tmp_dir="ex_{filename}_{md5}", description="Application packaged with PyInstallerEx",
17
+ author="Unknown", extract_mode="temp", engine_mode="pyinstaller",
18
+ icon=None, main=None):
19
+ self.filename = filename
20
+ self.version = version
21
+ self.installer = installer
22
+ self.tmp_dir = tmp_dir
23
+ self.description = description
24
+ self.author = author
25
+ self.extract_mode = extract_mode
26
+ self.engine_mode = engine_mode
27
+ self.icon = icon
28
+ self.main = main
29
+
30
+ @classmethod
31
+ def from_file(cls, config_path):
32
+ """从配置文件加载配置"""
33
+ try:
34
+ with open(config_path, 'r') as f:
35
+ data = json.load(f)
36
+ return cls(**data)
37
+ except (ValueError, IOError) as e:
38
+ raise ValueError("Invalid configuration file {0}: {1}".format(config_path, e))
39
+
40
+ def to_dict(self):
41
+ """转换为字典"""
42
+ return {
43
+ 'filename': self.filename,
44
+ 'version': self.version,
45
+ 'installer': self.installer,
46
+ 'tmp_dir': self.tmp_dir,
47
+ 'description': self.description,
48
+ 'author': self.author,
49
+ 'extract_mode': self.extract_mode,
50
+ 'engine_mode': self.engine_mode,
51
+ 'icon': self.icon,
52
+ 'main': self.main
53
+ }
54
+
55
+ def validate(self):
56
+ """验证配置有效性"""
57
+ # 不再强制要求filename必须指定,因为会在运行时根据脚本名设置
58
+ if not self.version:
59
+ raise ValueError("Version must be specified")
60
+ return True
61
+
62
+ def render_template(self, script_name, script_md5):
63
+ """渲染配置模板,替换占位符"""
64
+ filename = self.filename.replace("{filename}", script_name)
65
+ tmp_dir = self.tmp_dir.replace("{filename}", script_name).replace("{md5}", script_md5)
66
+
67
+ # 处理installer默认值
68
+ installer = self.installer
69
+ if not installer:
70
+ if os.name == 'nt': # Windows
71
+ installer = os.environ.get('TEMP', '/tmp')
72
+ else: # Unix/Linux
73
+ installer = '/tmp'
74
+
75
+ return PackageConfig(
76
+ filename=filename,
77
+ version=self.version,
78
+ installer=installer,
79
+ tmp_dir=tmp_dir,
80
+ description=self.description,
81
+ author=self.author,
82
+ extract_mode=self.extract_mode,
83
+ engine_mode=self.engine_mode,
84
+ icon=self.icon,
85
+ main=self.main
86
+ )
87
+
88
+ def save_to_file(self, output_path):
89
+ """保存配置到文件"""
90
+ with open(output_path, 'w') as f:
91
+ json.dump(self.to_dict(), f, indent=2)