Coverage for src/pymyinstall/win_installer/win_packages.py: 20%
169 statements
« prev ^ index » next coverage.py v7.1.0, created at 2023-07-19 01:47 +0200
« prev ^ index » next coverage.py v7.1.0, created at 2023-07-19 01:47 +0200
1"""
2@file
3@brief To install packages for a specific distribution.
4"""
5from __future__ import print_function
7import os
8import sys
9from .win_exception import WinInstallPackageException
10from ..installhelper.install_cmd_helper import run_cmd, get_pip_program
11from ..packaged import small_set, find_module_install
13if sys.version_info[0] == 2:
14 FileNotFoundError = Exception
17def get_modules_version(python_path):
18 """
19 return a dictionary { module:version }
21 @param python_path path to python
22 @return dictionary
23 """
24 if sys.platform.startswith("win"):
25 prog = os.path.join(python_path, "Scripts", "pip.exe")
26 if not os.path.exists(prog):
27 prog = get_pip_program(exe=python_path)
28 else:
29 prog = get_pip_program(exe=python_path)
31 cmd = prog + " list"
32 from pip import __version__
33 if int(__version__.split(".")[0]) >= 9:
34 cmd += " --format=columns"
36 try:
37 out, err = run_cmd(cmd, wait=True, fLOG=None, change_path=python_path)
38 except Exception as e:
39 raise RuntimeError("unable to run: {0}".format(cmd)) from e
41 if err is not None and len(err) > 0:
42 if len(err.split("\n")) > 3 or \
43 "You should consider upgrading via the 'pip install --upgrade pip' command." not in err:
44 raise RuntimeError("unable to run, #lines {0}\nERR-8:\n{1}\nOUT:\n{2}".format(
45 len(err.split("\n")), err, out))
47 lines = out.split("\n")
48 res = {}
49 for line in lines:
50 if "." in line:
51 spl = line.split()
52 if len(spl) == 2:
53 a = spl[0]
54 b = spl[1].strip(" \n\r")
55 res[a] = b.strip("()")
56 al = a.lower()
57 if al != a:
58 res[al] = res[a]
59 return res
62def win_install_package_other_python(python_path, package, verbose=False, deps=True, fLOG=print):
63 """
64 Install a package for another Python distribution than the current one.
66 @param python_path location of python
67 @param package location of the package (.tar.gz, .whl, .tgz, .bz2)
68 @param verbose display more information
69 @param deps take dependencies into account or not
70 @param fLOG logging function
71 @return operations ("pip", module) if installed, empty if already installed
73 .. versionchanged:: 1.1
74 ``deps=False`` is the default for module zipline
75 """
76 thename = os.path.split(package)[-1]
77 if thename == "jenkins.zip":
78 # jenkins.zip is not a python package
79 return []
80 if verbose:
81 fLOG("[pymy] *** INSTALL", package)
82 operations = []
84 if sys.version_info[0] == 2:
85 pip = os.path.join(python_path, "Scripts", "pip.exe")
86 else:
87 pip = os.path.join(python_path, "Scripts",
88 "pip%d.exe" % sys.version_info[0])
90 if not os.path.exists(pip):
91 raise FileNotFoundError(pip)
93 cmd = "{0} install {1}".format(pip, package)
94 if verbose:
95 fLOG(cmd)
97 cur = os.getcwd()
98 if cur != python_path:
99 os.chdir(python_path)
100 name = os.path.split(package)[-1]
101 if (deps is not None and not deps) or name.startswith("zipline"):
102 cmd += " --no-deps"
103 out, err = run_cmd(cmd, wait=True, fLOG=fLOG)
105 if cur != python_path:
106 os.chdir(cur)
108 if verbose:
109 fLOG(out)
111 if err is not None and len(err) > 0:
112 name = "-".join(package.split("-")[:-1])
113 look = "Successfully installed " + name
114 if look not in err:
115 raise WinInstallPackageException(
116 "unable to install {0}, due to:\nCMD\n{3}\nOUT:\n{1}\nERR-9:\n{2}\nNOT FOUND\n{4}".format(package, out, err, cmd, look))
118 if "No distributions matching the version" in out:
119 raise WinInstallPackageException(
120 "unable to install " +
121 package +
122 "\nCMD\n" +
123 cmd +
124 "\nOUT:\n" +
125 out +
126 "\nERR--A:\n" +
127 err)
128 elif "Testing of typecheck-decorator passed without failure." in out:
129 operations.append(("pip", package))
130 elif "Successfully installed" not in out:
131 if "error: Unable to find vcvarsall.bat" in out:
132 url = "http://www.xavierdupre.fr/blog/2013-07-07_nojs.html"
133 raise WinInstallPackageException(
134 "unable to install " +
135 package +
136 "\nread:\n" +
137 url +
138 "\nCMD\n" +
139 cmd +
140 "OUT:\n" +
141 out +
142 "\nERR--B:\n" +
143 err)
144 if "Requirement already satisfied" not in out:
145 raise WinInstallPackageException(
146 "unable to install " +
147 package +
148 "\nCMD\n" +
149 cmd +
150 "\nOUT:\n" +
151 out +
152 "\nERR--C:\n" +
153 err)
154 else:
155 operations.append(("pip", package))
157 return operations
160def _is_package_in_list(module_name, list_packages, no_wheel=False):
161 """
162 determines of this package is the one for the given module_name
164 @param list_packages list of packages names (list of wheel)
165 @param module_name module name
166 @param no_wheel skip wheels
167 @return package name
168 """
169 module_name = module_name.lower()
170 p = "." in module_name
171 if p:
172 pp = module_name.replace(".", "_")
174 d = "-" in module_name
175 if d:
176 pd = module_name.split("-")
177 if len(pd[-1]) == 0:
178 pd = "_".join(pd[:-1]) + "-"
179 else:
180 pd = "_".join(pd)
182 for a in list_packages:
183 al = a.lower()
184 if no_wheel and al.endswith(".whl"):
185 continue
186 if module_name == "python" and ".msi" not in a:
187 continue
188 if "theme-" in al:
189 al = al.split("theme-")
190 al = al[0].replace("-", "_") + "theme-" + al[1]
192 if al.startswith(module_name):
193 return a
194 if p and al.startswith(pp):
195 return a
196 if d and al.startswith(pd):
197 return a
198 return None
201def is_package_installed(python_path, module_name, installed_packages=None):
202 """
203 not very accurate but it should speed up the process
205 @param python_path python path
206 @param module_name module name (import name)
207 @param installed_packages list of installed packages (can be None)
208 @return boolean
209 """
210 if isinstance(module_name, str # unicode#
211 ):
212 module_name = [module_name]
213 modules = get_modules_version(python_path)
214 for name in module_name:
215 if name in modules:
216 return True
217 if installed_packages is not None and name in installed_packages:
218 return True
219 pymy = os.path.join(python_path, "lib", "site-packages", name)
220 if os.path.exists(pymy):
221 return True
222 pymy += ".py"
223 if os.path.exists(pymy):
224 return True
225 pymy = pymy.replace(".py", ".cp%d%d-win_amd64.pyd" %
226 (sys.version_info[0], sys.version_info[1]))
227 if os.path.exists(pymy):
228 return True
229 mod = find_module_install(name)
230 if mod:
231 pymy = os.path.join(python_path, "lib", "site-packages", mod.mname)
232 if os.path.exists(pymy):
233 return True
234 return False
237def win_install_packages_other_python(python_path, package_folder, verbose=False, module_list=None, fLOG=print):
238 """
239 Install all packages for another Python distribution
240 where package could be found in a folder
242 @param python_path location of python
243 @param package_folder location of the package (.tar.gz, .whl, .bz2, .tgz)
244 @param verbose display more information
245 @param module_list list of modules to install, if None, it tries to guess a good
246 order to install downloaded packages
247 @param fLOG logging function
248 @return operations ("pip", module) if installed, empty if already installed
249 """
250 files = os.listdir(package_folder)
251 files = [_ for _ in files if os.path.splitext(_)[-1] in {".gz", ".zip", ".whl", ".bz2", ".tgz"} and
252 _ not in {"Scite.zip", "scite.zip"} and
253 not _.startswith("SQLiteSpy_")]
255 # we need to order the package to install them in the right order
256 # it speeds up the process and avoid using C++ compiler
257 operations = []
258 done = set()
259 if module_list is None:
260 full_list = small_set()
261 else:
262 full_list = module_list
264 # existing list
265 installed_packages = get_modules_version(python_path)
267 for mod in full_list:
268 a = _is_package_in_list(mod.name + "-", files)
269 if a is None:
270 continue
271 if a not in done:
272 mname = mod.mname if mod.mname is not None else mod.name
273 if not is_package_installed(python_path, [mod.name, mname], installed_packages):
274 full = os.path.join(package_folder, a)
275 try:
276 op = win_install_package_other_python(
277 python_path, full, verbose=verbose, deps=mod.deps, fLOG=fLOG)
278 except Exception as e:
279 mes = "failed to install {0}: {1}".format(mod.name, full)
280 raise RuntimeError(mes) from e
281 if len(op) > 0:
282 fLOG("[pymy] installed", mod.name, " with ", a)
283 operations.extend(op)
284 done.add(a)
286 if verbose:
287 fLOG("[pymy] *** install packages with unknown dependencies")
288 for pack in files:
289 if pack not in done:
290 full = os.path.join(package_folder, pack)
291 op = win_install_package_other_python(
292 python_path, full, verbose=verbose, fLOG=fLOG)
293 if len(op) > 0:
294 fLOG("[pymy] installed", pack, " with ", full)
295 else:
296 fLOG("[pymy] skipped package", pack, " from ", full)
297 operations.extend(op)
299 return operations