summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKOLANICH <KOLANICH@users.noreply.github.com>2022-06-29 00:59:17 +0000
committerGitHub <noreply@github.com>2022-06-29 01:59:17 +0100
commit8856f51766e57cafb4e47636044e880076a64579 (patch)
tree02f803046b64d72a29238ba7881ab626cc91252d /src
parent245cae75ecbefd9fa815633a2b8dedb9b403d0f0 (diff)
downloadvirtualenv-8856f51766e57cafb4e47636044e880076a64579.tar.gz
Fixed the incorrect operation when `setuptools` plugins output something into `stdout`. (#2335)
Diffstat (limited to 'src')
-rw-r--r--src/virtualenv/discovery/cached_py_info.py41
-rw-r--r--src/virtualenv/discovery/py_info.py19
2 files changed, 58 insertions, 2 deletions
diff --git a/src/virtualenv/discovery/cached_py_info.py b/src/virtualenv/discovery/cached_py_info.py
index 4e1d976..7eb4161 100644
--- a/src/virtualenv/discovery/cached_py_info.py
+++ b/src/virtualenv/discovery/cached_py_info.py
@@ -8,8 +8,10 @@ from __future__ import absolute_import, unicode_literals
import logging
import os
+import random
import sys
from collections import OrderedDict
+from string import ascii_lowercase, ascii_uppercase, digits
from virtualenv.app_data import AppDataDisabled
from virtualenv.discovery.py_info import PythonInfo
@@ -85,10 +87,27 @@ def _get_via_file_cache(cls, app_data, path, exe, env):
return py_info
+COOKIE_LENGTH = 32 # type: int
+
+
+def gen_cookie():
+ return "".join(random.choice("".join((ascii_lowercase, ascii_uppercase, digits))) for _ in range(COOKIE_LENGTH))
+
+
def _run_subprocess(cls, exe, app_data, env):
py_info_script = Path(os.path.abspath(__file__)).parent / "py_info.py"
+ # Cookies allow to split the serialized stdout output generated by the script collecting the info from the output
+ # generated by something else. The right way to deal with it is to create an anonymous pipe and pass its descriptor
+ # to the child and output to it. But AFAIK all of them are either not cross-platform or too big to implement and are
+ # not in the stdlib. So the easiest and the shortest way I could mind is just using the cookies.
+ # We generate pseudorandom cookies because it easy to implement and avoids breakage from outputting modules source
+ # code, i.e. by debug output libraries. We reverse the cookies to avoid breakages resulting from variable values
+ # appearing in debug output.
+
+ start_cookie = gen_cookie()
+ end_cookie = gen_cookie()
with app_data.ensure_extracted(py_info_script) as py_info_script:
- cmd = [exe, str(py_info_script)]
+ cmd = [exe, str(py_info_script), start_cookie, end_cookie]
# prevent sys.prefix from leaking into the child process - see https://bugs.python.org/issue22490
env = env.copy()
env.pop("__PYVENV_LAUNCHER__", None)
@@ -108,6 +127,26 @@ def _run_subprocess(cls, exe, app_data, env):
out, err, code = "", os_error.strerror, os_error.errno
result, failure = None, None
if code == 0:
+ out_starts = out.find(start_cookie[::-1])
+
+ if out_starts > -1:
+ pre_cookie = out[:out_starts]
+
+ if pre_cookie:
+ sys.stdout.write(pre_cookie)
+
+ out = out[out_starts + COOKIE_LENGTH :]
+
+ out_ends = out.find(end_cookie[::-1])
+
+ if out_ends > -1:
+ post_cookie = out[out_ends + COOKIE_LENGTH :]
+
+ if post_cookie:
+ sys.stdout.write(post_cookie)
+
+ out = out[:out_ends]
+
result = cls._from_json(out)
result.executable = exe # keep original executable as this may contain initialization code
else:
diff --git a/src/virtualenv/discovery/py_info.py b/src/virtualenv/discovery/py_info.py
index 144a1d1..09a3dc1 100644
--- a/src/virtualenv/discovery/py_info.py
+++ b/src/virtualenv/discovery/py_info.py
@@ -524,4 +524,21 @@ class PythonInfo(object):
if __name__ == "__main__":
# dump a JSON representation of the current python
# noinspection PyProtectedMember
- print(PythonInfo()._to_json())
+ argv = sys.argv[1:]
+
+ if len(argv) >= 1:
+ start_cookie = argv[0]
+ argv = argv[1:]
+ else:
+ start_cookie = ""
+
+ if len(argv) >= 1:
+ end_cookie = argv[0]
+ argv = argv[1:]
+ else:
+ end_cookie = ""
+
+ sys.argv = sys.argv[:1] + argv
+
+ info = PythonInfo()._to_json()
+ sys.stdout.write("".join((start_cookie[::-1], info, end_cookie[::-1])))