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 

41class ModuleInstall: 

42 

43 """ 

44 defines the necessary information for a module 

45 

46 .. exref:: 

47 :title: Installation from GitHub 

48 

49 :: 

50 

51 ModuleInstall("pyquickhelper", "github", 

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

53 """ 

54 

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

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

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

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

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

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

61 

62 @staticmethod 

63 def is_annoying(module_name): 

64 """ 

65 some modules are not available on pipy 

66 """ 

67 return module_name in annoying_modules 

68 

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

70 version=None, script=None, index_url=None, deps=None, 

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

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

73 skip_import=False, pipgit=False): 

74 """ 

75 @param name name 

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

77 @param gitrepo github repository (example: sdpython) 

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

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

80 @param fLOG logging function 

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

82 the class will check this script is available 

83 @param deps overwrite deps parameters when installing the module 

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

85 @param purpose purpose of the module 

86 @param usage main usage for the module 

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

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

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

90 @param custom custom instructions to install, usually 

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

92 ``setup.py build`` and ``setup.py install`` 

93 @param branch only necessary for install process with github 

94 @param pip_options additional options for pip (list) 

95 @param overwrite overwrite the location of the wheel 

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

97 in the code to see what is supported) 

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

99 getting the full archive 

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

101 

102 .. versionchanged:: 1.1 

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

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

105 Parameter *pipgit* was added. 

106 """ 

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

108 raise NotImplementedError( 

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

110 

111 self.name = name 

112 self.kind = kind 

113 self.gitrepo = gitrepo 

114 self.version = version 

115 self.mname = mname 

116 self.script = script 

117 self.index_url = index_url 

118 self.deps = deps 

119 self.purpose = purpose 

120 self.usage = usage 

121 self.existing_version = None 

122 self.source = source 

123 self.custom = custom 

124 self.branch = branch 

125 self.pip_options = pip_options 

126 self.overwrite = overwrite 

127 self.post_installation = post 

128 self.pipgit = pipgit 

129 

130 if self.mname == self.name: 

131 raise ValueError( 

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

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

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

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

136 raise ValueError( 

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

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

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

140 

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

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

143 self.skip_import = True 

144 else: 

145 self.skip_import = skip_import 

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

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

148 

149 if self.kind not in ModuleInstall.allowedKind: 

150 raise Exception( 

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

152 kind, ",".join( 

153 ModuleInstall.allowedKind))) 

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

155 raise Exception("gitrepo cannot be empty") 

156 

157 self.fLOG = fLOG 

158 

159 def copy(self, version=None): 

160 """ 

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

162 

163 @param version version number or None for unchanged 

164 @return @see cl ModuleInstall 

165 

166 .. versionadded:: 1.0 

167 """ 

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

169 if version is not None: 

170 mod.version = version 

171 return mod 

172 

173 def as_dict(self, rst_link=False): 

174 """ 

175 returns the members in a dictionary 

176 

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

178 @return dictionary 

179 """ 

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

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

182 purpose=self.purpose, usage=self.usage, web=self.web, 

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

184 skip_import=self.skip_import, pipgit=self.pipgit) 

185 if rst_link: 

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

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

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

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

190 return r 

191 

192 def __cmp__(self, o): 

193 """ 

194 to sort modules 

195 

196 @param o other module 

197 @return -1, 0, 1 

198 """ 

199 def compare(v1, v2): 

200 if v1 is None: 

201 if v2 is None: 

202 return 0 

203 else: 

204 return 1 

205 else: 

206 if v2 is None: 

207 return -1 

208 else: 

209 if v1 < v2: 

210 return -1 

211 elif v1 > v2: 

212 return 1 

213 else: 

214 return 0 

215 

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

217 if r != 0: 

218 return r 

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

220 return r 

221 

222 def __lt__(self, o): 

223 """ 

224 overload operator ``<`` 

225 

226 @param o other module 

227 @return boolean 

228 """ 

229 return self.__cmp__(o) < 0 

230 

231 @staticmethod 

232 def clear_cache(): 

233 """ 

234 clear the local cache to get wheel link 

235 """ 

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

237 os.remove(ModuleInstall._page_cache_html) 

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

239 os.remove(ModuleInstall._page_cache_html2) 

240 

241 @property 

242 def Purpose(self): 

243 """ 

244 returns the comment 

245 """ 

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

247 

248 @property 

249 def Script(self): 

250 """ 

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

252 """ 

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

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

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

256 else: 

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

258 return sc 

259 

260 def __str__(self): 

261 """ 

262 usual 

263 """ 

264 if self.script is None: 

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

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

267 else: 

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

269 

270 @property 

271 def ImportName(self): 

272 """ 

273 return the import name 

274 """ 

275 if self.mname is not None: 

276 return self.mname 

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

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

279 else: 

280 return self.name 

281 

282 def is_installed_local(self): 

283 """ 

284 checks if a module is installed 

285 """ 

286 if self.script is None: 

287 if self.skip_import: 

288 try: 

289 return self.is_installed_local_cmd() 

290 except InstallError: 

291 return False 

292 else: 

293 try: 

294 if "." in self.ImportName: 

295 raise ImportError(self.ImportName) 

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

297 r = importlib.import_module(self.ImportName) 

298 return r 

299 else: 

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

301 return r is not None 

302 return r is not None 

303 except ImportError: 

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

305 self.ImportName, self.name) 

