Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/virtualenv/discovery/cached_py_info.py @ 1:56ad4e20f292 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author | guerler |
---|---|
date | Fri, 31 Jul 2020 00:32:28 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
0:d30785e31577 | 1:56ad4e20f292 |
---|---|
1 """ | |
2 | |
3 We acquire the python information by running an interrogation script via subprocess trigger. This operation is not | |
4 cheap, especially not on Windows. To not have to pay this hefty cost every time we apply multiple levels of | |
5 caching. | |
6 """ | |
7 from __future__ import absolute_import, unicode_literals | |
8 | |
9 import logging | |
10 import os | |
11 import pipes | |
12 import sys | |
13 from collections import OrderedDict | |
14 | |
15 from virtualenv.app_data import AppDataDisabled | |
16 from virtualenv.discovery.py_info import PythonInfo | |
17 from virtualenv.info import PY2 | |
18 from virtualenv.util.path import Path | |
19 from virtualenv.util.six import ensure_text | |
20 from virtualenv.util.subprocess import Popen, subprocess | |
21 | |
22 _CACHE = OrderedDict() | |
23 _CACHE[Path(sys.executable)] = PythonInfo() | |
24 | |
25 | |
26 def from_exe(cls, app_data, exe, raise_on_error=True, ignore_cache=False): | |
27 """""" | |
28 result = _get_from_cache(cls, app_data, exe, ignore_cache=ignore_cache) | |
29 if isinstance(result, Exception): | |
30 if raise_on_error: | |
31 raise result | |
32 else: | |
33 logging.info("%s", str(result)) | |
34 result = None | |
35 return result | |
36 | |
37 | |
38 def _get_from_cache(cls, app_data, exe, ignore_cache=True): | |
39 # note here we cannot resolve symlinks, as the symlink may trigger different prefix information if there's a | |
40 # pyenv.cfg somewhere alongside on python3.4+ | |
41 exe_path = Path(exe) | |
42 if not ignore_cache and exe_path in _CACHE: # check in the in-memory cache | |
43 result = _CACHE[exe_path] | |
44 else: # otherwise go through the app data cache | |
45 py_info = _get_via_file_cache(cls, app_data, exe_path, exe) | |
46 result = _CACHE[exe_path] = py_info | |
47 # independent if it was from the file or in-memory cache fix the original executable location | |
48 if isinstance(result, PythonInfo): | |
49 result.executable = exe | |
50 return result | |
51 | |
52 | |
53 def _get_via_file_cache(cls, app_data, path, exe): | |
54 path_text = ensure_text(str(path)) | |
55 try: | |
56 path_modified = path.stat().st_mtime | |
57 except OSError: | |
58 path_modified = -1 | |
59 if app_data is None: | |
60 app_data = AppDataDisabled() | |
61 py_info, py_info_store = None, app_data.py_info(path) | |
62 with py_info_store.locked(): | |
63 if py_info_store.exists(): # if exists and matches load | |
64 data = py_info_store.read() | |
65 of_path, of_st_mtime, of_content = data["path"], data["st_mtime"], data["content"] | |
66 if of_path == path_text and of_st_mtime == path_modified: | |
67 py_info = cls._from_dict({k: v for k, v in of_content.items()}) | |
68 else: | |
69 py_info_store.remove() | |
70 if py_info is None: # if not loaded run and save | |
71 failure, py_info = _run_subprocess(cls, exe, app_data) | |
72 if failure is None: | |
73 data = {"st_mtime": path_modified, "path": path_text, "content": py_info._to_dict()} | |
74 py_info_store.write(data) | |
75 else: | |
76 py_info = failure | |
77 return py_info | |
78 | |
79 | |
80 def _run_subprocess(cls, exe, app_data): | |
81 py_info_script = Path(os.path.abspath(__file__)).parent / "py_info.py" | |
82 with app_data.ensure_extracted(py_info_script) as py_info_script: | |
83 cmd = [exe, str(py_info_script)] | |
84 # prevent sys.prefix from leaking into the child process - see https://bugs.python.org/issue22490 | |
85 env = os.environ.copy() | |
86 env.pop("__PYVENV_LAUNCHER__", None) | |
87 logging.debug("get interpreter info via cmd: %s", LogCmd(cmd)) | |
88 try: | |
89 process = Popen( | |
90 cmd, | |
91 universal_newlines=True, | |
92 stdin=subprocess.PIPE, | |
93 stderr=subprocess.PIPE, | |
94 stdout=subprocess.PIPE, | |
95 env=env, | |
96 ) | |
97 out, err = process.communicate() | |
98 code = process.returncode | |
99 except OSError as os_error: | |
100 out, err, code = "", os_error.strerror, os_error.errno | |
101 result, failure = None, None | |
102 if code == 0: | |
103 result = cls._from_json(out) | |
104 result.executable = exe # keep original executable as this may contain initialization code | |
105 else: | |
106 msg = "failed to query {} with code {}{}{}".format( | |
107 exe, code, " out: {!r}".format(out) if out else "", " err: {!r}".format(err) if err else "", | |
108 ) | |
109 failure = RuntimeError(msg) | |
110 return failure, result | |
111 | |
112 | |
113 class LogCmd(object): | |
114 def __init__(self, cmd, env=None): | |
115 self.cmd = cmd | |
116 self.env = env | |
117 | |
118 def __repr__(self): | |
119 def e(v): | |
120 return v.decode("utf-8") if isinstance(v, bytes) else v | |
121 | |
122 cmd_repr = e(" ").join(pipes.quote(e(c)) for c in self.cmd) | |
123 if self.env is not None: | |
124 cmd_repr += e(" env of {!r}").format(self.env) | |
125 if PY2: | |
126 return cmd_repr.encode("utf-8") | |
127 return cmd_repr | |
128 | |
129 def __unicode__(self): | |
130 raw = repr(self) | |
131 if PY2: | |
132 return raw.decode("utf-8") | |
133 return raw | |
134 | |
135 | |
136 def clear(app_data): | |
137 app_data.py_info_clear() | |
138 _CACHE.clear() | |
139 | |
140 | |
141 ___all___ = ( | |
142 "from_exe", | |
143 "clear", | |
144 "LogCmd", | |
145 ) |