|
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.6", "64"): BASE_URL + "2.6.6/python-2.6.6.amd64.msi", |
|
22
|
|
|
("2.6", "32"): BASE_URL + "2.6.6/python-2.6.6.msi", |
|
23
|
|
|
("2.7", "64"): BASE_URL + "2.7.10/python-2.7.13.amd64.msi", |
|
24
|
|
|
("2.7", "32"): BASE_URL + "2.7.10/python-2.7.13.msi", |
|
25
|
|
|
# NOTE: no .msi installer for 3.3.6 |
|
26
|
|
|
("3.3", "64"): BASE_URL + "3.3.3/python-3.3.5.amd64.msi", |
|
27
|
|
|
("3.3", "32"): BASE_URL + "3.3.3/python-3.3.5.msi", |
|
28
|
|
|
("3.4", "64"): BASE_URL + "3.4.3/python-3.4.6.amd64.msi", |
|
29
|
|
|
("3.4", "32"): BASE_URL + "3.4.3/python-3.4.6.msi", |
|
30
|
|
|
("3.5", "64"): BASE_URL + "3.5.0/python-3.5.3-amd64.exe", |
|
31
|
|
|
("3.5", "32"): BASE_URL + "3.5.0/python-3.5.3.exe", |
|
32
|
|
|
("3.6", "64"): BASE_URL + "3.6.0/python-3.6.0-amd64.exe", |
|
33
|
|
|
("3.6", "32"): BASE_URL + "3.6.0/python-3.6.0.exe", |
|
34
|
|
|
} |
|
35
|
|
|
INSTALL_CMD = { |
|
36
|
|
|
# Commands are allowed to fail only if they are not the last command. Eg: uninstall (/x) allowed to fail. |
|
37
|
|
|
"2.6": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], |
|
38
|
|
|
["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], |
|
39
|
|
|
"2.7": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], |
|
40
|
|
|
["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], |
|
41
|
|
|
"3.3": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], |
|
42
|
|
|
["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], |
|
43
|
|
|
"3.4": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], |
|
44
|
|
|
["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], |
|
45
|
|
|
"3.5": [["{path}", "/quiet", "TargetDir={home}"]], |
|
46
|
|
|
"3.6": [["{path}", "/quiet", "TargetDir={home}"]], |
|
47
|
|
|
} |
|
48
|
|
|
|
|
49
|
|
|
|
|
50
|
|
|
def download_file(url, path): |
|
51
|
|
|
print("Downloading: {} (into {})".format(url, path)) |
|
52
|
|
|
progress = [0, 0] |
|
53
|
|
|
|
|
54
|
|
|
def report(count, size, total): |
|
55
|
|
|
progress[0] = count * size |
|
56
|
|
|
if progress[0] - progress[1] > 1000000: |
|
57
|
|
|
progress[1] = progress[0] |
|
58
|
|
|
print("Downloaded {:,}/{:,} ...".format(progress[1], total)) |
|
59
|
|
|
|
|
60
|
|
|
dest, _ = urlretrieve(url, path, reporthook=report) |
|
61
|
|
|
return dest |
|
62
|
|
|
|
|
63
|
|
|
|
|
64
|
|
|
def install_python(version, arch, home): |
|
65
|
|
|
print("Installing Python", version, "for", arch, "bit architecture to", home) |
|
66
|
|
|
if exists(home): |
|
67
|
|
|
return |
|
68
|
|
|
|
|
69
|
|
|
path = download_python(version, arch) |
|
70
|
|
|
print("Installing", path, "to", home) |
|
71
|
|
|
success = False |
|
72
|
|
|
for cmd in INSTALL_CMD[version]: |
|
73
|
|
|
cmd = [part.format(home=home, path=path) for part in cmd] |
|
74
|
|
|
print("Running:", " ".join(cmd)) |
|
75
|
|
|
try: |
|
76
|
|
|
check_call(cmd) |
|
77
|
|
|
except Exception as exc: |
|
78
|
|
|
print("Failed command", cmd, "with:", exc) |
|
79
|
|
|
if exists("install.log"): |
|
80
|
|
|
with open("install.log") as fh: |
|
81
|
|
|
print(fh.read()) |
|
82
|
|
|
else: |
|
83
|
|
|
success = True |
|
84
|
|
|
if success: |
|
85
|
|
|
print("Installation complete!") |
|
86
|
|
|
else: |
|
87
|
|
|
print("Installation failed") |
|
88
|
|
|
|
|
89
|
|
|
|
|
90
|
|
|
def download_python(version, arch): |
|
91
|
|
|
for _ in range(3): |
|
92
|
|
|
try: |
|
93
|
|
|
return download_file(URLS[version, arch], "installer.exe") |
|
94
|
|
|
except Exception as exc: |
|
95
|
|
|
print("Failed to download:", exc) |
|
96
|
|
|
print("Retrying ...") |
|
97
|
|
|
|
|
98
|
|
|
|
|
99
|
|
|
def install_pip(home): |
|
100
|
|
|
pip_path = home + "/Scripts/pip.exe" |
|
101
|
|
|
python_path = home + "/python.exe" |
|
102
|
|
|
if exists(pip_path): |
|
103
|
|
|
print("pip already installed.") |
|
104
|
|
|
else: |
|
105
|
|
|
print("Installing pip...") |
|
106
|
|
|
download_file(GET_PIP_URL, GET_PIP_PATH) |
|
107
|
|
|
print("Executing:", python_path, GET_PIP_PATH) |
|
108
|
|
|
check_call([python_path, GET_PIP_PATH]) |
|
109
|
|
|
|
|
110
|
|
|
|
|
111
|
|
|
def install_packages(home, *packages): |
|
112
|
|
|
cmd = [home + "/Scripts/pip.exe", "install"] |
|
113
|
|
|
cmd.extend(packages) |
|
114
|
|
|
check_call(cmd) |
|
115
|
|
|
|
|
116
|
|
|
|
|
117
|
|
|
if __name__ == "__main__": |
|
118
|
|
|
install_python(environ['PYTHON_VERSION'], environ['PYTHON_ARCH'], environ['PYTHON_HOME']) |
|
119
|
|
|
install_pip(environ['PYTHON_HOME']) |
|
120
|
|
|
install_packages(environ['PYTHON_HOME'], "setuptools>=18.0.1", "wheel", "tox", "virtualenv>=13.1.0") |
|
121
|
|
|
|