-
Notifications
You must be signed in to change notification settings - Fork 284
/
Copy pathpyexe.py
118 lines (94 loc) · 2.94 KB
/
pyexe.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#! /usr/bin/env python
"""Script to generate a Python exe which calls into a main module.
The generated script calls the "main" function in the main module.
Since the import paths and environment variables are setup correctly
before calling the "main" function, the script can be run like any
other executable, without having to specify PYTHONPATH and other
environment variables explicitly.
Usage:
$> pyexe.py [-p PATH] [-v VAR=VALUE] -m MAIN_MODULE -x EXE
PATH is the optional path to prepend to sys.path before calling the
"main" function of the MAIN_MODULE. EXE is the path to the generated
executable. VAR is the optional environment variable that should be
set to VALUE before calling the "main" function.
"""
import argparse
import os
import stat
import sys
EXE_TEMPLATE = """#! /usr/bin/env python
# DO NOT EDIT.
# Executable generated by {script}
import os
import sys
def add_paths(paths: str):
sys.path = paths + sys.path
python_path_key = 'PYTHONPATH'
old_value = os.environ.get(python_path_key, '')
paths_string = os.pathsep.join(paths)
new_value = paths_string if not old_value else paths_string + os.pathsep + old_value
os.environ[python_path_key] = new_value
{path_adjustment}
{env_adjustment}
{import_stmt}
sys.exit({modname}.main())
"""
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument(
"--path",
"-p",
action="append",
default=[],
help="Path to prepend to sys.path.",
)
parser.add_argument(
"--env",
"-v",
action="append",
default=[],
help="Environment variable to set. Should be of the form VAR=VALUE.",
)
parser.add_argument(
"--main_module",
"-m",
required=True,
help="The fully qualified name of the target main module of the exe.",
)
parser.add_argument(
"--exe_path", "-x", required=True, help="Path to the Python executable."
)
args = parser.parse_args()
for env in args.env:
if "=" not in env:
sys.exit(
"Environment variables should be specified in the form VAR=VALUE."
)
return args
def main():
options = parse_args()
path_adjustment = "add_paths([%s])" % ",".join(
["'%s'" % path for path in options.path]
)
env_adjustment = ""
for env in options.env:
var, value = env.split("=")
env_adjustment += f"os.environ['{var}'] = '{value}'\n"
if "." in options.main_module:
pkg, modname = options.main_module.rsplit(".", 1)
import_stmt = f"from {pkg} import {modname}"
else:
modname = options.main_module
import_stmt = f"import {options.main_module}"
with open(options.exe_path, "w") as target:
text = EXE_TEMPLATE.format(
script=__file__,
path_adjustment=path_adjustment,
env_adjustment=env_adjustment,
import_stmt=import_stmt,
modname=modname,
)
target.write(text)
os.chmod(options.exe_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
if __name__ == "__main__":
main()