| Viewing file:  builtin.py (5.99 KB)      -rw-r--r-- Select action/file-type:
 
  (+) |  (+) |  (+) | Code (+) | Session (+) |  (+) | SDB (+) |  (+) |  (+) |  (+) |  (+) |  (+) | 
 
import loggingimport os
 import sys
 
 from virtualenv.info import IS_WIN
 
 from .discover import Discover
 from .py_info import PythonInfo
 from .py_spec import PythonSpec
 
 
 class Builtin(Discover):
 def __init__(self, options):
 super().__init__(options)
 self.python_spec = options.python if options.python else [sys.executable]
 self.app_data = options.app_data
 self.try_first_with = options.try_first_with
 
 @classmethod
 def add_parser_arguments(cls, parser):
 parser.add_argument(
 "-p",
 "--python",
 dest="python",
 metavar="py",
 type=str,
 action="append",
 default=[],
 help="interpreter based on what to create environment (path/identifier) "
 "- by default use the interpreter where the tool is installed - first found wins",
 )
 parser.add_argument(
 "--try-first-with",
 dest="try_first_with",
 metavar="py_exe",
 type=str,
 action="append",
 default=[],
 help="try first these interpreters before starting the discovery",
 )
 
 def run(self):
 for python_spec in self.python_spec:
 result = get_interpreter(python_spec, self.try_first_with, self.app_data, self._env)
 if result is not None:
 return result
 return None
 
 def __repr__(self):
 spec = self.python_spec[0] if len(self.python_spec) == 1 else self.python_spec
 return f"{self.__class__.__name__} discover of python_spec={spec!r}"
 
 
 def get_interpreter(key, try_first_with, app_data=None, env=None):
 spec = PythonSpec.from_string_spec(key)
 logging.info("find interpreter for spec %r", spec)
 proposed_paths = set()
 env = os.environ if env is None else env
 for interpreter, impl_must_match in propose_interpreters(spec, try_first_with, app_data, env):
 key = interpreter.system_executable, impl_must_match
 if key in proposed_paths:
 continue
 logging.info("proposed %s", interpreter)
 if interpreter.satisfies(spec, impl_must_match):
 logging.debug("accepted %s", interpreter)
 return interpreter
 proposed_paths.add(key)
 
 
 def propose_interpreters(spec, try_first_with, app_data, env=None):
 # 0. try with first
 env = os.environ if env is None else env
 for py_exe in try_first_with:
 path = os.path.abspath(py_exe)
 try:
 os.lstat(path)  # Windows Store Python does not work with os.path.exists, but does for os.lstat
 except OSError:
 pass
 else:
 yield PythonInfo.from_exe(os.path.abspath(path), app_data, env=env), True
 
 # 1. if it's a path and exists
 if spec.path is not None:
 try:
 os.lstat(spec.path)  # Windows Store Python does not work with os.path.exists, but does for os.lstat
 except OSError:
 if spec.is_abs:
 raise
 else:
 yield PythonInfo.from_exe(os.path.abspath(spec.path), app_data, env=env), True
 if spec.is_abs:
 return
 else:
 # 2. otherwise try with the current
 yield PythonInfo.current_system(app_data), True
 
 # 3. otherwise fallback to platform default logic
 if IS_WIN:
 from .windows import propose_interpreters
 
 for interpreter in propose_interpreters(spec, app_data, env):
 yield interpreter, True
 # finally just find on path, the path order matters (as the candidates are less easy to control by end user)
 paths = get_paths(env)
 tested_exes = set()
 for pos, path in enumerate(paths):
 path_str = str(path)
 logging.debug(LazyPathDump(pos, path_str, env))
 for candidate, match in possible_specs(spec):
 found = check_path(candidate, path_str)
 if found is not None:
 exe = os.path.abspath(found)
 if exe not in tested_exes:
 tested_exes.add(exe)
 interpreter = PathPythonInfo.from_exe(exe, app_data, raise_on_error=False, env=env)
 if interpreter is not None:
 yield interpreter, match
 
 
 def get_paths(env):
 path = env.get("PATH", None)
 if path is None:
 try:
 path = os.confstr("CS_PATH")
 except (AttributeError, ValueError):
 path = os.defpath
 if not path:
 paths = []
 else:
 paths = [p for p in path.split(os.pathsep) if os.path.exists(p)]
 return paths
 
 
 class LazyPathDump:
 def __init__(self, pos, path, env):
 self.pos = pos
 self.path = path
 self.env = env
 
 def __repr__(self):
 content = f"discover PATH[{self.pos}]={self.path}"
 if self.env.get("_VIRTUALENV_DEBUG"):  # this is the over the board debug
 content += " with =>"
 for file_name in os.listdir(self.path):
 try:
 file_path = os.path.join(self.path, file_name)
 if os.path.isdir(file_path) or not os.access(file_path, os.X_OK):
 continue
 except OSError:
 pass
 content += " "
 content += file_name
 return content
 
 
 def check_path(candidate, path):
 _, ext = os.path.splitext(candidate)
 if sys.platform == "win32" and ext != ".exe":
 candidate = candidate + ".exe"
 if os.path.isfile(candidate):
 return candidate
 candidate = os.path.join(path, candidate)
 if os.path.isfile(candidate):
 return candidate
 return None
 
 
 def possible_specs(spec):
 # 4. then maybe it's something exact on PATH - if it was direct lookup implementation no longer counts
 yield spec.str_spec, False
 # 5. or from the spec we can deduce a name on path  that matches
 yield from spec.generate_names()
 
 
 class PathPythonInfo(PythonInfo):
 """python info from path"""
 
 
 __all__ = [
 "get_interpreter",
 "Builtin",
 "PathPythonInfo",
 ]
 
 |