11import os
22import shutil
3- import subprocess
3+ from subprocess import run , call
44import tempfile
55from pathlib import Path
66import fnmatch
77import re
88from textwrap import dedent
99import time
10-
1110import sys
12-
1311from locate import allow_relative_location_imports
1412
1513allow_relative_location_imports ("../.." )
1917from app_builder .exec_py import exec_py
2018from app_builder .shell import copy
2119from app_builder .util import help , init , rmtree
20+ from app_builder .run_and_suppress import run_and_suppress_pip
2221
2322
2423class 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+
81137def 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
232256if __name__ == "__main__" :
233257 run_versioned_main ()
0 commit comments