diff env/lib/python3.9/site-packages/virtualenv/seed/embed/via_app_data/via_app_data.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/env/lib/python3.9/site-packages/virtualenv/seed/embed/via_app_data/via_app_data.py	Mon Mar 22 18:12:50 2021 +0000
@@ -0,0 +1,140 @@
+"""Bootstrap"""
+from __future__ import absolute_import, unicode_literals
+
+import logging
+import sys
+import traceback
+from contextlib import contextmanager
+from subprocess import CalledProcessError
+from threading import Lock, Thread
+
+from virtualenv.info import fs_supports_symlink
+from virtualenv.seed.embed.base_embed import BaseEmbed
+from virtualenv.seed.wheels import get_wheel
+from virtualenv.util.path import Path
+
+from .pip_install.copy import CopyPipInstall
+from .pip_install.symlink import SymlinkPipInstall
+
+
+class FromAppData(BaseEmbed):
+    def __init__(self, options):
+        super(FromAppData, self).__init__(options)
+        self.symlinks = options.symlink_app_data
+
+    @classmethod
+    def add_parser_arguments(cls, parser, interpreter, app_data):
+        super(FromAppData, cls).add_parser_arguments(parser, interpreter, app_data)
+        can_symlink = app_data.transient is False and fs_supports_symlink()
+        parser.add_argument(
+            "--symlink-app-data",
+            dest="symlink_app_data",
+            action="store_true" if can_symlink else "store_false",
+            help="{} symlink the python packages from the app-data folder (requires seed pip>=19.3)".format(
+                "" if can_symlink else "not supported - ",
+            ),
+            default=False,
+        )
+
+    def run(self, creator):
+        if not self.enabled:
+            return
+        with self._get_seed_wheels(creator) as name_to_whl:
+            pip_version = name_to_whl["pip"].version_tuple if "pip" in name_to_whl else None
+            installer_class = self.installer_class(pip_version)
+            exceptions = {}
+
+            def _install(name, wheel):
+                try:
+                    logging.debug("install %s from wheel %s via %s", name, wheel, installer_class.__name__)
+                    key = Path(installer_class.__name__) / wheel.path.stem
+                    wheel_img = self.app_data.wheel_image(creator.interpreter.version_release_str, key)
+                    installer = installer_class(wheel.path, creator, wheel_img)
+                    parent = self.app_data.lock / wheel_img.parent
+                    with parent.non_reentrant_lock_for_key(wheel_img.name):
+                        if not installer.has_image():
+                            installer.build_image()
+                    installer.install(creator.interpreter.version_info)
+                except Exception:  # noqa
+                    exceptions[name] = sys.exc_info()
+
+            threads = list(Thread(target=_install, args=(n, w)) for n, w in name_to_whl.items())
+            for thread in threads:
+                thread.start()
+            for thread in threads:
+                thread.join()
+            if exceptions:
+                messages = ["failed to build image {} because:".format(", ".join(exceptions.keys()))]
+                for value in exceptions.values():
+                    exc_type, exc_value, exc_traceback = value
+                    messages.append("".join(traceback.format_exception(exc_type, exc_value, exc_traceback)))
+                raise RuntimeError("\n".join(messages))
+
+    @contextmanager
+    def _get_seed_wheels(self, creator):
+        name_to_whl, lock, fail = {}, Lock(), {}
+
+        def _get(distribution, version):
+            for_py_version = creator.interpreter.version_release_str
+            failure, result = None, None
+            # fallback to download in case the exact version is not available
+            for download in [True] if self.download else [False, True]:
+                failure = None
+                try:
+                    result = get_wheel(
+                        distribution=distribution,
+                        version=version,
+                        for_py_version=for_py_version,
+                        search_dirs=self.extra_search_dir,
+                        download=download,
+                        app_data=self.app_data,
+                        do_periodic_update=self.periodic_update,
+                        env=self.env,
+                    )
+                    if result is not None:
+                        break
+                except Exception as exception:  # noqa
+                    logging.exception("fail")
+                    failure = exception
+            if failure:
+                if isinstance(failure, CalledProcessError):
+                    msg = "failed to download {}".format(distribution)
+                    if version is not None:
+                        msg += " version {}".format(version)
+                    msg += ", pip download exit code {}".format(failure.returncode)
+                    output = failure.output if sys.version_info < (3, 5) else (failure.output + failure.stderr)
+                    if output:
+                        msg += "\n"
+                        msg += output
+                else:
+                    msg = repr(failure)
+                logging.error(msg)
+                with lock:
+                    fail[distribution] = version
+            else:
+                with lock:
+                    name_to_whl[distribution] = result
+
+        threads = list(
+            Thread(target=_get, args=(distribution, version))
+            for distribution, version in self.distribution_to_versions().items()
+        )
+        for thread in threads:
+            thread.start()
+        for thread in threads:
+            thread.join()
+        if fail:
+            raise RuntimeError("seed failed due to failing to download wheels {}".format(", ".join(fail.keys())))
+        yield name_to_whl
+
+    def installer_class(self, pip_version_tuple):
+        if self.symlinks and pip_version_tuple:
+            # symlink support requires pip 19.3+
+            if pip_version_tuple >= (19, 3):
+                return SymlinkPipInstall
+        return CopyPipInstall
+
+    def __unicode__(self):
+        base = super(FromAppData, self).__unicode__()
+        msg = ", via={}, app_data_dir={}".format("symlink" if self.symlinks else "copy", self.app_data)
+        return base[:-1] + msg + base[-1]