From fdee2d9a8d83997ea57f14d9d98ce3ad8cc74671 Mon Sep 17 00:00:00 2001 From: Ethan Paul <24588726+enpaul@users.noreply.github.com> Date: Sat, 17 Oct 2020 12:45:36 -0400 Subject: [PATCH] Add support for extras tox configuration option Install only non-optional project dependencies by default Install dependencies for extras when specified Fixes #4 --- tox_poetry_installer.py | 74 ++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/tox_poetry_installer.py b/tox_poetry_installer.py index 793c90d..2d259c1 100644 --- a/tox_poetry_installer.py +++ b/tox_poetry_installer.py @@ -52,6 +52,9 @@ _REPORTER_PREFIX = f"[{__title__}]:" _MAGIC_SUFFIX_MARKER = "@poetry" +PackageMap = Dict[str, PoetryPackage] + + class _SortedEnvDeps(NamedTuple): unlocked_deps: List[ToxDepConfig] locked_deps: List[ToxDepConfig] @@ -140,7 +143,7 @@ def _install_to_venv( installer.install(dependency) -def _find_transients(poetry: Poetry, dependency_name: str) -> Set[PoetryPackage]: +def _find_transients(packages: PackageMap, dependency_name: str) -> Set[PoetryPackage]: """Using a poetry object identify all dependencies of a specific dependency :param poetry: Populated poetry object which can be used to build a populated locked @@ -152,10 +155,6 @@ def _find_transients(poetry: Poetry, dependency_name: str) -> Set[PoetryPackage] .. note:: The package corresponding to the dependency named by ``dependency_name`` is included in the list of returned packages. """ - packages: Dict[str, PoetryPackage] = { - package.name: package - for package in poetry.locker.locked_repository(True).packages - } try: top_level = packages[dependency_name] @@ -185,13 +184,15 @@ def _find_transients(poetry: Poetry, dependency_name: str) -> Set[PoetryPackage] ) from None -def _install_env_dependencies(venv: ToxVirtualEnv, poetry: Poetry): +def _install_env_dependencies( + venv: ToxVirtualEnv, poetry: Poetry, packages: PackageMap +): env_deps = _sort_env_deps(venv) dependencies: List[PoetryPackage] = [] for dep in env_deps.locked_deps: try: - dependencies += _find_transients(poetry, dep.name.lower()) + dependencies += _find_transients(packages, dep.name.lower()) except ToxPoetryInstallerException as err: venv.status = "lockfile installation failed" reporter.error(f"{_REPORTER_PREFIX} {err}") @@ -212,21 +213,39 @@ def _install_env_dependencies(venv: ToxVirtualEnv, poetry: Poetry): _install_to_venv(poetry, venv, dependencies) -def _install_package_dependencies(venv: ToxVirtualEnv, poetry: Poetry): +def _install_package_dependencies( + venv: ToxVirtualEnv, poetry: Poetry, packages: PackageMap +): reporter.verbosity1( f"{_REPORTER_PREFIX} performing installation of project dependencies" ) - primary_dependencies = poetry.locker.locked_repository(False).packages + base_dependencies = [ + packages[item.name] for item in poetry.packages.requires if not item.is_optional + ] + + for extra in venv.envconfig.extras: + extra_dependencies = [ + packages[item.name] for item in poetry.package.extras[extra] + ] + + dependencies: List[PoetryPackage] = [] + for dep in base_dependencies + extra_dependencies: + try: + dependencies += _find_transients(packages, dep.name.lower()) + except ToxPoetryInstallerException as err: + venv.status = "lockfile installation failed" + reporter.error(f"{_REPORTER_PREFIX} {err}") + raise err reporter.verbosity1( - f"{_REPORTER_PREFIX} identified {len(primary_dependencies)} dependencies of project '{poetry.package.name}'" + f"{_REPORTER_PREFIX} identified {len(dependencies)} dependencies of project '{poetry.package.name}'" ) reporter.verbosity0( - f"{_REPORTER_PREFIX} ({venv.name}) installing {len(primary_dependencies)} project dependencies from lockfile" + f"{_REPORTER_PREFIX} ({venv.name}) installing {len(dependencies)} project dependencies from lockfile" ) - _install_to_venv(poetry, venv, primary_dependencies) + _install_to_venv(poetry, venv, dependencies) @hookimpl @@ -283,19 +302,26 @@ def tox_testenv_install_deps(venv: ToxVirtualEnv, action: ToxAction): f"{_REPORTER_PREFIX} loaded project pyproject.toml from {poetry.file}" ) + package_map: PackageMap = { + package.name: package + for package in poetry.locker.locked_repository(True).packages + } + # Handle the installation of any locked env dependencies from the lockfile - _install_env_dependencies(venv, poetry) + _install_env_dependencies(venv, poetry, package_map) # Handle the installation of the package dependencies from the lockfile if the package is # being installed to this venv; otherwise skip installing the package dependencies - if not venv.envconfig.skip_install and not venv.envconfig.config.skipsdist: - _install_package_dependencies(venv, poetry) - else: - if venv.envconfig.skip_install: - reporter.verbosity1( - f"{_REPORTER_PREFIX} env specifies 'skip_install = true', skipping installation of project package" - ) - elif venv.envconfig.config.skipsdist: - reporter.verbosity1( - f"{_REPORTER_PREFIX} config specifies 'skipsdist = true', skipping installation of project package" - ) + if venv.envconfig.skip_install: + reporter.verbosity1( + f"{_REPORTER_PREFIX} env specifies 'skip_install = true', skipping installation of project package" + ) + return + + if venv.envconfig.config.skipsdist: + reporter.verbosity1( + f"{_REPORTER_PREFIX} config specifies 'skipsdist = true', skipping installation of project package" + ) + return + + _install_package_dependencies(venv, poetry, package_map)