comparison env/lib/python3.7/site-packages/virtualenv/discovery/windows/pep514.py @ 0:26e78fe6e8c4 draft

"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author shellac
date Sat, 02 May 2020 07:14:21 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:26e78fe6e8c4
1 """Implement https://www.python.org/dev/peps/pep-0514/ to discover interpreters - Windows only"""
2 from __future__ import absolute_import, print_function, unicode_literals
3
4 import os
5 import re
6 from logging import basicConfig, getLogger
7
8 import six
9
10 if six.PY3:
11 import winreg
12 else:
13 # noinspection PyUnresolvedReferences
14 import _winreg as winreg
15
16 LOGGER = getLogger(__name__)
17
18
19 def enum_keys(key):
20 at = 0
21 while True:
22 try:
23 yield winreg.EnumKey(key, at)
24 except OSError:
25 break
26 at += 1
27
28
29 def get_value(key, value_name):
30 try:
31 return winreg.QueryValueEx(key, value_name)[0]
32 except OSError:
33 return None
34
35
36 def discover_pythons():
37 for hive, hive_name, key, flags, default_arch in [
38 (winreg.HKEY_CURRENT_USER, "HKEY_CURRENT_USER", r"Software\Python", 0, 64),
39 (winreg.HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE", r"Software\Python", winreg.KEY_WOW64_64KEY, 64),
40 (winreg.HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE", r"Software\Python", winreg.KEY_WOW64_32KEY, 32),
41 ]:
42 for spec in process_set(hive, hive_name, key, flags, default_arch):
43 yield spec
44
45
46 def process_set(hive, hive_name, key, flags, default_arch):
47 try:
48 with winreg.OpenKeyEx(hive, key, 0, winreg.KEY_READ | flags) as root_key:
49 for company in enum_keys(root_key):
50 if company == "PyLauncher": # reserved
51 continue
52 for spec in process_company(hive_name, company, root_key, default_arch):
53 yield spec
54 except OSError:
55 pass
56
57
58 def process_company(hive_name, company, root_key, default_arch):
59 with winreg.OpenKeyEx(root_key, company) as company_key:
60 for tag in enum_keys(company_key):
61 spec = process_tag(hive_name, company, company_key, tag, default_arch)
62 if spec is not None:
63 yield spec
64
65
66 def process_tag(hive_name, company, company_key, tag, default_arch):
67 with winreg.OpenKeyEx(company_key, tag) as tag_key:
68 version = load_version_data(hive_name, company, tag, tag_key)
69 if version is not None: # if failed to get version bail
70 major, minor, _ = version
71 arch = load_arch_data(hive_name, company, tag, tag_key, default_arch)
72 if arch is not None:
73 exe_data = load_exe(hive_name, company, company_key, tag)
74 if exe_data is not None:
75 exe, args = exe_data
76 name = str("python") if company == "PythonCore" else company
77 return name, major, minor, arch, exe, args
78
79
80 def load_exe(hive_name, company, company_key, tag):
81 key_path = "{}/{}/{}".format(hive_name, company, tag)
82 try:
83 with winreg.OpenKeyEx(company_key, r"{}\InstallPath".format(tag)) as ip_key:
84 with ip_key:
85 exe = get_value(ip_key, "ExecutablePath")
86 if exe is None:
87 ip = get_value(ip_key, None)
88 if ip is None:
89 msg(key_path, "no ExecutablePath or default for it")
90
91 else:
92 exe = os.path.join(ip, str("python.exe"))
93 if exe is not None and os.path.exists(exe):
94 args = get_value(ip_key, "ExecutableArguments")
95 return exe, args
96 else:
97 msg(key_path, "exe does not exists {}".format(key_path, exe))
98 except OSError:
99 msg("{}/{}".format(key_path, "InstallPath"), "missing")
100 return None
101
102
103 def load_arch_data(hive_name, company, tag, tag_key, default_arch):
104 arch_str = get_value(tag_key, "SysArchitecture")
105 if arch_str is not None:
106 key_path = "{}/{}/{}/SysArchitecture".format(hive_name, company, tag)
107 try:
108 return parse_arch(arch_str)
109 except ValueError as sys_arch:
110 msg(key_path, sys_arch)
111 return default_arch
112
113
114 def parse_arch(arch_str):
115 if isinstance(arch_str, six.string_types):
116 match = re.match(r"^(\d+)bit$", arch_str)
117 if match:
118 return int(next(iter(match.groups())))
119 error = "invalid format {}".format(arch_str)
120 else:
121 error = "arch is not string: {}".format(repr(arch_str))
122 raise ValueError(error)
123
124
125 def load_version_data(hive_name, company, tag, tag_key):
126 for candidate, key_path in [
127 (get_value(tag_key, "SysVersion"), "{}/{}/{}/SysVersion".format(hive_name, company, tag)),
128 (tag, "{}/{}/{}".format(hive_name, company, tag)),
129 ]:
130 if candidate is not None:
131 try:
132 return parse_version(candidate)
133 except ValueError as sys_version:
134 msg(key_path, sys_version)
135 return None
136
137
138 def parse_version(version_str):
139 if isinstance(version_str, six.string_types):
140 match = re.match(r"^(\d+)(?:\.(\d+))?(?:\.(\d+))?$", version_str)
141 if match:
142 return tuple(int(i) if i is not None else None for i in match.groups())
143 error = "invalid format {}".format(version_str)
144 else:
145 error = "version is not string: {}".format(repr(version_str))
146 raise ValueError(error)
147
148
149 def msg(path, what):
150 LOGGER.warning("PEP-514 violation in Windows Registry at {} error: {}".format(path, what))
151
152
153 def _run():
154 basicConfig()
155 interpreters = []
156 for spec in discover_pythons():
157 interpreters.append(repr(spec))
158 print("\n".join(sorted(interpreters)))
159
160
161 if __name__ == "__main__":
162 _run()