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 )