init
This commit is contained in:
commit
38355d2442
9083 changed files with 1225834 additions and 0 deletions
|
|
@ -0,0 +1,31 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from ..py_info import PythonInfo
|
||||
from ..py_spec import PythonSpec
|
||||
from .pep514 import discover_pythons
|
||||
|
||||
|
||||
class Pep514PythonInfo(PythonInfo):
|
||||
""" """
|
||||
|
||||
|
||||
def propose_interpreters(spec, cache_dir, env):
|
||||
# see if PEP-514 entries are good
|
||||
|
||||
# start with higher python versions in an effort to use the latest version available
|
||||
# and prefer PythonCore over conda pythons (as virtualenv is mostly used by non conda tools)
|
||||
existing = list(discover_pythons())
|
||||
existing.sort(
|
||||
key=lambda i: tuple(-1 if j is None else j for j in i[1:4]) + (1 if i[0] == "PythonCore" else 0,), reverse=True
|
||||
)
|
||||
|
||||
for name, major, minor, arch, exe, _ in existing:
|
||||
# pre-filter
|
||||
if name in ("PythonCore", "ContinuumAnalytics"):
|
||||
name = "CPython"
|
||||
registry_spec = PythonSpec(None, name, major, minor, None, arch, exe)
|
||||
if registry_spec.satisfies(spec):
|
||||
interpreter = Pep514PythonInfo.from_exe(exe, cache_dir, env=env, raise_on_error=False)
|
||||
if interpreter is not None:
|
||||
if interpreter.satisfies(spec, impl_must_match=True):
|
||||
yield interpreter
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,161 @@
|
|||
"""Implement https://www.python.org/dev/peps/pep-0514/ to discover interpreters - Windows only"""
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
import os
|
||||
import re
|
||||
from logging import basicConfig, getLogger
|
||||
|
||||
import six
|
||||
|
||||
if six.PY3:
|
||||
import winreg
|
||||
else:
|
||||
# noinspection PyUnresolvedReferences
|
||||
import _winreg as winreg
|
||||
|
||||
LOGGER = getLogger(__name__)
|
||||
|
||||
|
||||
def enum_keys(key):
|
||||
at = 0
|
||||
while True:
|
||||
try:
|
||||
yield winreg.EnumKey(key, at)
|
||||
except OSError:
|
||||
break
|
||||
at += 1
|
||||
|
||||
|
||||
def get_value(key, value_name):
|
||||
try:
|
||||
return winreg.QueryValueEx(key, value_name)[0]
|
||||
except OSError:
|
||||
return None
|
||||
|
||||
|
||||
def discover_pythons():
|
||||
for hive, hive_name, key, flags, default_arch in [
|
||||
(winreg.HKEY_CURRENT_USER, "HKEY_CURRENT_USER", r"Software\Python", 0, 64),
|
||||
(winreg.HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE", r"Software\Python", winreg.KEY_WOW64_64KEY, 64),
|
||||
(winreg.HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE", r"Software\Python", winreg.KEY_WOW64_32KEY, 32),
|
||||
]:
|
||||
for spec in process_set(hive, hive_name, key, flags, default_arch):
|
||||
yield spec
|
||||
|
||||
|
||||
def process_set(hive, hive_name, key, flags, default_arch):
|
||||
try:
|
||||
with winreg.OpenKeyEx(hive, key, 0, winreg.KEY_READ | flags) as root_key:
|
||||
for company in enum_keys(root_key):
|
||||
if company == "PyLauncher": # reserved
|
||||
continue
|
||||
for spec in process_company(hive_name, company, root_key, default_arch):
|
||||
yield spec
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def process_company(hive_name, company, root_key, default_arch):
|
||||
with winreg.OpenKeyEx(root_key, company) as company_key:
|
||||
for tag in enum_keys(company_key):
|
||||
spec = process_tag(hive_name, company, company_key, tag, default_arch)
|
||||
if spec is not None:
|
||||
yield spec
|
||||
|
||||
|
||||
def process_tag(hive_name, company, company_key, tag, default_arch):
|
||||
with winreg.OpenKeyEx(company_key, tag) as tag_key:
|
||||
version = load_version_data(hive_name, company, tag, tag_key)
|
||||
if version is not None: # if failed to get version bail
|
||||
major, minor, _ = version
|
||||
arch = load_arch_data(hive_name, company, tag, tag_key, default_arch)
|
||||
if arch is not None:
|
||||
exe_data = load_exe(hive_name, company, company_key, tag)
|
||||
if exe_data is not None:
|
||||
exe, args = exe_data
|
||||
return company, major, minor, arch, exe, args
|
||||
|
||||
|
||||
def load_exe(hive_name, company, company_key, tag):
|
||||
key_path = "{}/{}/{}".format(hive_name, company, tag)
|
||||
try:
|
||||
with winreg.OpenKeyEx(company_key, r"{}\InstallPath".format(tag)) as ip_key:
|
||||
with ip_key:
|
||||
exe = get_value(ip_key, "ExecutablePath")
|
||||
if exe is None:
|
||||
ip = get_value(ip_key, None)
|
||||
if ip is None:
|
||||
msg(key_path, "no ExecutablePath or default for it")
|
||||
|
||||
else:
|
||||
exe = os.path.join(ip, str("python.exe"))
|
||||
if exe is not None and os.path.exists(exe):
|
||||
args = get_value(ip_key, "ExecutableArguments")
|
||||
return exe, args
|
||||
else:
|
||||
msg(key_path, "could not load exe with value {}".format(exe))
|
||||
except OSError:
|
||||
msg("{}/{}".format(key_path, "InstallPath"), "missing")
|
||||
return None
|
||||
|
||||
|
||||
def load_arch_data(hive_name, company, tag, tag_key, default_arch):
|
||||
arch_str = get_value(tag_key, "SysArchitecture")
|
||||
if arch_str is not None:
|
||||
key_path = "{}/{}/{}/SysArchitecture".format(hive_name, company, tag)
|
||||
try:
|
||||
return parse_arch(arch_str)
|
||||
except ValueError as sys_arch:
|
||||
msg(key_path, sys_arch)
|
||||
return default_arch
|
||||
|
||||
|
||||
def parse_arch(arch_str):
|
||||
if isinstance(arch_str, six.string_types):
|
||||
match = re.match(r"^(\d+)bit$", arch_str)
|
||||
if match:
|
||||
return int(next(iter(match.groups())))
|
||||
error = "invalid format {}".format(arch_str)
|
||||
else:
|
||||
error = "arch is not string: {}".format(repr(arch_str))
|
||||
raise ValueError(error)
|
||||
|
||||
|
||||
def load_version_data(hive_name, company, tag, tag_key):
|
||||
for candidate, key_path in [
|
||||
(get_value(tag_key, "SysVersion"), "{}/{}/{}/SysVersion".format(hive_name, company, tag)),
|
||||
(tag, "{}/{}/{}".format(hive_name, company, tag)),
|
||||
]:
|
||||
if candidate is not None:
|
||||
try:
|
||||
return parse_version(candidate)
|
||||
except ValueError as sys_version:
|
||||
msg(key_path, sys_version)
|
||||
return None
|
||||
|
||||
|
||||
def parse_version(version_str):
|
||||
if isinstance(version_str, six.string_types):
|
||||
match = re.match(r"^(\d+)(?:\.(\d+))?(?:\.(\d+))?$", version_str)
|
||||
if match:
|
||||
return tuple(int(i) if i is not None else None for i in match.groups())
|
||||
error = "invalid format {}".format(version_str)
|
||||
else:
|
||||
error = "version is not string: {}".format(repr(version_str))
|
||||
raise ValueError(error)
|
||||
|
||||
|
||||
def msg(path, what):
|
||||
LOGGER.warning("PEP-514 violation in Windows Registry at {} error: {}".format(path, what))
|
||||
|
||||
|
||||
def _run():
|
||||
basicConfig()
|
||||
interpreters = []
|
||||
for spec in discover_pythons():
|
||||
interpreters.append(repr(spec))
|
||||
print("\n".join(sorted(interpreters)))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
_run()
|
||||
Loading…
Add table
Add a link
Reference in a new issue