Fix install of dev package dependencies from lockfile

Override default pip behavior by preemptively installing dev package dependencies
Keep support for tox default skip_install config flag
This commit is contained in:
Ethan Paul 2020-09-25 00:54:45 -04:00
parent b339e3d6d9
commit db09acd8fe
No known key found for this signature in database
GPG Key ID: C5F5542B54A4D9C6

View File

@ -9,6 +9,7 @@ from pathlib import Path
from typing import Dict from typing import Dict
from typing import List from typing import List
from typing import Optional from typing import Optional
from typing import Sequence
from typing import Tuple from typing import Tuple
from poetry.factory import Factory as PoetryFactory from poetry.factory import Factory as PoetryFactory
@ -34,6 +35,8 @@ __authors__ = ["Ethan Paul <24588726+enpaul@users.noreply.github.com>"]
_PEP508_VERSION_DELIMITERS: Tuple[str, ...] = ("~=", "==", "!=", ">", "<") _PEP508_VERSION_DELIMITERS: Tuple[str, ...] = ("~=", "==", "!=", ">", "<")
_REPORTER_PREFIX = f"[{__title__}]:"
class ToxPoetryInstallerException(Exception): class ToxPoetryInstallerException(Exception):
"""Error while installing locked dependencies to the test environment""" """Error while installing locked dependencies to the test environment"""
@ -43,9 +46,24 @@ class NoLockedDependencyError(ToxPoetryInstallerException):
"""Cannot install a package that is not in the lockfile""" """Cannot install a package that is not in the lockfile"""
def _make_poetry(venv: ToxVirtualEnv) -> Poetry: def _install_to_venv(
"""Helper to make a poetry object from a toxenv""" poetry: Poetry, venv: ToxVirtualEnv, packages: Sequence[PoetryPackage]
return PoetryFactory().create_poetry(venv.envconfig.config.toxinidir) ):
"""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
"""
installer = PoetryPipInstaller(
env=PoetryVirtualEnv(path=Path(venv.envconfig.envdir)),
io=PoetryNullIO(),
pool=poetry.pool,
)
for dependency in packages:
reporter.verbosity1(f"{_REPORTER_PREFIX} installing {dependency}")
installer.install(dependency)
def _find_locked_dependencies( def _find_locked_dependencies(
@ -73,7 +91,7 @@ def _find_locked_dependencies(
def find_transients(name: str) -> List[PoetryPackage]: def find_transients(name: str) -> List[PoetryPackage]:
if name in PoetryProvider.UNSAFE_PACKAGES: if name in PoetryProvider.UNSAFE_PACKAGES:
reporter.warning( reporter.warning(
f"Installing '{name}' using Poetry is not supported; skipping" f"{_REPORTER_PREFIX} installing '{name}' using Poetry is not supported; skipping"
) )
return [] return []
transients = [packages[name]] transients = [packages[name]]
@ -112,18 +130,16 @@ def tox_testenv_install_deps(
:param action: Tox action object :param action: Tox action object
""" """
log_prefix = f"[{__title__}] {venv.name} installdeps:"
if action.name == venv.envconfig.config.isolated_build_env: if action.name == venv.envconfig.config.isolated_build_env:
reporter.verbosity1( reporter.verbosity1(
f"{log_prefix} skipping isolated build environment '{action.name}'" f"{_REPORTER_PREFIX} skipping isolated build environment '{action.name}'"
) )
return None return None
poetry = _make_poetry(venv) poetry = PoetryFactory().create_poetry(venv.envconfig.config.toxinidir)
reporter.verbosity1( reporter.verbosity1(
f"{log_prefix} loaded project pyproject.toml from {poetry.file}" f"{_REPORTER_PREFIX} loaded project pyproject.toml from {poetry.file}"
) )
dependencies: List[PoetryPackage] = [] dependencies: List[PoetryPackage] = []
@ -131,17 +147,31 @@ def tox_testenv_install_deps(
dependencies += _find_locked_dependencies(poetry, env_dependency.name) dependencies += _find_locked_dependencies(poetry, env_dependency.name)
reporter.verbosity1( reporter.verbosity1(
f"{log_prefix} identified {len(dependencies)} dependencies for environment {action.name}" f"{_REPORTER_PREFIX} identified {len(dependencies)} actual dependencies from {len(venv.envconfig.deps)} specified env dependencies"
) )
installer = PoetryPipInstaller( reporter.verbosity0(
env=PoetryVirtualEnv(path=Path(venv.envconfig.envdir)), f"{_REPORTER_PREFIX} ({venv.name}) installing {len(dependencies)} env dependencies from lockfile"
io=PoetryNullIO(),
pool=poetry.pool,
) )
_install_to_venv(poetry, venv, dependencies)
for dependency in dependencies: if not venv.envconfig.skip_install:
reporter.verbosity0(f"{log_prefix} installing {dependency}") reporter.verbosity1(
installer.install(dependency) f"{_REPORTER_PREFIX} env specifies 'skip_install = false', performing installation of development package dependencies"
)
primary_dependencies = poetry.locker.locked_repository(False).packages
reporter.verbosity1(
f"{_REPORTER_PREFIX} identified {len(primary_dependencies)} dependencies of development package"
)
reporter.verbosity0(
f"{_REPORTER_PREFIX} ({venv.name}) installing {len(primary_dependencies)} development package dependencies from lockfile"
)
_install_to_venv(poetry, venv, primary_dependencies)
else:
reporter.verbosity1(
f"{_REPORTER_PREFIX} env specifies 'skip_install = true', skipping installation of top-level package"
)
return dependencies return dependencies