|
| 1 | +""" |
| 2 | +AppVeyor will at least have few Pythons around so there's no point of implementing a bootstrapper in PowerShell. |
| 3 | +
|
| 4 | +This is a port of https://github.com/pypa/python-packaging-user-guide/blob/master/source/code/install.ps1 |
| 5 | +with various fixes and improvements that just weren't feasible to implement in PowerShell. |
| 6 | +""" |
| 7 | +from __future__ import print_function |
| 8 | +from os import environ |
| 9 | +from os.path import exists |
| 10 | +from subprocess import check_call |
| 11 | + |
| 12 | +try: |
| 13 | + from urllib.request import urlretrieve |
| 14 | +except ImportError: |
| 15 | + from urllib import urlretrieve |
| 16 | + |
| 17 | +BASE_URL = "https://www.python.org/ftp/python/" |
| 18 | +GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" |
| 19 | +GET_PIP_PATH = "C:\get-pip.py" |
| 20 | +URLS = { |
| 21 | + ("2.7", "64"): BASE_URL + "2.7.10/python-2.7.10.amd64.msi", |
| 22 | + ("2.7", "32"): BASE_URL + "2.7.10/python-2.7.10.msi", |
| 23 | + ("3.3", "64"): BASE_URL + "3.3.6/python-3.3.6.amd64.msi", |
| 24 | + ("3.3", "32"): BASE_URL + "3.3.6/python-3.3.6.msi", |
| 25 | + ("3.4", "64"): BASE_URL + "3.4.3/python-3.4.3.amd64.msi", |
| 26 | + ("3.4", "32"): BASE_URL + "3.4.3/python-3.4.3.msi", |
| 27 | + ("3.5", "64"): BASE_URL + "3.5.0/python-3.5.0-amd64.exe", |
| 28 | + ("3.5", "32"): BASE_URL + "3.5.0/python-3.5.0.exe", |
| 29 | +} |
| 30 | +INSTALL_CMD = { |
| 31 | + "2.7": ["msiexec.exe", "/qn", "/i", "{path}", "TARGETDIR={home}"], |
| 32 | + "3.3": ["msiexec.exe", "/qn", "/i", "{path}", "TARGETDIR={home}"], |
| 33 | + "3.4": ["msiexec.exe", "/qn", "/i", "{path}", "TARGETDIR={home}"], |
| 34 | + "3.5": ["{path}", "/quiet", "TargetDir={home}"], |
| 35 | +} |
| 36 | + |
| 37 | + |
| 38 | +def download_file(url, path): |
| 39 | + progress = [0, 0] |
| 40 | + |
| 41 | + def report(count, size, total): |
| 42 | + progress[0] = count * size |
| 43 | + if progress[0] - progress[1] > 1000000: |
| 44 | + progress[1] = progress[0] |
| 45 | + print("Downloaded {:,}/{:,} ...".format(progress[1], total)) |
| 46 | + |
| 47 | + dest, _ = urlretrieve(url, path, reporthook=report) |
| 48 | + return dest |
| 49 | + |
| 50 | + |
| 51 | +def install_python(version, arch, home): |
| 52 | + print("Installing Python", version, "for", arch, "bit architecture to", home) |
| 53 | + if exists(home): |
| 54 | + return |
| 55 | + |
| 56 | + path = download_python(version, arch) |
| 57 | + print("Installing", path, "to", home) |
| 58 | + cmd = [part.format(home=home, path=path) for part in INSTALL_CMD[version]] |
| 59 | + print("Running:", " ".join(cmd)) |
| 60 | + check_call(cmd) |
| 61 | + print("Installation complete!") |
| 62 | + |
| 63 | + |
| 64 | +def download_python(version, arch): |
| 65 | + for _ in range(3): |
| 66 | + try: |
| 67 | + return download_file(URLS[version, arch], "installer.exe") |
| 68 | + except Exception as exc: |
| 69 | + print("Failed to download:", exc) |
| 70 | + print("Retrying ...") |
| 71 | + |
| 72 | + |
| 73 | +def install_pip(home): |
| 74 | + pip_path = home + "/Scripts/pip.exe" |
| 75 | + python_path = home + "/python.exe" |
| 76 | + if exists(pip_path): |
| 77 | + print("pip already installed.") |
| 78 | + else: |
| 79 | + print("Installing pip...") |
| 80 | + download_file(GET_PIP_URL, GET_PIP_PATH) |
| 81 | + print("Executing:", python_path, GET_PIP_PATH) |
| 82 | + check_call([python_path, GET_PIP_PATH]) |
| 83 | + |
| 84 | + |
| 85 | +def install_packages(home, *packages): |
| 86 | + cmd = [home + "/Scripts/pip.exe", "install"] |
| 87 | + cmd.extend(packages) |
| 88 | + check_call(cmd) |
| 89 | + |
| 90 | + |
| 91 | +if __name__ == "__main__": |
| 92 | + install_python(environ['PYTHON_VERSION'], environ['PYTHON_ARCH'], environ['PYTHON_HOME']) |
| 93 | + install_pip(environ['PYTHON_HOME']) |
| 94 | + install_packages(environ['PYTHON_HOME'], "setuptools>=18.0.1", "wheel", "tox", "virtualenv>=13.1.0") |
0 commit comments