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 Various function to install various python module from various location. 

4""" 

5from __future__ import print_function 

6import sys 

7import re 

8import os 

9import time 

10import importlib 

11import datetime 

12import warnings 

13from pip import __version__ as pip_version 

14from .install_cmd_helper import python_version, run_cmd, unzip_files, get_pip_program 

15from .install_cmd_helper import get_python_program, get_file_modification_date, get_conda_program, is_conda_distribution 

16from .module_install_exceptions import MissingPackageOnPyPiException, MissingInstalledPackageException, InstallError 

17from .module_install_exceptions import DownloadError, MissingVersionWheelException, WrongWheelException, MissingWheelException 

18from .module_install_version import get_pypi_version, get_module_version, annoying_modules, get_module_metadata 

19from .module_install_version import numeric_version, compare_version, choose_most_recent, get_wheel_version 

20from .module_install_page_wheel import get_page_wheel, read_page_wheel, save_page_wheel, enumerate_links_module, extract_all_links 

21from .missing_license import missing_module_licenses 

22from .internet_settings import default_user_agent 

23from .install_cmd_regex import regex_wheel_versions 

24 

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

26 from urlparse import urlsplit 

27 import urllib2 as urllib_request 

28 import urllib2 as urllib_error 

29 import xmlrpclib as xmlrpc_client 

30 from codecs import open 

31 FileNotFoundError = Exception 

32 PermissionError = Exception 

33else: 

34 from urllib.parse import urlsplit 

35 import urllib.request as urllib_request 

36 import urllib.error as urllib_error 

37 import importlib.util 

38 import xmlrpc.client as xmlrpc_client 

39 

40 

41def _filter_pip_out(out): 

42 lines = out.split('\n') 

43 res = [] 

44 for line in lines: 

45 if "WARNING:" in line: 

46 continue 

47 if "Consider adding this directory to PATH" in line: 

48 continue 

49 res.append(line) 

50 return "\n".join(res).strip(' \n\r\t') 

51 

52 

53class ModuleInstall: 

54 

55 """ 

56 defines the necessary information for a module 

57 

58 .. exref:: 

59 :title: Installation from GitHub 

60 

61 :: 

62 

63 ModuleInstall("pyquickhelper", "github", 

64 "sdpython").install(temp_folder="temp") 

65 """ 

66 

67 allowedKind = ["pip", "github", "exe", "exe2", "wheel", "wheel2"] 

68 exeLocation = "https://www.lfd.uci.edu/~gohlke/pythonlibs/" 

69 exeLocationXd_Default = "http://www.xavierdupre.fr/enseignement/setup/" 

70 gitexe = r"C:\Program Files (x86)\Git" 

71 github_pattern_zip = "https://github.com/{1}/{0}/archive/{2}.zip" 

72 github_pattern_git = "https://github.com/{1}/{0}.git{2}" 

73 

74 @staticmethod 

75 def is_annoying(module_name): 

76 """ 

77 some modules are not available on pipy 

78 """ 

79 return module_name in annoying_modules 

80 

81 def __init__(self, name, kind="pip", gitrepo=None, mname=None, fLOG=print, 

82 version=None, script=None, index_url=None, deps=None, 

83 purpose=None, usage=None, web=None, source=None, custom=None, 

84 branch="master", pip_options=None, overwrite=None, post=None, 

85 skip_import=False, pipgit=False): 

86 """ 

87 @param name name 

88 @param kind kind of installation (*pip*, *github*, *wheel*) 

89 @param gitrepo github repository (example: sdpython) 

90 @param mname sometimes, the module name is different from its official name 

91 @param version to install a specific version (None for the latest) 

92 @param fLOG logging function 

93 @param script some extensions are not a module but an application (such as ``spyder``), 

94 the class will check this script is available 

95 @param deps overwrite deps parameters when installing the module 

96 @param index_url to get the package from a custom pypi server 

97 @param purpose purpose of the module 

98 @param usage main usage for the module 

99 @param web website for the module, if None, default to pipy 

100 @param source to overwrite parameter *source* of methods 

101 @see me download, @see me install or @see me update. 

102 @param custom custom instructions to install, usually 

103 ``['build', 'install']`` to run 

104 ``setup.py build`` and ``setup.py install`` 

105 @param branch only necessary for install process with github 

106 @param pip_options additional options for pip (list) 

107 @param overwrite overwrite the location of the wheel 

108 @param post instructions post installation (look for this parameter 

109 in the code to see what is supported) 

110 @param pipgit install the module with ``pip + git`` instead of 

111 getting the full archive 

112 @param skip_import added to indicate the module cannot be imported 

113 

114 .. versionchanged:: 1.1 

115 Parameters *source*, *custom*, *branch*, *pip_options*, *overwrite*, *post* were added. 

116 Parameter *skip_import* was introduced to skip the checking of the installation. 

117 Parameter *pipgit* was added. 

118 """ 

119 if kind != "pip" and version is not None: 

120 raise NotImplementedError( 

121 "version can be only specified if kind=='pip'") 

122 

123 self.name = name 

124 self.kind = kind 

125 self.gitrepo = gitrepo 

126 self.version = version 

127 self.mname = mname 

128 self.script = script 

129 self.index_url = index_url 

130 self.deps = deps 

131 self.purpose = purpose 

132 self.usage = usage 

133 self.existing_version = None 

134 self.source = source 

135 self.custom = custom 

136 self.branch = branch 

137 self.pip_options = pip_options 

138 self.overwrite = overwrite 

139 self.post_installation = post 

140 self.pipgit = pipgit 

141 

142 if self.mname == self.name: 

143 raise ValueError( 

144 "Do not specify mname if it is equal to name '{0}'.".format(self.name)) 

145 if self.pipgit and self.gitrepo is None: 

146 raise ValueError("If pipgit=True, gitrepo must be specified.") 

147 if self.pipgit and kind != 'github': 

148 raise ValueError( 

149 "If pipgit=True, kind must be 'github' not '{0}'.".format(kind)) 

150 if self.version is not None and self.gitrepo is not None: 

151 raise ValueError("version must be None if gitrepo is not.") 

152 

153 if self.mname is not None and self.mname.startswith("-"): 

154 self.mname = self.mname[1:] 

155 self.skip_import = True 

156 else: 

157 self.skip_import = skip_import 

158 self.web = web if web is not None else ( 

159 "https://pypi.python.org/pypi/" + self.name) 

160 

161 if self.kind not in ModuleInstall.allowedKind: 

162 raise Exception( 

163 "unable to interpret kind {0}, it should be in {1}".format( 

164 kind, ",".join( 

165 ModuleInstall.allowedKind))) 

166 if self.kind == "github" and self.gitrepo is None: 

167 raise Exception("gitrepo cannot be empty") 

168 

169 self.fLOG = fLOG 

170 

171 def copy(self, version=None): 

172 """ 

173 copy the module, if version is not None, change the version number 

174 

175 @param version version number or None for unchanged 

176 @return @see cl ModuleInstall 

177 

178 .. versionadded:: 1.0 

179 """ 

180 mod = ModuleInstall(**self.as_dict()) 

181 if version is not None: 

182 mod.version = version 

183 return mod 

184 

185 def as_dict(self, rst_link=False): 

186 """ 

187 returns the members in a dictionary 

188 

