Skip to content

Commit 4f738f5

Browse files
committed
Build full venv per version
1 parent ff32045 commit 4f738f5

File tree

2 files changed

+81
-57
lines changed

2 files changed

+81
-57
lines changed

application.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
app_builder: v0.13.1
1+
app_builder: v0.15.0
22

33
Application:
44
name: App Builder

cli/py/app-builder.py

Lines changed: 80 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import os
22
import shutil
3-
import subprocess
3+
from subprocess import run, call
44
import tempfile
55
from pathlib import Path
66
import fnmatch
77
import re
88
from textwrap import dedent
99
import time
10-
1110
import sys
12-
1311
from locate import allow_relative_location_imports
1412

1513
allow_relative_location_imports("../..")
@@ -19,6 +17,7 @@
1917
from app_builder.exec_py import exec_py
2018
from app_builder.shell import copy
2119
from app_builder.util import help, init, rmtree
20+
from app_builder.run_and_suppress import run_and_suppress_pip
2221

2322

2423
class ApplicationYamlError(Exception):
@@ -78,12 +77,69 @@ def get_app_version():
7877
return version
7978

8079

80+
# Move this code to py/src so that anyone can make a autory-based venv at will
81+
def create_app_builder_based_venv(
82+
venv_path: Path,
83+
) -> Path:
84+
85+
base_python_exe = (
86+
Path(__file__).resolve().parent.parent.parent
87+
/ "bin"
88+
/ "python"
89+
/ "python"
90+
/ "python.exe"
91+
)
92+
93+
run(
94+
[
95+
str(base_python_exe),
96+
"-m",
97+
"venv",
98+
str(venv_path),
99+
"--without-pip",
100+
],
101+
check=True,
102+
)
103+
104+
# Define the source directory to copy files from
105+
src_base = base_python_exe.parent.parent
106+
107+
# Ignore files in original that will cause overwrites
108+
exclude_relpath_lower_strings = {
109+
"scripts",
110+
"python",
111+
"python.exe",
112+
"pyvenv.cfg",
113+
"lib",
114+
}
115+
116+
def copy_included_files(src: Path = src_base):
117+
relpath = src.resolve().relative_to(src_base)
118+
if relpath.as_posix().lower() not in exclude_relpath_lower_strings:
119+
if src.is_dir():
120+
for f in src.glob("*"):
121+
copy_included_files(f)
122+
else:
123+
dest = venv_path / relpath
124+
dest.parent.mkdir(parents=True, exist_ok=True)
125+
shutil.copy2(src, dest)
126+
127+
copy_included_files()
128+
129+
# Load original autory's site packages in order for the venv to have access to them
130+
(venv_path / "Lib" / "site-packages" / "base_site_packages.pth").write_text(
131+
f"import site; site.addsitedir({repr((src_base / 'Lib/site-packages').as_posix())})"
132+
)
133+
134+
return venv_path / "Scripts" / "python.exe"
135+
136+
81137
def ensure_app_version():
82138
rev = get_app_version()
83139
path_rev = paths.versions.joinpath(rev)
84140

85141
# Maybe no work needed
86-
if not path_rev.joinpath("run.py").is_file():
142+
if not path_rev.joinpath("run.cmd").is_file():
87143

88144
print(f"Requested version '{rev}' in application.yaml")
89145
print(f"Initiate app-builder '{rev}' dependencies")
@@ -96,24 +152,25 @@ def ensure_app_version():
96152
tdir = Path(tdir)
97153

98154
tmp_rev_repo = tdir.joinpath("repo")
99-
tmp_site = tdir.joinpath("site-packages")
100155
os.makedirs(tmp_rev_repo, exist_ok=True)
101-
os.makedirs(tmp_site, exist_ok=True)
102156

103157
for i in paths.live_repo.glob("*"):
104158
if i.name == ".git":
105159
continue
106160
copy(i, tmp_rev_repo.joinpath(i.name))
107161

108-
assert 0 == subprocess.call(
162+
create_app_builder_based_venv(tdir / "venv")
163+
164+
run_and_suppress_pip(
109165
[
110-
sys.executable,
166+
tdir / "venv" / "Scripts" / "python.exe",
111167
"-m",
112168
"pip",
113169
"install",
114170
"-r",
115171
tmp_rev_repo.joinpath("requirements.txt"),
116172
"--no-warn-script-location",
173+
"--disable-pip-version-check",
117174
]
118175
)
119176

@@ -124,49 +181,6 @@ def ensure_app_version():
124181
print(f"App-builder version '{rev}' successful")
125182
print()
126183

127-
# Inject launcher - note that launcher may change with the app-builder version driving this, so keep it volatile
128-
with open(path_rev.joinpath("run.py"), "w") as fw:
129-
fw.write(
130-
dedent(
131-
r"""
132-
from pathlib import Path
133-
import subprocess
134-
import sys
135-
import os
136-
from textwrap import dedent
137-
138-
this_dir = Path(__file__).resolve().parent
139-
site_dir = this_dir.joinpath('site-packages')
140-
script = this_dir.joinpath("repo", "app_builder", "main.py")
141-
142-
def repr_str(x):
143-
return repr(str(x))
144-
145-
sys.exit(
146-
subprocess.call(
147-
[
148-
sys.executable,
149-
"-c",
150-
dedent(f'''
151-
import sys;
152-
sys.argv = sys.argv[0:1]+{repr(sys.argv[1:])};
153-
sys.path.insert(0, {repr_str(site_dir)});
154-
script = f{repr_str(script)};
155-
globs = globals();
156-
globs["__file__"] = script;
157-
globs["__name__"] = "__main__";
158-
file = open(script, 'rb');
159-
script_txt = file.read();
160-
file.close();
161-
exec(compile(script_txt, script, 'exec'), globs);
162-
'''),
163-
]
164-
)
165-
)
166-
"""
167-
)
168-
)
169-
170184
return rev
171185

172186

@@ -218,16 +232,26 @@ def run_versioned_main():
218232

219233
rev_path = paths.versions.joinpath(rev)
220234

221-
# run
222-
exec_py(rev_path.joinpath("run.py"))
235+
rev_path.joinpath("run.cmd").write_text(
236+
r'@call "%~dp0\venv\Scripts\python.exe" "%~dp0repo\app_builder\main.py" %*'
237+
)
238+
239+
# Run directly
240+
exit_code = call(
241+
[
242+
rev_path / "venv" / "Scripts" / "python.exe",
243+
rev_path / "repo" / "app_builder" / "main.py",
244+
*sys.argv[1:],
245+
],
246+
)
223247

224248
# Leave trail
225-
with open(rev_path.joinpath("run.log"), "w") as fw:
226-
pass
249+
rev_path.joinpath("run.log").write_text("")
227250

228-
# Clean up some old versions
229251
version_cleanup()
230252

253+
sys.exit(exit_code)
254+
231255

232256
if __name__ == "__main__":
233257
run_versioned_main()

0 commit comments

Comments
 (0)