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