2021-02-10 04:36:40 +00:00
|
|
|
"""Funcationality for performing virtualenv installation"""
|
|
|
|
# Silence this one globally to support the internal function imports for the proxied poetry module.
|
|
|
|
# See the docstring in 'tox_poetry_installer._poetry' for more context.
|
|
|
|
# pylint: disable=import-outside-toplevel
|
2021-04-16 05:55:30 +00:00
|
|
|
import concurrent.futures
|
|
|
|
import contextlib
|
2021-02-10 04:36:40 +00:00
|
|
|
import typing
|
2021-07-07 18:29:15 +00:00
|
|
|
from datetime import datetime
|
2022-07-03 03:28:43 +00:00
|
|
|
from typing import Collection
|
2021-02-10 04:36:40 +00:00
|
|
|
from typing import Set
|
|
|
|
|
2023-01-10 02:34:35 +00:00
|
|
|
from tox.tox_env.api import ToxEnv as ToxVirtualEnv
|
2021-02-10 04:36:40 +00:00
|
|
|
|
2021-04-16 23:57:50 +00:00
|
|
|
from tox_poetry_installer import logger
|
2021-02-16 02:01:44 +00:00
|
|
|
from tox_poetry_installer import utilities
|
2021-02-10 04:36:40 +00:00
|
|
|
|
|
|
|
if typing.TYPE_CHECKING:
|
|
|
|
from tox_poetry_installer import _poetry
|
|
|
|
|
|
|
|
|
|
|
|
def install(
|
2021-04-16 05:55:30 +00:00
|
|
|
poetry: "_poetry.Poetry",
|
|
|
|
venv: ToxVirtualEnv,
|
2023-05-04 11:05:02 +00:00
|
|
|
packages: Collection["_poetry.PoetryPackage"],
|
2021-05-05 20:18:41 +00:00
|
|
|
parallels: int = 0,
|
2021-02-10 04:36:40 +00:00
|
|
|
):
|
|
|
|
"""Install a bunch of packages to a virtualenv
|
|
|
|
|
|
|
|
:param poetry: Poetry object the packages were sourced from
|
|
|
|
:param venv: Tox virtual environment to install the packages to
|
|
|
|
:param packages: List of packages to install to the virtual environment
|
2021-04-16 05:55:30 +00:00
|
|
|
:param parallels: Number of parallel processes to use for installing dependency packages, or
|
|
|
|
``None`` to disable parallelization.
|
2021-02-10 04:36:40 +00:00
|
|
|
"""
|
|
|
|
from tox_poetry_installer import _poetry
|
|
|
|
|
2023-01-10 02:34:35 +00:00
|
|
|
logger.info(f"Installing {len(packages)} packages to environment at {venv.env_dir}")
|
2021-02-10 04:36:40 +00:00
|
|
|
|
2023-05-04 11:05:02 +00:00
|
|
|
install_executor = _poetry.Executor(
|
2021-02-16 02:01:44 +00:00
|
|
|
env=utilities.convert_virtualenv(venv),
|
2021-02-10 04:36:40 +00:00
|
|
|
io=_poetry.NullIO(),
|
|
|
|
pool=poetry.pool,
|
2023-05-04 11:05:02 +00:00
|
|
|
config=_poetry.Config(),
|
2021-02-10 04:36:40 +00:00
|
|
|
)
|
|
|
|
|
2023-05-04 11:05:02 +00:00
|
|
|
installed: Set[_poetry.PoetryPackage] = set()
|
2021-02-10 04:36:40 +00:00
|
|
|
|
2023-05-04 11:05:02 +00:00
|
|
|
def logged_install(dependency: _poetry.PoetryPackage) -> None:
|
2021-07-07 18:29:15 +00:00
|
|
|
start = datetime.now()
|
|
|
|
logger.debug(f"Installing {dependency}")
|
2023-05-04 11:05:02 +00:00
|
|
|
install_executor.execute([_poetry.Install(package=dependency)])
|
2021-07-07 18:29:15 +00:00
|
|
|
end = datetime.now()
|
|
|
|
logger.debug(f"Finished installing {dependency} in {end - start}")
|
|
|
|
|
2021-04-16 05:55:30 +00:00
|
|
|
@contextlib.contextmanager
|
|
|
|
def _optional_parallelize():
|
|
|
|
"""A bit of cheat, really
|
|
|
|
|
|
|
|
A context manager that exposes a common interface for the caller that optionally
|
|
|
|
enables/disables the usage of the parallel thread pooler depending on the value of
|
|
|
|
the ``parallels`` parameter.
|
|
|
|
"""
|
2021-05-05 20:18:41 +00:00
|
|
|
if parallels > 0:
|
2021-04-16 05:55:30 +00:00
|
|
|
with concurrent.futures.ThreadPoolExecutor(
|
|
|
|
max_workers=parallels
|
|
|
|
) as executor:
|
|
|
|
yield executor.submit
|
2021-02-10 04:36:40 +00:00
|
|
|
else:
|
2021-04-16 05:55:30 +00:00
|
|
|
yield lambda func, arg: func(arg)
|
|
|
|
|
|
|
|
with _optional_parallelize() as executor:
|
2023-03-29 22:52:51 +00:00
|
|
|
futures = []
|
2021-04-16 05:55:30 +00:00
|
|
|
for dependency in packages:
|
|
|
|
if dependency not in installed:
|
|
|
|
installed.add(dependency)
|
2021-07-07 18:29:15 +00:00
|
|
|
logger.debug(f"Queuing {dependency}")
|
2023-03-29 22:52:51 +00:00
|
|
|
future = executor(logged_install, dependency)
|
|
|
|
if future is not None:
|
|
|
|
futures.append(future)
|
2021-04-16 05:55:30 +00:00
|
|
|
else:
|
2021-04-16 23:57:50 +00:00
|
|
|
logger.debug(f"Skipping {dependency}, already installed")
|
|
|
|
logger.debug("Waiting for installs to finish...")
|
2023-03-29 22:52:51 +00:00
|
|
|
|
|
|
|
for future in concurrent.futures.as_completed(futures):
|
|
|
|
# Don't actually care about the return value, just waiting on the
|
|
|
|
# future to ensure any exceptions that were raised in the called
|
|
|
|
# function are propagated.
|
|
|
|
future.result()
|