diff --git a/README.md b/README.md index b54abed..ad53fe8 100644 --- a/README.md +++ b/README.md @@ -201,6 +201,7 @@ configuration section. | `install_dev_deps` | Boolean | False | Whether all of the Poetry dev-dependencies should be installed to the test environment. | | `install_project_deps` | Boolean | True | Whether all of the Poetry primary dependencies for the project package should be installed to the test environment. | | `require_poetry` | Boolean | False | Whether Tox should be forced to fail if the plugin cannot import Poetry locally. If `False` then the plugin will be skipped for the test environment if Poetry cannot be imported. If `True` then the plugin will force the environment to error and the Tox run to fail. | +| `poetry_dep_groups` | List | `[]` | Names of Poetry dependency groups specified in `pyproject.toml` to install to the test environment. | ### Runtime Options diff --git a/tox_poetry_installer/hooks.py b/tox_poetry_installer/hooks.py index 886e68f..de27e29 100644 --- a/tox_poetry_installer/hooks.py +++ b/tox_poetry_installer/hooks.py @@ -4,6 +4,7 @@ All implementations of tox hooks are defined here, as well as any single-use hel specifically related to implementing the hooks (to keep the size/readability of the hook functions themselves manageable). """ +from itertools import chain from typing import Optional import tox @@ -117,6 +118,13 @@ def tox_addoption(parser: ToxParser): help="Automatically install all Poetry development dependencies to the environment", ) + parser.add_testenv_attribute( + name="poetry_dep_groups", + type="line-list", + default=[], + help="List of Poetry dependency groups to install to the environment", + ) + parser.add_testenv_attribute( name="install_project_deps", type="string", @@ -196,6 +204,20 @@ def tox_testenv_install_deps(venv: ToxVirtualEnv, action: ToxAction) -> Optional dev_deps = [] logger.info("Env does not install development dependencies, skipping") + group_deps = utilities.dedupe_packages( + list( + chain( + *[ + utilities.find_group_deps(group, packages, virtualenv, poetry) + for group in venv.envconfig.poetry_dep_groups + ] + ) + ) + ) + logger.info( + f"Identified {len(group_deps)} group dependencies to install to env" + ) + env_deps = utilities.find_additional_deps( packages, virtualenv, poetry, venv.envconfig.locked_deps ) @@ -231,7 +253,9 @@ def tox_testenv_install_deps(venv: ToxVirtualEnv, action: ToxAction) -> Optional logger.error(f"Internal plugin error: {err}") raise err - dependencies = utilities.dedupe_packages(dev_deps + env_deps + project_deps) + dependencies = utilities.dedupe_packages( + dev_deps + group_deps + env_deps + project_deps + ) if ( venv.envconfig.config.option.parallel_install_threads != constants.DEFAULT_INSTALL_THREADS diff --git a/tox_poetry_installer/utilities.py b/tox_poetry_installer/utilities.py index 99c988d..5aeba8e 100644 --- a/tox_poetry_installer/utilities.py +++ b/tox_poetry_installer/utilities.py @@ -246,6 +246,33 @@ def find_additional_deps( return dedupe_packages(dependencies) +def find_group_deps( + group: str, + packages: PackageMap, + venv: "_poetry.VirtualEnv", + poetry: "_poetry.Poetry", +) -> List[PoetryPackage]: + """Find the dependencies belonging to a dependency group + + Recursively identify the Poetry dev dependencies + + :param group: Name of the dependency group from the project's ``pyproject.toml`` + :param packages: Mapping of all locked package names to their corresponding package object + :param venv: Poetry virtual environment to use for package compatibility checks + :param poetry: Poetry object for the current project + """ + return find_additional_deps( + packages, + venv, + poetry, + poetry.pyproject.data["tool"]["poetry"] + .get("group", {}) + .get(group, {}) + .get("dependencies", {}) + .keys(), + ) + + def find_dev_deps( packages: PackageMap, venv: "_poetry.VirtualEnv", poetry: "_poetry.Poetry" ) -> List[PoetryPackage]: @@ -257,16 +284,7 @@ def find_dev_deps( :param venv: Poetry virtual environment to use for package compatibility checks :param poetry: Poetry object for the current project """ - dev_group_deps = find_additional_deps( - packages, - venv, - poetry, - poetry.pyproject.data["tool"]["poetry"] - .get("group", {}) - .get("dev", {}) - .get("dependencies", {}) - .keys(), - ) + dev_group_deps = find_group_deps("dev", packages, venv, poetry) # Legacy pyproject.toml poetry format: legacy_dev_group_deps = find_additional_deps(