189 @param rst_link if True, add rst_link, license, classifier 

190 @return dictionary 

191 """ 

192 r = dict(name=self.name, kind=self.kind, gitrepo=self.gitrepo, version=self.version, 

193 mname=self.mname, script=self.script, deps=self.deps, index_url=self.index_url, 

194 purpose=self.purpose, usage=self.usage, web=self.web, 

195 post=None if self.post_installation is None else self.post_installation.copy(), 

196 skip_import=self.skip_import, pipgit=self.pipgit) 

197 if rst_link: 

198 r["rst_link"] = "`{0} <{1}>`_".format(self.name, self.web) 

199 r["license"] = self.get_installed_license() 

200 r["installed"] = self.get_installed_version() 

201 r["classifier"] = self.get_installed_classifier() 

202 return r 

203 

204 def __cmp__(self, o): 

205 """ 

206 to sort modules 

207 

208 @param o other module 

209 @return -1, 0, 1 

210 """ 

211 def compare(v1, v2): 

212 if v1 is None: 

213 if v2 is None: 

214 return 0 

215 else: 

216 return 1 

217 else: 

218 if v2 is None: 

219 return -1 

220 else: 

221 if v1 < v2: 

222 return -1 

223 elif v1 > v2: 

224 return 1 

225 else: 

226 return 0 

227 

228 r = compare(self.usage, o.usage) 

229 if r != 0: 

230 return r 

231 r = compare(self.name.lower(), o.name.lower()) 

232 return r 

233 

234 def __lt__(self, o): 

235 """ 

236 overload operator ``<`` 

237 

238 @param o other module 

239 @return boolean 

240 """ 

241 return self.__cmp__(o) < 0 

242 

243 @staticmethod 

244 def clear_cache(): 

245 """ 

246 clear the local cache to get wheel link 

247 """ 

248 if os.path.exists(ModuleInstall._page_cache_html): 

249 os.remove(ModuleInstall._page_cache_html) 

250 if os.path.exists(ModuleInstall._page_cache_html2): 

251 os.remove(ModuleInstall._page_cache_html2) 

252 

253 @property 

254 def Purpose(self): 

255 """ 

256 returns the comment 

257 """ 

258 return "-" if self.purpose is None else self.purpose 

259 

260 @property 

261 def Script(self): 

262 """ 

263 returns the script to run if the extension is an application and not a module 

264 """ 

265 exe = os.path.split(sys.executable)[0] 

266 if sys.platform.startswith("win"): 

267 sc = os.path.join(exe, "Scripts", self.script) 

268 else: 

269 sc = os.path.join(exe, "Scripts", os.path.splitext(self.script)[0]) 

270 return sc 

271 

272 def __str__(self): 

273 """ 

274 usual 

275 """ 

276 if self.script is None: 

277 return "{0}:{1}:import {2}:v{3}".format( 

278 self.name, self.kind, self.ImportName, self.version) 

279 else: 

280 return "{0}:{1}:{2}:v{3}".format(self.name, self.kind, self.Script, self.version) 

281 

282 @property 

283 def ImportName(self): 

284 """ 

285 return the import name 

286 """ 

287 if self.mname is not None: 

288 return self.mname 

289 if self.name.startswith("python-"): 

290 return self.name[len("python-"):] 

291 else: 

292 return self.name 

293 

294 def is_installed_local(self): 

295 """ 

296 checks if a module is installed 

297 """ 

298 if self.script is None: 

299 if self.skip_import: 

300 try: 

301 return self.is_installed_local_cmd() 

302 except InstallError: 

303 return False 

304 else: 

305 try: 

306 if "." in self.ImportName: 

307 raise ImportError(self.ImportName) 

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

309 r = importlib.import_module(self.ImportName) 

310 return r 

311 else: 

312 r = importlib.util.find_spec(self.ImportName) 

313 return r is not None 

314 return r is not None 

315 except ImportError: 

316 txt = "import {0} # {1}".format( 

317 self.ImportName, self.name) 

318 try: 

319 exec(txt) 

320 return True 

321 except Exception: 

322 return False 

323 else: 

324 return os.path.exists(self.Script) 

325 

326 def is_installed_local_cmd(self): 

327 """ 

328 Test the module by running a command line. 

329 Does some others verifications for a specific modules such as scipy. 

330 

331 .. versionadded:: 1.1 

332 """ 

333 exe = get_python_program() 

334 if self.skip_import: 

335 cmd = exe + \ 

336 ' -u -c "import pip # pip.main(["show", "{0}"])'.format( 

337 self.name) 

338 out, err = run_cmd(cmd, fLOG=self.fLOG) 

339 if err or out is None: 

340 raise InstallError("Cannot find {0}\nCMD:\n{1}\nOUT:\n{2}\nERR-K1:\n{3}".format( 

341 self.name, cmd, out, err)) 

342 if ("Name: " + self.name) not in out: 

343 raise InstallError("Cannot find {0}\nCMD:\n{1}\nOUT:\n{2}\nERR-K3:\n{3}".format( 

344 self.name, cmd, out, err)) 

345 return True 

346 else: 

347 cmd = exe + \ 

348 ' -u -c "import {0} # {1}"'.format(self.ImportName, self.name) 

349 out, err = run_cmd(cmd, fLOG=self.fLOG) 

350 if err: 

351 raise InstallError("cannot import module {0}\nCMD:\n{1}\nOUT:\n{2}\nERR-K2:\n{3}".format( 

352 self.ImportName, cmd, out, err)) 

353 if self.name == "scipy": 

354 cmd = exe + ' -u -c "import scipy.sparse"' 

355 out, err = run_cmd(cmd, fLOG=self.fLOG) 

356 if err: 

357 if sys.platform.startswith("win") and sys.version_info[:2] >= (3, 5) and "DLL" in err: 

358 mes = ("scipy.sparse is failing, you should check that Visual Studio 2015 is " + 

359 "installed\n{0}\nCMD:\n{1}\nOUT:\n{2}\nERR-M:\n{3}") 

360 raise InstallError(mes.format( 

361 self.ImportName, cmd, out, err)) 

362 raise InstallError("scipy.sparse is failing\n{0}\nCMD:\n{1}\nOUT:\n{2}\nERR-L:\n{3}".format( 

363 self.ImportName, cmd, out, err)) 

364 return True 

365 

366 _page_cache_html2 = os.path.join( 

367 os.path.abspath(os.path.split(__file__)[0]), "page2.html") 

368 

369 def get_exewheel_url_link2(self, file_save=None, wheel=False, source=None): 

370 """ 

371 for windows, get the url of the setup using a webpage 

372 

373 @param file_save for debug purposes 

374 @param wheel returns the wheel file or the exe file 