306 try: 

307 exec(txt) 

308 return True 

309 except Exception: 

310 return False 

311 else: 

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

313 

314 def is_installed_local_cmd(self): 

315 """ 

316 Test the module by running a command line. 

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

318 

319 .. versionadded:: 1.1 

320 """ 

321 exe = get_python_program() 

322 if self.skip_import: 

323 cmd = exe + \ 

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

325 self.name) 

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

327 if err or out is None: 

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

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

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

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

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

333 return True 

334 else: 

335 cmd = exe + \ 

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

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

338 if err: 

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

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

341 if self.name == "scipy": 

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

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

344 if err: 

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

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

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

348 raise InstallError(mes.format( 

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

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

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

352 return True 

353 

354 _page_cache_html2 = os.path.join( 

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

356 

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

358 """ 

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

360 

361 @param file_save for debug purposes 

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

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

364 @return url, exe name 

365 

366 .. versionchanged:: 1.1 

367 Parameter *source* was added. 

368 """ 

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

370 source = ModuleInstall.exeLocationXd_Default 

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

372 

373 if "cached_page2" not in self.__dict__: 

374 page = ModuleInstall._page_cache_html2 

375 

376 exi = os.path.exists(page) 

377 if exi: 

378 dt = get_file_modification_date(page) 

379 now = datetime.datetime.now() 

380 df = now - dt 

381 if df > datetime.timedelta(1): 

382 exi = False 

383 

384 if exi: 

385 text = read_page_wheel(page) 

386 self.cached_page2 = text 

387 else: 

388 text = get_page_wheel(source_page) 

389 save_page_wheel(page, text) 

390 self.cached_page2 = text 

391 

392 page = self.cached_page2 

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

394 alls = reg.findall(page) 

395 if len(alls) == 0: 

396 keep = [] 

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

398 lline = line.lower() 

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

400 keep.append(line) 

401 raise Exception( 

402 "module " + 

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

404 

405 version = python_version() 

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

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

408 plat = "amd64" 

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

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

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

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

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

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

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

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

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

418 if len(links) == 0: 

419 # exception for guidata 

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

421 if len(links) == 0: 

422 if file_save is not None: 

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

424 f.write(page) 

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

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

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

428 

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

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

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

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

433 

434 def filter_cond(name): 

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

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

437 

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

439 if len(links_) == 0: 

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

441 pref1, pref2, pref3) 

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

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

444 links = links_ 

445 

446 links = [(l.split("/")[-1], l) for l in links] 

447 links0 = links 

448 

449 if self.name == "numpy": 

450 links = [l for l in links if "unoptimized" not in l[ 

451 0].lower() and "vanilla" not in l[0].lower()] 

452 

453 if len(links) == 0: 

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

455 self.name + 

456 "\nEX:\n" + 

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

458 

459 link = choose_most_recent(links) 

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

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

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

463 whl += ".whl" 

464 return url, whl 

465 

466 _page_cache_html = os.path.join( 

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

468 

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

470 """ 

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

472 

473 @param file_save for debug purposes 

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

475 @return url, exe name 

476 """ 

477 if "cached_page" not in self.__dict__: 

478 page = ModuleInstall._page_cache_html 

479 

480 exi = os.path.exists(page) 

481 if exi: 

482 dt = get_file_modification_date(page) 

483 now = datetime.datetime.now() 

484 df = now - dt 

485 if df > datetime.timedelta(1): 

486 exi = False 

487 

488 if exi: 

489 text = read_page_wheel(page) 

490 self.cached_page = text 

491 else: 

492 text = get_page_wheel(ModuleInstall.exeLocation) 

493 save_page_wheel(page, text) 

494 self.cached_page = text 

495 

496 page = self.cached_page 

497 alls = extract_all_links(page) 

498 if len(alls) == 0: 

499 keep = [] 

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

501 lline = line.lower() 

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

503 keep.append(line) 

504 raise Exception( 

505 "module " + 

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

507 

508 version = python_version() 

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

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

511 plat = "amd64" 

512 links = list(enumerate_links_module( 

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

514 if len(links) == 0: 

515 if file_save is not None: 

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

517 f.write(page) 

518 raise MissingWheelException( 

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

520 nbnone = [l for l in links if l[2] is None] 

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

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

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

524 links0 = links 

525 

526 if self.name == "numpy": 

527 links = [l for l in links if "unoptimized" not in l[ 

528 0].lower() and "vanilla" not in l[0].lower()] 

529 

530 if len(links) == 0: 

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

532 self.name + 

533 "\nEX:\n" + 

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

535 

536 link = choose_most_recent(links) 

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

538 if link[2] is None: 

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

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

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

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

543 whl += ".whl" 

544 return url, whl 

545 

546 def unzipfiles(self, zipf, whereTo): 

547 """ 

548 unzip files from a zip archive 

549 

550 @param zipf archive 

551 @param whereTo destination folder 

552 @return list of unzipped files 

553 """ 

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

555 

556 def extract_version(self, name): 

557 """ 

558 extract the version from a filename 

559 

560 @param name filename 

561 @return verions (str) 

562 """ 

563 res = None 

564 for i, regve in enumerate(regex_wheel_versions): 

565 reg = re.compile(regve) 

566 res = reg.search(name) 

567 if res is not None: 

568 if i == 6: 

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

570 else: 

571 return res.groups()[0] 

572 raise MissingVersionWheelException( 

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

574 

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

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

577 """ 

578 Downloads the module without installation. 

579 

580 @param temp_folder destination 

581 @param force force the installation even if already installed 

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

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

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

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

586 see @see me get_exewheel_url_link2 

587 @return downloaded files 

588 

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

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

591 """ 

592 disable_options = {} 

593 if source is None: 

594 source = self.source 

595 kind = self.kind 

596 

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

598 

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

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

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

602 pp = get_pip_program() 

603 if self.pipgit: 

604 br = "@" + \ 

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

606 link = ModuleInstall.github_pattern_git.format( 

607 self.name, self.gitrepo, br) 

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

609 else: 

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

611 if self.version is not None: 

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

613 if " " in temp_folder: 

614 raise FileNotFoundError( 

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

616 if deps: 

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

618 else: 

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

620 

621 if self.index_url is not None: 

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

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

624 self.index_url, slash) 

625 parsed_uri = urlsplit(self.index_url) 

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

627 if self.pip_options is not None: 

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

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

630 

631 out, err = run_cmd( 

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

633 if "Successfully downloaded" not in out: 

634 raise DownloadError( 

635 "unable to download with pip " + 

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

637 cmd + "\nOUT:\n" + 

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

639 err) 

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

641 for line in lines: 

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

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

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

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

646 raise FileNotFoundError( 

647 "unable to find downloaded file " + 

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

649 cmd + "\nOUT:\n" + 

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

651 err) 

652 

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

654 if source is not None: 

655 kind = "wheel2" 

656 ver = python_version() 

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

658 # nothing to download, you should use pip 

659 return None 

660 else: 

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

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

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

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

665 *sys.version_info[0:2]) 

666 elif kind == "wheel": 

667 url, whl = self.get_exewheel_url_link( 

668 file_save=file_save, wheel=True) 

669 else: 

670 url, whl = self.get_exewheel_url_link2( 

671 file_save=file_save, wheel=True, source=source) 

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

673 

674 exi = os.path.exists(whlname) 

675 if force or not exi: 

676 

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

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

679 if self.existing_version is None: 

680 self.existing_version = self.extract_version(whl) 

681 req = urllib_request.Request( 

682 url, headers={ 

683 'User-agent': default_user_agent}) 

684 try: 

685 u = urllib_request.urlopen(req) 

686 text = u.read() 

687 u.close() 

688 except urllib_error.HTTPError as e: 

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

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

691 

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

693 os.makedirs(temp_folder) 

694 

695 if len(text) <= 1000: 

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

697 len(text), url, default_user_agent)) 

698 

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

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

701 f.write(text) 

702 

703 return whlname 

704 

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

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

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

708 zipurl = ModuleInstall.github_pattern_zip.format( 

709 self.name, self.gitrepo, self.branch) 

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

711 try: 

712 req = urllib_request.Request( 

713 zipurl, headers={ 

714 'User-agent': default_user_agent}) 

715 u = urllib_request.urlopen(req) 

716 text = u.read() 

717 u.close() 

718 except urllib_error.HTTPError as e: 

719 raise Exception( 

720 "unable to get archive from: " + 

721 zipurl) from e 

722 

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

724 os.makedirs(temp_folder) 

725 u = open(outfile, "wb") 

726 u.write(text) 

727 u.close() 

728 

729 if unzipFile: 

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

731 files = self.unzipfiles(outfile, temp_folder) 

732 return files 

733 else: 

734 return outfile 

735 

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

737 if source is not None: 

738 kind = "exe2" 

739 ver = python_version() 

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

741 raise Exception( 

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

743 url, exe = self.get_exewheel_url_link( 

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

745 file_save=file_save, source=source) 

746 

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

748 req = urllib_request.Request( 

749 url, headers={ 

750 'User-agent': default_user_agent}) 

751 u = urllib_request.urlopen(req) 

752 text = u.read() 

753 u.close() 

754 

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

756 os.makedirs(temp_folder) 

757 

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

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

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

761 f.write(text) 

762 return exename 

763 

764 raise ImportError( 

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

766 kind, 

767 self.name)) 

768 

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

770 """ 

771 returns the version of a package on pypi 

772 

773 @param url pipy server 

774 @return version 

775 

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

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

778 """ 

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

780 # we use a function which caches the result 

781 return get_pypi_version(self.name) 

782 else: 

783 pypi = xmlrpc_client.ServerProxy(url) 

784 available = pypi.package_releases(self.name) 

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

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

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

788 available = pypi.package_releases(self.mname) 

789 

790 if available is None: 

791 raise MissingPackageOnPyPiException( 

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

793 

794 return available[0] 

795 

796 def get_pypi_numeric_version(self): 

797 """ 

798 returns the version of a package in pypi 

799 

800 @return tuple 

801 """ 

802 vers = self.get_pypi_version() 

803 if vers is None: 

804 return None 

805 if isinstance(vers, list): 

806 v = self.get_pypi_version() 

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

808 return numeric_version(vers) 

809 

810 def get_installed_version(self): 

811 """ 

812 return the version of the installed package 

813 

814 @return version 

815 """ 

816 vers = get_module_version(None) 

817 if self.name in vers: 

818 return vers[self.name] 

819 cap = self.name.capitalize() 

820 if cap in vers: 

821 return vers[cap] 

822 cap = self.name.lower() 

823 if cap in vers: 

824 return vers[cap] 

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

826 if cap in vers: 

827 return vers[cap] 

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

829 if cap in vers: 

830 return vers[cap] 

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

832 if cap in vers: 

833 return vers[cap] 

834 if self.mname is not None: 

835 if self.mname in vers: 

836 return vers[self.mname] 

837 cap = self.mname.lower() 

838 if cap in vers: 

839 return vers[cap] 

840 return None 

841 

842 def get_installed_metadata(self): 

843 """ 

844 return the metadata of the installed package 

845 

846 @return dictionary 

847 """ 

848 r = get_module_metadata(self.name) 

849 if r is None: 

850 return get_module_metadata(self.mname) 

851 else: 

852 return r 

853 

854 def get_installed_license(self): 

855 """ 

856 return the license of the installed package 

857 

858 @return string 

859 """ 

860 meta = self.get_installed_metadata() 

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

862 res = None 

863 else: 

864 res = None 

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

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

867 res = v 

868 break 

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

870 if res is not None: 

871 if isinstance(res, list): 

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

873 if len(res) > 0: 

874 res = res[0] 

875 else: 

876 res = None 

877 if res in adm: 

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

879 return res 

880 

881 def get_installed_classifier(self): 

882 """ 

883 return the classifier of the installed package 

884 

885 @return string 

886 """ 

887 meta = self.get_installed_metadata() 

888 if meta is None: 

889 return None 

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

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

892 return v 

893 return None 

894 

895 def is_installed_version(self): 

896 """ 

897 tells if a module is installed 

898 

899 @return boolean 

900 """ 

901 return self.get_installed_version() is not None 

902 

903 def get_installed_numeric_version(self): 

904 """ 

905 returns the version as number (not string) 

906 

907 @return tuple 

908 """ 

909 vers = self.get_installed_version() 

910 if vers is None: 

911 return None 

912 return numeric_version(vers) 

913 

914 def has_update(self): 

915 """ 

916 tells if the package has a newer version on pipy 

917 

918 @return boolean 

919 """ 

920 if ModuleInstall.is_annoying(self.name): 

921 return False 

922 vers = self.get_installed_numeric_version() 

923 if self.version is None: 

924 pypi = self.get_pypi_numeric_version() 

925 return compare_version(pypi, vers) > 0 

926 else: 

927 num = numeric_version(self.version) 

928 return compare_version(num, vers) > 0 

929 

930 def _check_installation(self): 

931 """ 

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

933 we try to track that 

934 """ 

935 try: 

936 import numpy 

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

938 raise InstallError( 

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

940 except ImportError: 

941 # not installed 

942 pass 

943 return True 

944 

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

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

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

948 """ 

949 Installs the package. 

950 

951 @param force_kind overwrite self.kind 

952 @param force force the installation even if already installed 

953 @param temp_folder folder where to download the setup 

954 @param log display logs or not 

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

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

957 @param source overwrite the source of the wheels, 

958 see @see me get_exewheel_url_link2 

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

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

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

962 @return boolean 

963 

964 The options mentioned in parameter ``options`` 

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

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

967 installing a module from github. 

968 

969 .. versionchanged:: 1.0 

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

971 

972 .. versionchanged:: 1.1 

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

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

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

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

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

978 Parameter *post* was added. 

979 Parameter *out_streas* added. 

980 """ 

981 if source is None: 

982 source = self.source 

983 if post is None: 

984 post = self.post_installation 

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

986 try: 

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

988 log=log, options=options, deps=deps) 

989 except InstallError as e: 

990 warnings.warn(str(e)) 

991 # we try the regular way now 

992 

993 if not force and self.is_installed_version(): 

994 return True 

995 

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

997 

998 if options is None: 

999 options = self.pip_options 

1000 if options is None: 

1001 options = [] 

1002 

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

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

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

1006 ret = None 

1007 custom = custom or self.custom 

1008 

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

1010 if custom is not None: 

1011 raise NotImplementedError( 

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

1013 pp = get_pip_program() 

1014 if self.pipgit: 

1015 br = "@" + \ 

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

1017 link = ModuleInstall.github_pattern_git.format( 

1018 self.name, self.gitrepo, br) 

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

1020 else: 

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

1022 if self.version is not None: 

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

1024 if len(options) > 0: 

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

1026 

1027 if not deps: 

1028 cmd += ' --no-deps' 

1029 

1030 if self.index_url is not None: 

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

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

1033 self.index_url, slash) 

1034 else: 

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

1036 

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

1038 memo = sys.argv 

1039 sys.argv = [] 

1040 out, err = run_cmd( 

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

1042 if out_streams is not None: 

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

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

1045 sys.argv = memo 

1046 

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

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

1049 

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

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

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

1053 raise InstallError(mes) 

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

1055 ret = True 

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

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

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

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

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

1061 raise InstallError(mes) 

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

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

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

1065 raise InstallError(mes) 

1066 else: 

1067 ret = not uptodate 

1068 

1069 elif kind == "conda": 

1070 if custom is not None: 

1071 raise NotImplementedError( 

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

1073 if "--upgrade" in options: 

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

1075 command = "update" 

1076 else: 

1077 command = "install" 

1078 pp = get_conda_program() 

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

1080 if self.version is not None: 

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

1082 if len(options) > 0: 

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

1084 

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

1086 if not deps: 

1087 cmd += ' --no-deps' 

1088 

1089 out, err = run_cmd( 

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

1091 if out_streams is not None: 

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

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

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

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

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

1097 raise InstallError(mes) 

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

1099 ret = True 

1100 elif "Successfully installed" not in out: 

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

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

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

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

1105 raise InstallError(mes) 

1106 if "Requirement already satisfied" not in out: 

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

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

1109 raise InstallError(mes) 

1110 else: 

1111 ret = True 

1112 

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

1114 if custom is not None: 

1115 raise NotImplementedError( 

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

1117 ver = python_version() 

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

1119 ret = self.install("pip") 

1120 whlname = self.name 

1121 ret = True 

1122 else: 

1123 whlname = self.download( 

1124 temp_folder=temp_folder, 

1125 force=force, 

1126 unzipFile=True, 

1127 source=source) 

1128 vers = self.get_installed_numeric_version() 

1129 ret = True 

1130 if vers is not None: 

1131 whlvers = numeric_version(get_wheel_version(whlname)) 

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

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

1134 whlvers, vers, whlname)) 

1135 ret = False 

1136 if ret: 

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

1138 

1139 pip = get_pip_program() 

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

1141 if self.version is not None: 

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

1143 if len(options) > 0: 

1144 opts = list(options) 

1145 if len(opts): 

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

1147 if not deps: 

1148 cmd += ' --no-deps' 

1149 

1150 out, err = run_cmd( 

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

1152 if out_streams is not None: 

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

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

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

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

1157 raise InstallError(mes) 

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

1159 ret = True 

1160 elif "Successfully installed" not in out: 

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

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

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

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

1165 raise InstallError(mes) 

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

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

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

1169 raise InstallError(mes) 

1170 else: 

1171 ret = True 

1172 

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

1174 # the following code requires admin rights 

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

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

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

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

1179 # cmd = pip + " install -e 

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

1181 # self.name) 

1182 

1183 files = self.download(temp_folder=temp_folder, 

1184 force=force, unzipFile=True) 

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

1186 if len(setu) == 0: 

1187 raise Exception( 

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

1189 self.name) 

1190 if len(setu) > 1: 

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

1192 setu.sort() 

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

1194 raise InstallError( 

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

1196 self.name + 

1197 "\n" + 

1198 "\n".join( 

1199 str(_) for _ in setu)) 

1200 self.fLOG( 

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

1202 setu = [setu[0][1]] 

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

1204 

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

1206 cwd = os.getcwd() 

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

1208 if custom is None: 

1209 custom = ["install"] 

1210 cmds = [] 

1211 for command in custom: 

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

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

1214 command) 

1215 cmds.append(cmd1) 

1216 

1217 def enumerate_filtered_option(options): 

1218 for o in options: 

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

1220 yield o 

1221 

1222 filter_options = list(enumerate_filtered_option(options)) 

1223 if len(filter_options) > 0: 

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

1225 

1226 if deps: 

1227 # it will not work 

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

1229 pass 

1230 

1231 outs = "" 

1232 errs = "" 

1233 for cmd in cmds: 

1234 out, err = run_cmd( 

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

1236 if out_streams is not None: 

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

1238 if len(outs) > 0: 

1239 outs += "\n" 

1240 if len(errs) > 0: 

1241 errs += "\n" 

1242 outs += out 

1243 errs += err 

1244 os.chdir(cwd) 

1245 

1246 out, err = outs, errs 

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

1248 if "Finished processing dependencies" not in out: 

1249 raise InstallError( 

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

1251 str(self) + 

1252 "\nOUT:\n" + 

1253 out + 

1254 "\nERR-X:\n" + 

1255 err) 

1256 self.fLOG( 

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

1258 if "Permission denied" in out: 

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

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

1261 ret = True 

1262 

1263 elif kind == "exe": 

1264 if custom is not None: 

1265 raise NotImplementedError( 

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

1267 ver = python_version() 

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

1269 ret = self.install("pip") 

1270 else: 

1271 exename = self.download( 

1272 temp_folder=temp_folder, 

1273 force=force, 

1274 unzipFile=True, 

1275 source=source) 

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

1277 out, err = run_cmd( 

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

1279 if out_streams is not None: 

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

1281 ret = len(err) == 0 

1282 

1283 elif kind == "exe2": 

1284 if custom is not None: 

1285 raise NotImplementedError( 

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

1287 ver = python_version() 

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

1289 ret = self.install("pip") 

1290 else: 

1291 exename = self.download( 

1292 temp_folder=temp_folder, 

1293 force=force, 

1294 unzipFile=True, 

1295 source=source) 

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

1297 out, err = run_cmd( 

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

1299 if out_streams is not None: 

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

1301 ret = len(err) == 0 

1302 else: 

1303 raise ImportError( 

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

1305 kind, 

1306 self.name)) 

1307 

1308 if ret is not None and ret: 

1309 self._check_installation() 

1310 

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

1312 # takes longer than expected 

1313 # if not self.is_installed_version() : 

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

1315 

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

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

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

1319 ti = 0 

1320 while not self.is_installed_local(): 

1321 time.sleep(0.5) 

1322 ti += 0.5 

1323 if ti > 60: 

1324 self.fLOG( 

1325 "wait for the end of execution of ", 

1326 self.name) 

1327 ti = 0 

1328 

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

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

1331 full = f.read() 

1332 

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

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

1335 if cmd not in full: 

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

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

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

1339 f.write( 

1340 full.replace( 

1341 "@echo off", 

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

1343 else: 

1344 f.write(cmd) 

1345 f.write("\n") 

1346 f.write(full) 

1347 

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

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

1350 ret = self.run_post_installation(post) 

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

1352 

1353 if ret is not None and ret: 

1354 # we check the module was properly installed 

1355 if not self.is_installed_local_cmd(): 

1356 raise InstallError( 

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

1358 

1359 return ret 

1360 

1361 def run_post_installation(self, post): 

1362 """ 

1363 Run instructions post installation 

1364 

1365 @param post dictionary, instructions post installation 

1366 @return boolean 

1367 

1368 Example: 

1369 

1370 :: 

1371 

1372 post = dict( 

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

1374 

1375 .. versionadded:: 1.1 

1376 """ 

1377 if not isinstance(post, dict): 

1378 raise TypeError("expecting a dictionary") 

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

1380 if k == "cmd_python": 

1381 # processed just above 

1382 continue 

1383 if k == "pre_cmd": 

1384 if v == "module_install_preprocess": 

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

1386 self.module_install_preprocess(post) 

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

1388 else: 

1389 raise ValueError( 

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

1391 else: 

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

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

1394 if k == "cmd_python": 

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

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

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

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

1399 raise InstallError( 

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

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

1402 elif k == "pre_cmd": 

1403 # processed just above 

1404 continue 

1405 else: 

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

1407 return True 

1408 

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

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

1411 """ 

1412 Updates the package if necessary, we use 

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

1414 

1415 @param force_kind overwrite self.kind 

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

1417 @param temp_folder folder where to download the setup 

1418 @param log display logs or not 

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

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

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

1422 @return boolean 

1423 

1424 The options mentioned in parameter ``options`` 

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

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

1427 installing a module from github. 

1428 

1429 .. versionchanged:: 1.1 

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

1431 """ 

1432 if source is None: 

1433 source = self.source 

1434 if ModuleInstall.is_annoying(self.name): 

1435 return False 

1436 

1437 if not self.is_installed_version(): 

1438 raise MissingInstalledPackageException(self.name) 

1439 

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

1441 return True 

1442 

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

1444 

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

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

1447 if opt not in options: 

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

1449 options.append(opt) 

1450 

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

1452 temp_folder=temp_folder, log=log, options=options, source=source) 

1453 return res 

1454 

1455 def module_install_preprocess(self, post): 

1456 """ 

1457 Run some preprocessing for specific modules 

1458 

1459 @param post dictionary 

1460 """ 

1461 if self.name == "pywin32": 

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

1463 if "cmd_python" not in post: 

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

1465 cmd_python = post["cmd_python"] 

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

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

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

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

1470 raise FileNotFoundError(name) 

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

1472 content = f.read() 

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

1474 if repby not in content: 

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

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

1477 content = content.replace( 

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

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

1480 f.write(content) 

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

1482 else: 

1483 self.fLOG( 

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

1485 else: 

1486 raise ValueError( 

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