Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""" 

2@file 

3@brief To install packages for a specific distribution. 

4""" 

5from __future__ import print_function 

6 

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 

12 

13if sys.version_info[0] == 2: 

14 FileNotFoundError = Exception 

15 

16 

17def get_modules_version(python_path): 

18 """ 

19 return a dictionary { module:version } 

20 

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) 

30 

31 cmd = prog + " list" 

32 from pip import __version__ 

33 if int(__version__.split(".")[0]) >= 9: 

34 cmd += " --format=columns" 

35 

36 try: 

37 out, err = run_cmd(cmd, wait=True, fLOG=None, change_path=python_path) 

38 except Exception as e: 

39 raise Exception("unable to run: {0}".format(cmd)) from e 

40 

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 Exception("unable to run, #lines {0}\nERR-8:\n{1}\nOUT:\n{2}".format( 

45 len(err.split("\n")), err, out)) 

46 

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 

60 

61 

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. 

65 

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 

72 

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 = [] 

83 

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]) 

89 

90 if not os.path.exists(pip): 

91 raise FileNotFoundError(pip) 

92 

93 cmd = "{0} install {1}".format(pip, package) 

94 if verbose: 

95 fLOG(cmd) 

96 

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) 

104 

105 if cur != python_path: 

106 os.chdir(cur) 

107 

108 if verbose: 

109 fLOG(out) 

110 

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)) 

117 

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)) 

156 

157 return operations 

158 

159 

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 

163 

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(".", "_") 

173 

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) 

181 

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] 

191 

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 

199 

200 

201def is_package_installed(python_path, module_name, installed_packages=None): 

202 """ 

203 not very accurate but it should speed up the process 

204 

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 

235 

236 

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 

241 

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_")] 

254 

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 

263 

264 # existing list 

265 installed_packages = get_modules_version(python_path) 

266 

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 Exception(mes) from e 

281 if len(op) > 0: 

282 fLOG("[pymy] installed", mod.name, " with ", a) 

283 operations.extend(op) 

284 done.add(a) 

285 

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) 

298 

299 return operations