1from __future__ import unicode_literals 2 3import json 4import sys 5 6import tox 7from tox import reporter 8from tox.constants import SITE_PACKAGE_QUERY_SCRIPT, VERSION_QUERY_SCRIPT 9 10 11class Interpreters: 12 def __init__(self, hook): 13 self.name2executable = {} 14 self.executable2info = {} 15 self.hook = hook 16 17 def get_executable(self, envconfig): 18 """ return path object to the executable for the given 19 name (e.g. python2.7, python3.6, python etc.) 20 if name is already an existing path, return name. 21 If an interpreter cannot be found, return None. 22 """ 23 try: 24 return self.name2executable[envconfig.envname] 25 except KeyError: 26 exe = self.hook.tox_get_python_executable(envconfig=envconfig) 27 reporter.verbosity2("{} uses {}".format(envconfig.envname, exe)) 28 self.name2executable[envconfig.envname] = exe 29 return exe 30 31 def get_info(self, envconfig): 32 executable = self.get_executable(envconfig) 33 name = envconfig.basepython 34 if not executable: 35 return NoInterpreterInfo(name=name) 36 try: 37 return self.executable2info[executable] 38 except KeyError: 39 info = run_and_get_interpreter_info(name, executable) 40 self.executable2info[executable] = info 41 return info 42 43 def get_sitepackagesdir(self, info, envdir): 44 if not info.executable: 45 return "" 46 envdir = str(envdir) 47 try: 48 res = exec_on_interpreter(str(info.executable), SITE_PACKAGE_QUERY_SCRIPT, str(envdir)) 49 except ExecFailed as e: 50 reporter.verbosity1("execution failed: {} -- {}".format(e.out, e.err)) 51 return "" 52 else: 53 return res["dir"] 54 55 56def run_and_get_interpreter_info(name, executable): 57 assert executable 58 try: 59 result = exec_on_interpreter(str(executable), VERSION_QUERY_SCRIPT) 60 result["version_info"] = tuple(result["version_info"]) # fix json dump transformation 61 del result["name"] 62 del result["version"] 63 result["executable"] = str(executable) 64 except ExecFailed as e: 65 return NoInterpreterInfo(name, executable=e.executable, out=e.out, err=e.err) 66 else: 67 return InterpreterInfo(name, **result) 68 69 70def exec_on_interpreter(*args): 71 from subprocess import Popen, PIPE 72 73 popen = Popen(args, stdout=PIPE, stderr=PIPE, universal_newlines=True) 74 out, err = popen.communicate() 75 if popen.returncode: 76 raise ExecFailed(args[0], args[1:], out, err) 77 if err: 78 sys.stderr.write(err) 79 try: 80 result = json.loads(out) 81 except Exception: 82 raise ExecFailed(args[0], args[1:], out, "could not decode {!r}".format(out)) 83 return result 84 85 86class ExecFailed(Exception): 87 def __init__(self, executable, source, out, err): 88 self.executable = executable 89 self.source = source 90 self.out = out 91 self.err = err 92 93 94class InterpreterInfo: 95 def __init__(self, name, executable, version_info, sysplatform, is_64): 96 self.name = name 97 self.executable = executable 98 self.version_info = version_info 99 self.sysplatform = sysplatform 100 self.is_64 = is_64 101 102 def __str__(self): 103 return "<executable at {}, version_info {}>".format(self.executable, self.version_info) 104 105 106class NoInterpreterInfo: 107 def __init__(self, name, executable=None, out=None, err="not found"): 108 self.name = name 109 self.executable = executable 110 self.version_info = None 111 self.out = out 112 self.err = err 113 114 def __str__(self): 115 if self.executable: 116 return "<executable at {}, not runnable>".format(self.executable) 117 else: 118 return "<executable not found for: {}>".format(self.name) 119 120 121if tox.INFO.IS_WIN: 122 from .windows import tox_get_python_executable 123else: 124 from .unix import tox_get_python_executable 125assert tox_get_python_executable 126