comparison env/lib/python3.9/site-packages/virtualenv/discovery/builtin.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 from __future__ import absolute_import, unicode_literals
2
3 import logging
4 import os
5 import sys
6
7 from virtualenv.info import IS_WIN
8 from virtualenv.util.six import ensure_str, ensure_text
9
10 from .discover import Discover
11 from .py_info import PythonInfo
12 from .py_spec import PythonSpec
13
14
15 class Builtin(Discover):
16 def __init__(self, options):
17 super(Builtin, self).__init__(options)
18 self.python_spec = options.python if options.python else [sys.executable]
19 self.app_data = options.app_data
20 self.try_first_with = options.try_first_with
21
22 @classmethod
23 def add_parser_arguments(cls, parser):
24 parser.add_argument(
25 "-p",
26 "--python",
27 dest="python",
28 metavar="py",
29 type=str,
30 action="append",
31 default=[],
32 help="interpreter based on what to create environment (path/identifier) "
33 "- by default use the interpreter where the tool is installed - first found wins",
34 )
35 parser.add_argument(
36 "--try-first-with",
37 dest="try_first_with",
38 metavar="py_exe",
39 type=str,
40 action="append",
41 default=[],
42 help="try first these interpreters before starting the discovery",
43 )
44
45 def run(self):
46 for python_spec in self.python_spec:
47 result = get_interpreter(python_spec, self.try_first_with, self.app_data, self._env)
48 if result is not None:
49 return result
50 return None
51
52 def __repr__(self):
53 return ensure_str(self.__unicode__())
54
55 def __unicode__(self):
56 spec = self.python_spec[0] if len(self.python_spec) == 1 else self.python_spec
57 return "{} discover of python_spec={!r}".format(self.__class__.__name__, spec)
58
59
60 def get_interpreter(key, try_first_with, app_data=None, env=None):
61 spec = PythonSpec.from_string_spec(key)
62 logging.info("find interpreter for spec %r", spec)
63 proposed_paths = set()
64 env = os.environ if env is None else env
65 for interpreter, impl_must_match in propose_interpreters(spec, try_first_with, app_data, env):
66 key = interpreter.system_executable, impl_must_match
67 if key in proposed_paths:
68 continue
69 logging.info("proposed %s", interpreter)
70 if interpreter.satisfies(spec, impl_must_match):
71 logging.debug("accepted %s", interpreter)
72 return interpreter
73 proposed_paths.add(key)
74
75
76 def propose_interpreters(spec, try_first_with, app_data, env=None):
77 # 0. try with first
78 env = os.environ if env is None else env
79 for py_exe in try_first_with:
80 path = os.path.abspath(py_exe)
81 try:
82 os.lstat(path) # Windows Store Python does not work with os.path.exists, but does for os.lstat
83 except OSError:
84 pass
85 else:
86 yield PythonInfo.from_exe(os.path.abspath(path), app_data, env=env), True
87
88 # 1. if it's a path and exists
89 if spec.path is not None:
90 try:
91 os.lstat(spec.path) # Windows Store Python does not work with os.path.exists, but does for os.lstat
92 except OSError:
93 if spec.is_abs:
94 raise
95 else:
96 yield PythonInfo.from_exe(os.path.abspath(spec.path), app_data, env=env), True
97 if spec.is_abs:
98 return
99 else:
100 # 2. otherwise try with the current
101 yield PythonInfo.current_system(app_data), True
102
103 # 3. otherwise fallback to platform default logic
104 if IS_WIN:
105 from .windows import propose_interpreters
106
107 for interpreter in propose_interpreters(spec, app_data, env):
108 yield interpreter, True
109 # finally just find on path, the path order matters (as the candidates are less easy to control by end user)
110 paths = get_paths(env)
111 tested_exes = set()
112 for pos, path in enumerate(paths):
113 path = ensure_text(path)
114 logging.debug(LazyPathDump(pos, path, env))
115 for candidate, match in possible_specs(spec):
116 found = check_path(candidate, path)
117 if found is not None:
118 exe = os.path.abspath(found)
119 if exe not in tested_exes:
120 tested_exes.add(exe)
121 interpreter = PathPythonInfo.from_exe(exe, app_data, raise_on_error=False, env=env)
122 if interpreter is not None:
123 yield interpreter, match
124
125
126 def get_paths(env):
127 path = env.get(str("PATH"), None)
128 if path is None:
129 try:
130 path = os.confstr("CS_PATH")
131 except (AttributeError, ValueError):
132 path = os.defpath
133 if not path:
134 paths = []
135 else:
136 paths = [p for p in path.split(os.pathsep) if os.path.exists(p)]
137 return paths
138
139
140 class LazyPathDump(object):
141 def __init__(self, pos, path, env):
142 self.pos = pos
143 self.path = path
144 self.env = env
145
146 def __repr__(self):
147 return ensure_str(self.__unicode__())
148
149 def __unicode__(self):
150 content = "discover PATH[{}]={}".format(self.pos, self.path)
151 if self.env.get(str("_VIRTUALENV_DEBUG")): # this is the over the board debug
152 content += " with =>"
153 for file_name in os.listdir(self.path):
154 try:
155 file_path = os.path.join(self.path, file_name)
156 if os.path.isdir(file_path) or not os.access(file_path, os.X_OK):
157 continue
158 except OSError:
159 pass
160 content += " "
161 content += file_name
162 return content
163
164
165 def check_path(candidate, path):
166 _, ext = os.path.splitext(candidate)
167 if sys.platform == "win32" and ext != ".exe":
168 candidate = candidate + ".exe"
169 if os.path.isfile(candidate):
170 return candidate
171 candidate = os.path.join(path, candidate)
172 if os.path.isfile(candidate):
173 return candidate
174 return None
175
176
177 def possible_specs(spec):
178 # 4. then maybe it's something exact on PATH - if it was direct lookup implementation no longer counts
179 yield spec.str_spec, False
180 # 5. or from the spec we can deduce a name on path that matches
181 for exe, match in spec.generate_names():
182 yield exe, match
183
184
185 class PathPythonInfo(PythonInfo):
186 """"""