375 @param source source of the wheels (ex: ``2`` or ``http://...``) 

376 @return url, exe name 

377 

378 .. versionchanged:: 1.1 

379 Parameter *source* was added. 

380 """ 

381 if source is None or source == "2": 

382 source = ModuleInstall.exeLocationXd_Default 

383 source_page = source.rstrip("/") + "/index_modules_list.html" 

384 

385 if "cached_page2" not in self.__dict__: 

386 page = ModuleInstall._page_cache_html2 

387 

388 exi = os.path.exists(page) 

389 if exi: 

390 dt = get_file_modification_date(page) 

391 now = datetime.datetime.now() 

392 df = now - dt 

393 if df > datetime.timedelta(1): 

394 exi = False 

395 

396 if exi: 

397 text = read_page_wheel(page) 

398 self.cached_page2 = text 

399 else: 

400 text = get_page_wheel(source_page) 

401 save_page_wheel(page, text) 

402 self.cached_page2 = text 

403 

404 page = self.cached_page2 

405 reg = re.compile('href=\\"(.*?)\\"') 

406 alls = reg.findall(page) 

407 if len(alls) == 0: 

408 keep = [] 

409 for line in page.split("\n"): 

410 lline = line.lower() 

411 if self.name in lline or (self.mname and self.mname in lline): 

412 keep.append(line) 

413 raise Exception( 

414 "module " + 

415 self.name + "\nexample:\n" + "\n".join(keep)) 

416 

417 version = python_version() 

418 plat = version[0] if version[0] == "win32" else version[1] 

419 if version[1] == '64bit' and version[0] == 'win32': 

420 plat = "amd64" 

421 cp = "-cp%d%d-" % sys.version_info[:2] 

422 py = "-py%d%d-" % sys.version_info[:2] 

423 pyn = "-py%d-" % sys.version_info[0] 

424 links = [_ for _ in alls if "/" + 

425 self.name in _ and (pyn in _ or py in _ or cp in _) and (plat in _ or "-any" in _)] 

426 if len(links) == 0 and "-" in self.name: 

427 name_ = self.name.replace("-", "_") 

428 links = [_ for _ in alls if "/" + 

429 name_ in _ and (pyn in _ or py in _ or cp in _) and (plat in _ or "-any" in _)] 

430 if len(links) == 0: 

431 # exception for guidata 

432 links = [_ for _ in alls if self.name in _ and "-py2.py3-" in _] 

433 if len(links) == 0: 

434 if file_save is not None: 

435 with open(file_save, "w", encoding="utf8") as f: 

436 f.write(page) 

437 short_list = [_ for _ in alls if self.name in _] 

438 raise MissingWheelException("unable to find a single link for " + 

439 self.name + "\n" + "\n".join(short_list)) 

440 

441 # Last filter. Removes modules with a longer name. 

442 pref1 = self.name.lower() + "-" 

443 pref2 = self.name.lower().replace("-", "_") + "-" 

444 pref3 = self.name.lower().replace("_", "-") + "-" 

445 

446 def filter_cond(name): 

447 name = name.split("/")[-1].lower() 

448 return name.startswith(pref1) or name.startswith(pref2) or name.startswith(pref3) 

449 

450 links_ = [_ for _ in links if filter_cond(_)] 

451 if len(links_) == 0: 

452 prefs = "\npref1={0}\npref2={1}\npref3={2}".format( 

453 pref1, pref2, pref3) 

454 raise MissingWheelException("unable to find a single link for " + 

455 self.name + "\n" + "\n".join(links) + prefs) 

456 links = links_ 

457 

458 links = [(lu.split("/")[-1], lu) for lu in links] 

459 links0 = links 

460 

461 if self.name == "numpy": 

462 links = [lu for lu in links if "unoptimized" not in lu[ 

463 0].lower() and "vanilla" not in lu[0].lower()] 

464 

465 if len(links) == 0: 

466 raise Exception("unable to find a single link for " + 

467 self.name + 

468 "\nEX:\n" + 

469 "\n".join(str(_) for _ in links0)) 

470 

471 link = choose_most_recent(links) 

472 self.existing_version = self.extract_version(link[0]) 

473 url, whl = link[1], link[0] 

474 if not whl.endswith(".whl"): 

475 whl += ".whl" 

476 return url, whl 

477 

478 _page_cache_html = os.path.join( 

479 os.path.abspath(os.path.split(__file__)[0]), "page.html") 

480 

481 def get_exewheel_url_link(self, file_save=None, wheel=False): 

482 """ 

483 for windows, get the url of the setup using a webpage 

484 

485 @param file_save for debug purposes 

486 @param wheel returns the wheel file or the exe file 

487 @return url, exe name 

488 """ 

489 if "cached_page" not in self.__dict__: 

490 page = ModuleInstall._page_cache_html 

491 

492 exi = os.path.exists(page) 

493 if exi: 

494 dt = get_file_modification_date(page) 

495 now = datetime.datetime.now() 

496 df = now - dt 

497 if df > datetime.timedelta(1): 

498 exi = False 

499 

500 if exi: 

501 text = read_page_wheel(page) 

502 self.cached_page = text 

503 else: 

504 text = get_page_wheel(ModuleInstall.exeLocation) 

505 save_page_wheel(page, text) 

506 self.cached_page = text 

507 

508 page = self.cached_page 

509 alls = extract_all_links(page) 

510 if len(alls) == 0: 

511 keep = [] 

512 for line in page.split("\n"): 

513 lline = line.lower() 

514 if self.name in lline or (self.mname and self.mname in lline): 

515 keep.append(line) 

516 raise Exception( 

517 "module " + 

518 self.name + "\nexample:\n" + "\n".join(keep)) 

519 

520 version = python_version() 

521 plat = version[0] if version[0] == "win32" else version[1] 

522 if version[1] == '64bit' and version[0] == 'win32': 

523 plat = "amd64" 

524 links = list(enumerate_links_module( 

525 self.name, alls, sys.version_info, plat)) 

526 if len(links) == 0: 

527 if file_save is not None: 

528 with open(file_save, "w", encoding="utf8") as f: 

529 f.write(page) 

530 raise MissingWheelException( 

531 "Unable to find a single link for " + self.name) 

532 nbnone = [lu for lu in links if lu[2] is None] 

533 if len(nbnone) * 2 > len(links): 

534 raise WrongWheelException("Unable to find any version in\n{0}".format( 

535 "\n".join(str(_) for _ in links))) 

536 links0 = links 

537 

538 if self.name == "numpy": 

539 links = [lu for lu in links if "unoptimized" not in lu[ 

540 0].lower() and "vanilla" not in lu[0].lower()] 

541 

542 if len(links) == 0: 

543 raise Exception("unable to find a single link for " + 

544 self.name + 

545 "\nEX:\n" + 

546 "\n".join(str(_) for _ in links0)) 

547 

548 link = choose_most_recent(links) 

549 self.existing_version = self.extract_version(link[0]) 

550 if link[2] is None: 

551 raise WrongWheelException("Unable to find a proper link in {0}\n{1}".format( 

552 link, "\n".join(str(_) for _ in links))) 

553 url, whl = ModuleInstall.exeLocation + link[2], link[0] 

554 if not whl.endswith(".whl"): 

555 whl += ".whl" 

556 return url, whl 

557 

558 def unzipfiles(self, zipf, whereTo): 

559 """ 

560 unzip files from a zip archive 

561 

562 @param zipf archive 

563 @param whereTo destination folder 

564 @return list of unzipped files 

565 """ 

566 return unzip_files(zipf, whereTo, self.fLOG) 

567 

568 def extract_version(self, name): 

569 """ 

570 extract the version from a filename 

571 

572 @param name filename 

573 @return verions (str) 

574 """ 

575 res = None 

576 for i, regve in enumerate(regex_wheel_versions): 

577 reg = re.compile(regve) 

578 res = reg.search(name) 

579 if res is not None: 

580 if i == 6: 

581 return ".".join(res.groups()) 

582 else: 

583 return res.groups()[0] 

584 raise MissingVersionWheelException( 

585 "Unable to extract version number from '{0}'\nREGEX\n{1}".format(name, "\n".join(regex_wheel_versions))) 

586 

587 def download(self, temp_folder=".", force=False, unzipFile=True, 

588 file_save=None, deps=False, source=None): 

589 """ 

590 Downloads the module without installation. 

591 

592 @param temp_folder destination 

593 @param force force the installation even if already installed 

594 @param unzipFile if it can be unzipped, it will be (for github, mostly) 

595 @param file_save for debug purposes, do not change it unless you know what you are doing 

596 @param deps download the dependencies too (only available for pip) 

597 @param source overwrite source of the download, only for wheel packages, 

598 see @see me get_exewheel_url_link2 

599 @return downloaded files 

600 

601 *deps* is overwritten by *self.deps* if not None 

602 If *source* is None, it is overwritten by *self.source*. 

603 """ 

604 disable_options = {} 

605 if source is None: 

606 source = self.source 

607 kind = self.kind 

608 

609 deps = deps if self.deps is None else self.deps 

610 

611 if kind == "pip" or self.pipgit: 

612 # see https://pip.pypa.io/en/latest/reference/pip_install.html 

613 # we use pip install <package> --download=temp_folder 

614 pp = get_pip_program() 

615 if self.pipgit: 

616 br = "@" + \ 

617 self.branch if self.branch not in (None, "master") else "" 

618 link = ModuleInstall.github_pattern_git.format( 

619 self.name, self.gitrepo, br) 

620 cmd = pp + ' download git+' + link 

621 else: 

622 cmd = pp + ' download {0}'.format(self.name) 

623 if self.version is not None: 

624 cmd += "=={0}".format(self.version) 

625 if " " in temp_folder: 

626 raise FileNotFoundError( 

627 "no space allowed in folders: [" + temp_folder + "]") 

628 if deps: 

629 cmd += ' --dest={0}'.format(temp_folder) 

630 else: 

631 cmd += ' --dest={0} --no-deps'.format(temp_folder) 

632 

633 if self.index_url is not None: 

634 slash = '' if self.index_url.endswith('/') else '/' 

635 cmd += ' --no-cache-dir --index={0}{1}simple/'.format( 

636 self.index_url, slash) 

637 parsed_uri = urlsplit(self.index_url) 

638 cmd += " --trusted-host " + parsed_uri.hostname 

639 if self.pip_options is not None: 

640 diff = [_ for _ in self.pip_options if _ not in disable_options] 

641 cmd += " " + " ".join(diff) 

642 

643 out, err = run_cmd( 

644 cmd, wait=True, fLOG=self.fLOG) 

645 if "Successfully downloaded" not in out: 

646 raise DownloadError( 

647 "unable to download with pip " + 

648 str(self) + "\nCMD:\n" + 

649 cmd + "\nOUT:\n" + 

650 out + "\nERR-N:\n" + 

651 err) 

652 lines = out.split("\n") 

653 for line in lines: 

654 if line.strip().startswith("Saved "): 

655 return line.split("Saved")[-1].strip() 

656 elif line.strip().startswith("File was already downloaded"): 

657 return line.split("File was already downloaded")[-1].strip() 

658 raise FileNotFoundError( 

659 "unable to find downloaded file " + 

660 str(self) + "\nCMD:\n" + 

661 cmd + "\nOUT:\n" + 

662 out + "\nERR-O:\n" + 

663 err) 

664 

665 if kind in ("wheel", "wheel2"): 

666 if source is not None: 

667 kind = "wheel2" 

668 ver = python_version() 

669 if ver[0] != "win32": 

670 # nothing to download, you should use pip 

671 return None 

672 else: 

673 if hasattr(self, "overwrite") and self.overwrite is not None: 

674 over = self.overwrite.format(*sys.version_info[0:2]) 

675 url, whl = over, over.split("/")[-1] 

676 self.existing_version = "{0}{1}".format( 

677 *sys.version_info[0:2]) 

678 elif kind == "wheel": 

679 url, whl = self.get_exewheel_url_link( 

680 file_save=file_save, wheel=True) 

681 else: 

682 url, whl = self.get_exewheel_url_link2( 

683 file_save=file_save, wheel=True, source=source) 

684 whlname = os.path.join(temp_folder, whl) 

685 

686 exi = os.path.exists(whlname) 

687 if force or not exi: 

688 

689 self.fLOG("[pymy] downloading", whl) 

690 # self.fLOG("url", url) 

691 if self.existing_version is None: 

692 self.existing_version = self.extract_version(whl) 

693 req = urllib_request.Request( 

694 url, headers={ 

695 'User-agent': default_user_agent}) 

696 try: 

697 u = urllib_request.urlopen(req) 

698 text = u.read() 

699 u.close() 

700 except urllib_error.HTTPError as e: 

701 raise DownloadError("unable to download {} from {}".format( 

702 os.path.split(whlname)[-1], url)) from e 

703 

704 if not os.path.exists(temp_folder): 

705 os.makedirs(temp_folder) 

706 

707 if len(text) <= 1000: 

708 raise WrongWheelException("Size of downloaded wheel is too small: {0}\nurl={1}\nagent={2}".format( 

709 len(text), url, default_user_agent)) 

710 

711 self.fLOG("[pymy] writing", whl) 

712 with open(whlname, "wb") as f: 

713 f.write(text) 

714 

715 return whlname 

716 

717 elif kind == "github" and not self.pipgit: 

718 outfile = os.path.join(temp_folder, self.name + ".zip") 

719 if force or not os.path.exists(outfile): 

720 zipurl = ModuleInstall.github_pattern_zip.format( 

721 self.name, self.gitrepo, self.branch) 

722 self.fLOG("[pymy] downloading", zipurl) 

723 try: 

724 req = urllib_request.Request( 

725 zipurl, headers={ 

726 'User-agent': default_user_agent}) 

727 u = urllib_request.urlopen(req) 

728 text = u.read() 

729 u.close() 

730 except urllib_error.HTTPError as e: 

731 raise Exception( 

732 "unable to get archive from: " + 

733 zipurl) from e 

734 

735 if not os.path.exists(temp_folder): 

736 os.makedirs(temp_folder) 

737 u = open(outfile, "wb") 

738 u.write(text) 

739 u.close() 

740 

741 if unzipFile: 

742 self.fLOG("[pymy] unzipping ", outfile) 

743 files = self.unzipfiles(outfile, temp_folder) 

744 return files 

745 else: 

746 return outfile 

747 

748 if kind in ("exe", "exe2"): 

749 if source is not None: 

750 kind = "exe2" 

751 ver = python_version() 

752 if ver[0] != "win32": 

753 raise Exception( 

754 "this option is not available on other systems than Windows, version={0}".format(ver)) 

755 url, exe = self.get_exewheel_url_link( 

756 file_save=file_save) if kind == "exe" else self.get_exewheel_url_link2( 

757 file_save=file_save, source=source) 

758 

759 self.fLOG("[pymy] downloading", exe) 

760 req = urllib_request.Request( 

761 url, headers={ 

762 'User-agent': default_user_agent}) 

763 u = urllib_request.urlopen(req) 

764 text = u.read() 

765 u.close() 

766 

767 if not os.path.exists(temp_folder): 

768 os.makedirs(temp_folder) 

769 

770 exename = os.path.join(temp_folder, exe) 

771 self.fLOG("[pymy] writing", exe) 

772 with open(exename, "wb") as f: 

773 f.write(text) 

774 return exename 

775 

776 raise ImportError( 

777 "unknown kind: {0} for module {1}".format( 

778 kind, 

779 self.name)) 

780 

781 def get_pypi_version(self, url='https://pypi.python.org/pypi'): 

782 """ 

783 returns the version of a package on pypi 

784 

785 @param url pipy server 

786 @return version 

787 

788 See also `installing_python_packages_programatically.py <https://gist.github.com/rwilcox/755524>`_, 

789 `pkgtools.pypi: PyPI interface <http://pkgtools.readthedocs.org/en/latest/pypi.html>`_. 

790 """ 

791 if url == 'https://pypi.python.org/pypi': 

792 # we use a function which caches the result 

793 return get_pypi_version(self.name) 

794 else: 

795 pypi = xmlrpc_client.ServerProxy(url) 

796 available = pypi.package_releases(self.name) 

797 if available is None or len(available) == 0: 

798 available = pypi.package_releases(self.name.capitalize()) 

799 if (available is None or len(available) == 0) and self.mname is not None: 

800 available = pypi.package_releases(self.mname) 

801 

802 if available is None: 

803 raise MissingPackageOnPyPiException( 

804 "; ".join([self.name, self.name.capitalize(), self.mname])) 

805 

806 return available[0] 

807 

808 def get_pypi_numeric_version(self): 

809 """ 

810 returns the version of a package in pypi 

811 

812 @return tuple 

813 """ 

814 vers = self.get_pypi_version() 

815 if vers is None: 

816 return None 

817 if isinstance(vers, list): 

818 v = self.get_pypi_version() 

819 raise TypeError("unexpected type: {0} -- {1}".format(vers, v)) 

820 return numeric_version(vers) 

821 

822 def get_installed_version(self): 

823 """ 

824 return the version of the installed package 

825 

826 @return version 

827 """ 

828 vers = get_module_version(None) 

829 if self.name in vers: 

830 return vers[self.name] 

831 cap = self.name.capitalize() 

832 if cap in vers: 

833 return vers[cap] 

834 cap = self.name.lower() 

835 if cap in vers: 

836 return vers[cap] 

837 cap = self.name.replace("-", "_") 

838 if cap in vers: 

839 return vers[cap] 

840 cap = self.name.replace("_", "-") 

841 if cap in vers: 

842 return vers[cap] 

843 cap = self.name.lower().replace("_", "-") 

844 if cap in vers: 

845 return vers[cap] 

846 if self.mname is not None: 

847 if self.mname in vers: 

848 return vers[self.mname] 

849 cap = self.mname.lower() 

850 if cap in vers: 

851 return vers[cap] 

852 return None 

853 

854 def get_installed_metadata(self): 

855 """ 

856 return the metadata of the installed package 

857 

858 @return dictionary 

859 """ 

860 r = get_module_metadata(self.name) 

861 if r is None: 

862 return get_module_metadata(self.mname) 

863 else: 

864 return r 

865 

866 def get_installed_license(self): 

867 """ 

868 return the license of the installed package 

869 

870 @return string 

871 """ 

872 meta = self.get_installed_metadata() 

873 if meta is None or len(meta) == 0: 

874 res = None 

875 else: 

876 res = None 

877 for k, v in meta.items(): 

878 if k.lower() == "license": 

879 res = v 

880 break 

881 adm = {None, "", "UNKNOWN"} 

882 if res is not None: 

883 if isinstance(res, list): 

884 res = [_ for _ in res if _ and _ not in adm] 

885 if len(res) > 0: 

886 res = res[0] 

887 else: 

888 res = None 

889 if res in adm: 

890 res = missing_module_licenses.get(self.name, None) 

891 return res 

892 

893 def get_installed_classifier(self): 

894 """ 

895 return the classifier of the installed package 

896 

897 @return string 

898 """ 

899 meta = self.get_installed_metadata() 

900 if meta is None: 

901 return None 

902 for k, v in meta.items(): 

903 if k.lower() == "classifier": 

904 return v 

905 return None 

906 

907 def is_installed_version(self): 

908 """ 

909 tells if a module is installed 

910 

911 @return boolean 

912 """ 

913 return self.get_installed_version() is not None 

914 

915 def get_installed_numeric_version(self): 

916 """ 

917 returns the version as number (not string) 

918 

919 @return tuple 

920 """ 

921 vers = self.get_installed_version() 

922 if vers is None: 

923 return None 

924 return numeric_version(vers) 

925 

926 def has_update(self): 

927 """ 

928 tells if the package has a newer version on pipy 

929 

930 @return boolean 

931 """ 

932 if ModuleInstall.is_annoying(self.name): 

933 return False 

934 vers = self.get_installed_numeric_version() 

935 if self.version is None: 

936 pypi = self.get_pypi_numeric_version() 

937 return compare_version(pypi, vers) > 0 

938 else: 

939 num = numeric_version(self.version) 

940 return compare_version(num, vers) > 0 

941 

942 def _check_installation(self): 

943 """ 

944 some modules uninstall and install modules with another version number, 

945 we try to track that 

946 """ 

947 try: 

948 import numpy 

949 if compare_version(numpy.__version__, "1.10") < 0: 

950 raise InstallError( 

951 "numpy does not have a goof version number, it should be >= 1.10 not {0}".format(numpy.__version__)) 

952 except ImportError: 

953 # not installed 

954 pass 

955 return True 

956 

957 def install(self, force_kind=None, force=False, temp_folder=".", 

958 log=False, options=None, deps=False, source=None, 

959 custom=None, post=None, out_streams=None): 

960 """ 

961 Installs the package. 

962 

963 @param force_kind overwrite self.kind 

964 @param force force the installation even if already installed 

965 @param temp_folder folder where to download the setup 

966 @param log display logs or not 

967 @param options other options to add to the command line (see below) in a list 

968 @param deps install the dependencies too (only available for pip) 

969 @param source overwrite the source of the wheels, 

970 see @see me get_exewheel_url_link2 

971 @param custom overwrite parameters in ``self.custom`` 

972 @param post instructions post installation (see the cnostructor for more help) 

973 @param out_streams if it is a list, the function will add standard outputs 

974 @return boolean 

975 

976 The options mentioned in parameter ``options`` 

977 are described here: `pip install <http://www.pip-installer.org/en/latest/usage.html>`_ 

978 or `setup.py options <http://docs.python.org/3.4/install/>`_ if you 

979 installing a module from github. 

980 

981 .. versionchanged:: 1.0 

982 *deps* is overwritten by *self.deps* if not None 

983 

984 .. versionchanged:: 1.1 

985 On Anaconda (based on function @see fn is_conda_distribution), we try *conda* first 

986 before switching to the regular way if it did not work. 

987 Exception were changed from ``Exception`` to ``InstallError``. 

988 Parameter *source* was added, if None, it is overwritten by *self.source*. 

989 Parameter *custom* was added, it works the same as *source*. 

990 Parameter *post* was added. 

991 Parameter *out_streas* added. 

992 """ 

993 if source is None: 

994 source = self.source 

995 if post is None: 

996 post = self.post_installation 

997 if not force and force_kind is None and is_conda_distribution(): 

998 try: 

999 return self.install(force_kind="conda", force=True, temp_folder=temp_folder, 

1000 log=log, options=options, deps=deps) 

1001 except InstallError as e: 

1002 warnings.warn(str(e)) 

1003 # we try the regular way now 

1004 

1005 if not force and self.is_installed_version(): 

1006 return True 

1007 

1008 deps = deps if self.deps is None else self.deps 

1009 

1010 if options is None: 

1011 options = self.pip_options 

1012 if options is None: 

1013 options = [] 

1014 

1015 kind = force_kind if force_kind is not None else self.kind 

1016 add = (" with " + kind) if kind != self.kind else "" 

1017 self.fLOG("[pymy] installation of " + str(self) + add) 

1018 ret = None 

1019 custom = custom or self.custom 

1020 

1021 if kind == "pip" or self.pipgit: 

1022 if custom is not None: 

1023 raise NotImplementedError( 

1024 "custom must be None not '{0}' when kind is '{1}'".format(custom, kind)) 

1025 pp = get_pip_program() 

1026 if self.pipgit: 

1027 br = "@" + \ 

1028 self.branch if self.branch not in (None, "master") else "" 

1029 link = ModuleInstall.github_pattern_git.format( 

1030 self.name, self.gitrepo, br) 

1031 cmd = pp + ' install git+' + link 

1032 else: 

1033 cmd = pp + " install {0}".format(self.name) 

1034 if self.version is not None: 

1035 cmd += "=={0}".format(self.version) 

1036 if len(options) > 0: 

1037 cmd += " " + " ".join(options) 

1038 

1039 if not deps: 

1040 cmd += ' --no-deps' 

1041 

1042 if self.index_url is not None: 

1043 slash = '' if self.index_url.endswith('/') else '/' 

1044 cmd += ' --no-cache-dir --index={0}{1}simple/'.format( 

1045 self.index_url, slash) 

1046 else: 

1047 cmd += " --cache-dir={0}".format(temp_folder) 

1048 

1049 if self.name == "kivy-garden": 

1050 memo = sys.argv 

1051 sys.argv = [] 

1052 out, err = run_cmd( 

1053 cmd, wait=True, fLOG=self.fLOG) 

1054 out = _filter_pip_out(out) 

1055 if out_streams is not None: 

1056 out_streams.append((cmd, out, err)) 

1057 if self.name == "kivy-garden": 

1058 sys.argv = memo 

1059 

1060 success2 = "Requirement already up-to-date: " + self.name 

1061 uptodate = success2.replace("-", "_") in out.replace("-", "_") 

1062 

1063 if "No distributions matching the version" in out: 

1064 mes = "(1) unable to install with pip {0}\nCMD:\n{1}\nOUT:\n{2}\nERR-P:\n{3}".format( 

1065 str(self), cmd, out, err) 

1066 raise InstallError(mes) 

1067 if "Testing of typecheck-decorator passed without failure." in out: 

1068 ret = True 

1069 elif "Successfully installed" not in out and not uptodate: 

1070 if "error: Unable to find vcvarsall.bat" in out: 

1071 url = "http://www.xavierdupre.fr/blog/2013-07-07_nojs.html" 

1072 mes = "(2) unable to install with pip {0}\nread:\n{1}\nCMD:\n{2}\nOUT:\n{3}\nERR-P:\n{4}".format( 

1073 str(self), url, cmd, out, err) 

1074 raise InstallError(mes) 

1075 if "Requirement already satisfied" not in out and not uptodate: 

1076 mes = "(3) unable to install with pip {0}\nCMD:\n{1}\nOUT:\n{2}\nERR-Q:\n{3}".format( 

1077 str(self), cmd, out, err) 

1078 raise InstallError(mes) 

1079 else: 

1080 ret = not uptodate 

1081 

1082 elif kind == "conda": 

1083 if custom is not None: 

1084 raise NotImplementedError( 

1085 "custom must be None not '{0}' when kind is '{1}'".format(custom, kind)) 

1086 if "--upgrade" in options: 

1087 options = [_ for _ in options if _ != "--upgrade"] 

1088 command = "update" 

1089 else: 

1090 command = "install" 

1091 pp = get_conda_program() 

1092 cmd = pp + " {1} {0}".format(self.name, command) 

1093 if self.version is not None: 

1094 cmd += "=={0}".format(self.version) 

1095 if len(options) > 0: 

1096 cmd += " " + " ".join(options) 

1097 

1098 cmd += " --no-pin --yes --quiet" 

1099 if not deps: 

1100 cmd += ' --no-deps' 

1101 

1102 out, err = run_cmd( 

1103 cmd, wait=True, fLOG=self.fLOG) 

1104 if out_streams is not None: 

1105 out_streams.append((cmd, out, err)) 

1106 if "No distributions matching the version" in out or \ 

1107 "No packages found in current linux" in out: 

1108 mes = "(4) unable to install with conda {0}\nCMD:\n{1}\nOUT:\n{2}\nERR-R:\n{3}".format( 

1109 str(self), cmd, out, err) 

1110 raise InstallError(mes) 

1111 if "Testing of typecheck-decorator passed without failure." in out: 

1112 ret = True 

1113 elif "Successfully installed" not in out: 

1114 if "error: Unable to find vcvarsall.bat" in out: 

1115 url = "http://www.xavierdupre.fr/blog/2013-07-07_nojs.html" 

1116 mes = "(5) unable to install with conda {0}\nread:\n{1}\nCMD:\n{2}\nOUT:\n{3}\nERR-S:\n{4}".format( 

1117 str(self), url, cmd, out, err) 

1118 raise InstallError(mes) 

1119 if "Requirement already satisfied" not in out: 

1120 mes = "(6) unable to install with conda {0}\nCMD:\n{1}\nOUT:\n{2}\nERR-T:\n{3}".format( 

1121 str(self), cmd, out, err) 

1122 raise InstallError(mes) 

1123 else: 

1124 ret = True 

1125 

1126 elif kind in ("wheel", "wheel2"): 

1127 if custom is not None: 

1128 raise NotImplementedError( 

1129 "custom must be None not '{0}' when kind is '{1}'".format(custom, kind)) 

1130 ver = python_version() 

1131 if ver[0] != "win32": 

1132 ret = self.install("pip") 

1133 whlname = self.name 

1134 ret = True 

1135 else: 

1136 whlname = self.download( 

1137 temp_folder=temp_folder, 

1138 force=force, 

1139 unzipFile=True, 

1140 source=source) 

1141 vers = self.get_installed_numeric_version() 

1142 ret = True 

1143 if vers is not None: 

1144 whlvers = numeric_version(get_wheel_version(whlname)) 

1145 if compare_version(vers, whlvers) >= 0: 

1146 self.fLOG("[pymy] skipping, no newer version {0} <= {1}: whl= {2}".format( 

1147 whlvers, vers, whlname)) 

1148 ret = False 

1149 if ret: 

1150 self.fLOG("[pymy] installing", os.path.split(whlname)[-1]) 

1151 

1152 pip = get_pip_program() 

1153 cmd = pip + " install {0}".format(whlname) 

1154 if self.version is not None: 

1155 cmd += "=={0}".format(self.version) 

1156 if len(options) > 0: 

1157 opts = list(options) 

1158 if len(opts): 

1159 cmd += " " + " ".join(opts) 

1160 if not deps: 

1161 cmd += ' --no-deps' 

1162 

1163 out, err = run_cmd( 

1164 cmd, wait=True, fLOG=self.fLOG) 

1165 if out_streams is not None: 

1166 out_streams.append((cmd, out, err)) 

1167 if "No distributions matching the version" in out: 

1168 mes = "(7) unable to install with wheel {0}\nCMD:\n{1}\nOUT:\n{2}\nERR-U:\n{3}".format( 

1169 str(self), cmd, out, err) 

1170 raise InstallError(mes) 

1171 if "Testing of typecheck-decorator passed without failure." in out: 

1172 ret = True 

1173 elif "Successfully installed" not in out: 

1174 if "error: Unable to find vcvarsall.bat" in out: 

1175 url = "http://www.xavierdupre.fr/blog/2013-07-07_nojs.html" 

1176 mes = "(8) unable to install with wheel {0}\nread:\n{1}\nCMD:\n{2}\nOUT:\n{3}\nERR-V:\n{4}".format( 

1177 str(self), url, cmd, out, err) 

1178 raise InstallError(mes) 

1179 if "Requirement already satisfied" not in out and "Requirement already up-to-date" not in out: 

1180 mes = "(9) unable to install with wheel {0}\nCMD:\n{1}\nOUT:\n{2}\nERR-W:\n{3}".format( 

1181 str(self), cmd, out, err) 

1182 raise InstallError(mes) 

1183 else: 

1184 ret = True 

1185 

1186 elif kind == "github" and not self.pipgit: 

1187 # the following code requires admin rights 

1188 # if python_version()[0].startswith("win") and kind == "git" and not os.path.exists(ModuleInstall.gitexe) : 

1189 # raise FileNotFoundError("you need to install github first: see http://windows.github.com/") 

1190 # if python_version()[0].startswith("win"): 

1191 # os.chdir(os.path.join(ModuleInstall.gitexe,"bin")) 

1192 # cmd = pip + " install -e 

1193 # git://github.com/{0}/{1}-python.git#egg={1}".format(self.gitrepo, 

1194 # self.name) 

1195 

1196 files = self.download(temp_folder=temp_folder, 

1197 force=force, unzipFile=True) 

1198 setu = [_ for _ in files if _.endswith("setup.py")] 

1199 if len(setu) == 0: 

1200 raise Exception( 

1201 "unable to find setup.py for module " + 

1202 self.name) 

1203 if len(setu) > 1: 

1204 setu = [(len(_), _) for _ in setu] 

1205 setu.sort() 

1206 if setu[0][0] == setu[1][0]: 

1207 raise InstallError( 

1208 "(10) more than one setup.py for module " + 

1209 self.name + 

1210 "\n" + 

1211 "\n".join( 

1212 str(_) for _ in setu)) 

1213 self.fLOG( 

1214 "[pymy] warning: more than one setup: " + str(setu)) 

1215 setu = [setu[0][1]] 

1216 setu = os.path.abspath(setu[0]) 

1217 

1218 self.fLOG("[pymy] install ", setu[0]) 

1219 cwd = os.getcwd() 

1220 os.chdir(os.path.split(setu)[0]) 

1221 if custom is None: 

1222 custom = ["install"] 

1223 cmds = [] 

1224 for command in custom: 

1225 cmd1 = "{0} setup.py {1}".format( 

1226 sys.executable.replace("pythonw.exe", "python.exe"), 

1227 command) 

1228 cmds.append(cmd1) 

1229 

1230 def enumerate_filtered_option(options): 

1231 for o in options: 

1232 if o not in ('--no-deps', '--upgrade'): 

1233 yield o 

1234 

1235 filter_options = list(enumerate_filtered_option(options)) 

1236 if len(filter_options) > 0: 

1237 cmds[-1] += " " + " ".join(filter_options) 

1238 

1239 if deps: 

1240 # it will not work 

1241 # cmd += ' --no-deps' 

1242 pass 

1243 

1244 outs = "" 

1245 errs = "" 

1246 for cmd in cmds: 

1247 out, err = run_cmd( 

1248 cmd, wait=True, fLOG=self.fLOG) 

1249 if out_streams is not None: 

1250 out_streams.append((cmd, out, err)) 

1251 if len(outs) > 0: 

1252 outs += "\n" 

1253 if len(errs) > 0: 

1254 errs += "\n" 

1255 outs += out 

1256 errs += err 

1257 os.chdir(cwd) 

1258 

1259 out, err = outs, errs 

1260 if "Successfully installed" not in out and "install C" not in out: 

1261 if "Finished processing dependencies" not in out: 

1262 raise InstallError( 

1263 "(11) unable to install with github " + 

1264 str(self) + 

1265 "\nOUT:\n" + 

1266 out + 

1267 "\nERR-X:\n" + 

1268 err) 

1269 self.fLOG( 

1270 "warning: ``Successfully installed`` or ``install C`` not found") 

1271 if "Permission denied" in out: 

1272 raise PermissionError(" ".join(["(12) unable to install with github", str(self), "\n--CMD--:\n", 

1273 "\n".join(cmds), "\n--OUT--\n", out, "\n--ERR-Y--\n", err])) 

1274 ret = True 

1275 

1276 elif kind == "exe": 

1277 if custom is not None: 

1278 raise NotImplementedError( 

1279 "custom must be None not '{0}' when kind is '{1}'".format(custom, kind)) 

1280 ver = python_version() 

1281 if ver[0] != "win32": 

1282 ret = self.install("pip") 

1283 else: 

1284 exename = self.download( 

1285 temp_folder=temp_folder, 

1286 force=force, 

1287 unzipFile=True, 

1288 source=source) 

1289 self.fLOG("[pymy] executing", os.path.split(exename)[-1]) 

1290 out, err = run_cmd( 

1291 exename + " /s /qn /SILENT", wait=True, fLOG=self.fLOG) 

1292 if out_streams is not None: 

1293 out_streams.append((cmd, out, err)) 

1294 ret = len(err) == 0 

1295 

1296 elif kind == "exe2": 

1297 if custom is not None: 

1298 raise NotImplementedError( 

1299 "custom must be None not '{0}' when kind is '{1}'".format(custom, kind)) 

1300 ver = python_version() 

1301 if ver[0] != "win32": 

1302 ret = self.install("pip") 

1303 else: 

1304 exename = self.download( 

1305 temp_folder=temp_folder, 

1306 force=force, 

1307 unzipFile=True, 

1308 source=source) 

1309 self.fLOG("[pymy] executing", os.path.split(exename)[-1]) 

1310 out, err = run_cmd( 

1311 exename + " /s /qn", wait=True, fLOG=self.fLOG) 

1312 if out_streams is not None: 

1313 out_streams.append((cmd, out, err)) 

1314 ret = len(err) == 0 

1315 else: 

1316 raise ImportError( 

1317 "unknown kind: {0} for module {1}".format( 

1318 kind, 

1319 self.name)) 

1320 

1321 if ret is not None and ret: 

1322 self._check_installation() 

1323 

1324 # at this stage, there is a bug, for some executable, the execution 

1325 # takes longer than expected 

1326 # if not self.is_installed_version() : 

1327 # raise Exception("unable to install module: {0}, str:{1}".format(self.name, self)) 

1328 

1329 if ret is not None and ret and self.script is not None: 

1330 if sys.platform.startswith("win"): 

1331 # here, we have to wait until the script is installed 

1332 ti = 0 

1333 while not self.is_installed_local(): 

1334 time.sleep(0.5) 

1335 ti += 0.5 

1336 if ti > 60: 

1337 self.fLOG( 

1338 "wait for the end of execution of ", 

1339 self.name) 

1340 ti = 0 

1341 

1342 # we add set path=%path%;PYTHONPATH 

1343 with open(self.Script, "r") as f: 

1344 full = f.read() 

1345 

1346 exe = os.path.split(sys.executable)[0] 

1347 cmd = "set path=%path%;{0}".format(exe) 

1348 if cmd not in full: 

1349 self.fLOG("[pymy] add {0} to {1}".format(cmd, self.Script)) 

1350 with open(self.Script, "w") as f: 

1351 if full.startswith("@echo off"): 

1352 f.write( 

1353 full.replace( 

1354 "@echo off", 

1355 "@echo off\n{0}".format(cmd))) 

1356 else: 

1357 f.write(cmd) 

1358 f.write("\n") 

1359 f.write(full) 

1360 

1361 if ret is not None and post is not None: 

1362 self.fLOG("[pymy] _ run_post_installation [begin]", post) 

1363 ret = self.run_post_installation(post) 

1364 self.fLOG("[pymy] _ run_post_installation [end]") 

1365 

1366 if ret is not None and ret: 

1367 # we check the module was properly installed 

1368 if not self.is_installed_local_cmd(): 

1369 raise InstallError( 

1370 "** unable to install module {0}, unable to import it".format(self.name)) 

1371 

1372 return ret 

1373 

1374 def run_post_installation(self, post): 

1375 """ 

1376 Run instructions post installation 

1377 

1378 @param post dictionary, instructions post installation 

1379 @return boolean 

1380 

1381 Example: 

1382 

1383 :: 

1384 

1385 post = dict( 

1386 cmd_python="Scripts\\\\pywin32_postinstall.py -install") 

1387 

1388 .. versionadded:: 1.1 

1389 """ 

1390 if not isinstance(post, dict): 

1391 raise TypeError("expecting a dictionary") 

1392 for k, v in post.items(): 

1393 if k == "cmd_python": 

1394 # processed just above 

1395 continue 

1396 if k == "pre_cmd": 

1397 if v == "module_install_preprocess": 

1398 self.fLOG("[pymy] _ module_install_preprocess [begin]") 

1399 self.module_install_preprocess(post) 

1400 self.fLOG("[pymy] _ module_install_preprocess [end]") 

1401 else: 

1402 raise ValueError( 

1403 "Unable to interpret value '{0}'.".format(v)) 

1404 else: 

1405 raise KeyError("Unable to interpret command '{0}'".format(k)) 

1406 for k, v in post.items(): 

1407 if k == "cmd_python": 

1408 exe = os.path.abspath(sys.executable) 

1409 cmd = '"{0}" {1}'.format(exe, v.format(os.path.dirname(exe))) 

1410 out, err = run_cmd(cmd, wait=True, fLOG=self.fLOG) 

1411 if err is not None and len(err) > 0: 

1412 raise InstallError( 

1413 "Post installation script failed.\nCMD\n{0}\nOUT\n{1}\nERR-Z\n{2}".format(cmd, out, err)) 

1414 self.fLOG("[pymy] OUT:\n{0}".format(out)) 

1415 elif k == "pre_cmd": 

1416 # processed just above 

1417 continue 

1418 else: 

1419 raise KeyError("Unable to interpret command '{0}'".format(k)) 

1420 return True 

1421 

1422 def update(self, force_kind=None, force=False, temp_folder=".", 

1423 log=False, options=None, deps=False, source=None): 

1424 """ 

1425 Updates the package if necessary, we use 

1426 ``pip install <module_name> --upgrade --no-deps``, 

1427 

1428 @param force_kind overwrite self.kind 

1429 @param force force the installation even if not need to update 

1430 @param temp_folder folder where to download the setup 

1431 @param log display logs or not 

1432 @param options others options to add to the command line (see below) an a list 

1433 @param deps download the dependencies too (only available for pip) 

1434 @param source overwrite the source of the wheel, see @see me get_exewheel_url_link2 

1435 @return boolean 

1436 

1437 The options mentioned in parameter ``options`` 

1438 are described here: `pip install <http://www.pip-installer.org/en/latest/usage.html>`_ 

1439 or `setup.py options <http://docs.python.org/3.4/install/>`_ if you 

1440 installing a module from github. 

1441 

1442 .. versionchanged:: 1.1 

1443 Parameter *source* was added, if None, it is overwritten by *self.source*. 

1444 """ 

1445 if source is None: 

1446 source = self.source 

1447 if ModuleInstall.is_annoying(self.name): 

1448 return False 

1449 

1450 if not self.is_installed_version(): 

1451 raise MissingInstalledPackageException(self.name) 

1452 

1453 if not force and not self.has_update(): 

1454 return True 

1455 

1456 self.fLOG("[pymy] update of ", self) 

1457 

1458 options = [] if options is None else list(options) 

1459 for opt in ["--upgrade", "--no-deps"]: 

1460 if opt not in options: 

1461 if not deps or opt == "--no-deps": 

1462 options.append(opt) 

1463 

1464 res = self.install(force_kind=force_kind, force=True, 

1465 temp_folder=temp_folder, log=log, options=options, source=source) 

1466 return res 

1467 

1468 def module_install_preprocess(self, post): 

1469 """ 

1470 Run some preprocessing for specific modules 

1471 

1472 @param post dictionary 

1473 """ 

1474 if self.name == "pywin32": 

1475 self.fLOG("[pymy] _ module_install_preprocess", self.name) 

1476 if "cmd_python" not in post: 

1477 raise KeyError("Key 'cmd_python' is not in post.") 

1478 cmd_python = post["cmd_python"] 

1479 name = cmd_python.split()[0].format( 

1480 os.path.dirname(sys.executable)) 

1481 self.fLOG("[pymy] _ opening", name) 

1482 if not os.path.exists(name): 

1483 raise FileNotFoundError(name) 

1484 with open(name, "r") as f: 

1485 content = f.read() 

1486 repby = "os.path.exists(dest)" 

1487 if repby not in content: 

1488 self.fLOG("[pymy] _ Preprocess '{0}'".format(name)) 

1489 rep = "def CopyTo(desc, src, dest):" 

1490 content = content.replace( 

1491 rep, "{0}\n if {1}: return".format(rep, repby)) 

1492 with open(name, "w") as f: 

1493 f.write(content) 

1494 self.fLOG("[pymy] _ Done.") 

1495 else: 

1496 self.fLOG( 

1497 "Skip preprocess '{0}' because '{1}' was found.".format(name, repby)) 

1498 else: 

1499 raise ValueError( 

1500 "No preprocessing for module '{0}'.".format(self.name))