diff --git a/deptry/dependency_getter/poetry.py b/deptry/dependency_getter/poetry.py index 8cafdcde..31c88f23 100644 --- a/deptry/dependency_getter/poetry.py +++ b/deptry/dependency_getter/poetry.py @@ -43,10 +43,16 @@ def _get_poetry_dev_dependencies(self) -> list[Dependency]: pyproject_data = load_pyproject_toml(self.config) with contextlib.suppress(KeyError): - dependencies = {**pyproject_data["tool"]["poetry"]["dev-dependencies"], **dependencies} + dependencies = {**dependencies, **pyproject_data["tool"]["poetry"]["dev-dependencies"]} - with contextlib.suppress(KeyError): - dependencies = {**pyproject_data["tool"]["poetry"]["group"]["dev"]["dependencies"], **dependencies} + try: + dependency_groups = pyproject_data["tool"]["poetry"]["group"] + except KeyError: + dependency_groups = {} + + for group_values in dependency_groups.values(): + with contextlib.suppress(KeyError): + dependencies = {**dependencies, **group_values["dependencies"]} return self._get_dependencies(dependencies, self.package_module_name_map) diff --git a/tests/data/project_with_poetry/poetry.toml b/tests/data/project_with_poetry/poetry.toml new file mode 100644 index 00000000..ab1033bd --- /dev/null +++ b/tests/data/project_with_poetry/poetry.toml @@ -0,0 +1,2 @@ +[virtualenvs] +in-project = true diff --git a/tests/data/project_with_poetry/pyproject.toml b/tests/data/project_with_poetry/pyproject.toml new file mode 100644 index 00000000..ad787461 --- /dev/null +++ b/tests/data/project_with_poetry/pyproject.toml @@ -0,0 +1,33 @@ +[tool.poetry] +name = "test" +version = "0.0.1" +description = "A test project" +authors = ["test "] + +[tool.poetry.dependencies] +python = ">=3.7" +pkginfo = ">=1.8.3" +toml = "*" +urllib3 = ">=1.26.12" + +click = { version = ">=8.1.3", optional = true } +isort = { version = ">=5.10.1", optional = true } +requests = { version = ">=2.28.1", optional = true } + +[tool.poetry.extras] +foo = [ + "click", + "isort", +] +bar = ["requests"] + +[tool.poetry.group.lint.dependencies] +black = "^22.6.0" +mypy = "^1.3.0" + +[tool.poetry.group.test.dependencies] +pytest = "^7.3.0" +pytest-cov = "^4.0.0" + +[tool.deptry] +ignore_unused = ["pkginfo"] diff --git a/tests/data/project_with_poetry/src/main.py b/tests/data/project_with_poetry/src/main.py new file mode 100644 index 00000000..7d5e2312 --- /dev/null +++ b/tests/data/project_with_poetry/src/main.py @@ -0,0 +1,10 @@ +from os import chdir, walk +from pathlib import Path + +import black +import click +import mypy +import pytest +import pytest_cov +import white as w +from urllib3 import contrib diff --git a/tests/data/project_with_poetry/src/notebook.ipynb b/tests/data/project_with_poetry/src/notebook.ipynb new file mode 100644 index 00000000..a51bdb9d --- /dev/null +++ b/tests/data/project_with_poetry/src/notebook.ipynb @@ -0,0 +1,37 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "id": "9f4924ec-2200-4801-9d49-d4833651cbc4", + "metadata": {}, + "outputs": [], + "source": [ + "import click\n", + "from urllib3 import contrib\n", + "import toml" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tests/functional/cli/test_cli_poetry.py b/tests/functional/cli/test_cli_poetry.py new file mode 100644 index 00000000..e12c7a33 --- /dev/null +++ b/tests/functional/cli/test_cli_poetry.py @@ -0,0 +1,105 @@ +from __future__ import annotations + +from pathlib import Path +from typing import TYPE_CHECKING + +from click.testing import CliRunner + +from deptry.cli import deptry +from tests.utils import get_issues_report, run_within_dir + +if TYPE_CHECKING: + from tests.functional.types import ToolSpecificProjectBuilder + + +def test_cli_with_poetry(poetry_project_builder: ToolSpecificProjectBuilder) -> None: + with run_within_dir(poetry_project_builder("project_with_poetry")): + result = CliRunner().invoke(deptry, ". -o report.json") + + assert result.exit_code == 1 + assert get_issues_report() == [ + { + "error": { + "code": "DEP002", + "message": "'isort' defined as a dependency but not used in the codebase", + }, + "module": "isort", + "location": { + "file": str(Path("pyproject.toml")), + "line": None, + "column": None, + }, + }, + { + "error": { + "code": "DEP002", + "message": "'requests' defined as a dependency but not used in the codebase", + }, + "module": "requests", + "location": { + "file": str(Path("pyproject.toml")), + "line": None, + "column": None, + }, + }, + { + "error": { + "code": "DEP004", + "message": "'black' imported but declared as a dev dependency", + }, + "module": "black", + "location": { + "file": str(Path("src/main.py")), + "line": 4, + "column": 0, + }, + }, + { + "error": { + "code": "DEP004", + "message": "'mypy' imported but declared as a dev dependency", + }, + "module": "mypy", + "location": { + "file": str(Path("src/main.py")), + "line": 6, + "column": 0, + }, + }, + { + "error": { + "code": "DEP004", + "message": "'pytest' imported but declared as a dev dependency", + }, + "module": "pytest", + "location": { + "file": str(Path("src/main.py")), + "line": 7, + "column": 0, + }, + }, + { + "error": { + "code": "DEP004", + "message": "'pytest_cov' imported but declared as a dev dependency", + }, + "module": "pytest_cov", + "location": { + "file": str(Path("src/main.py")), + "line": 8, + "column": 0, + }, + }, + { + "error": { + "code": "DEP001", + "message": "'white' imported but missing from the dependency definitions", + }, + "module": "white", + "location": { + "file": str(Path("src/main.py")), + "line": 9, + "column": 0, + }, + }, + ] diff --git a/tests/unit/dependency_getter/test_poetry.py b/tests/unit/dependency_getter/test_poetry.py index 1da321d0..c0cdad8f 100644 --- a/tests/unit/dependency_getter/test_poetry.py +++ b/tests/unit/dependency_getter/test_poetry.py @@ -7,7 +7,8 @@ def test_dependency_getter(tmp_path: Path) -> None: - fake_pyproject_toml = """[tool.poetry.dependencies] + fake_pyproject_toml = """ +[tool.poetry.dependencies] python = ">=3.7,<4.0" bar = { version = ">=2.5.1,<4.0.0", python = ">3.7" } foo-bar = { version = ">=2.5.1,<4.0.0", optional = true, python = ">3.7" } @@ -15,7 +16,16 @@ def test_dependency_getter(tmp_path: Path) -> None: [tool.poetry.dev-dependencies] toml = "^0.10.2" -qux = { version = ">=2.5.1,<4.0.0", optional = true }""" +qux = { version = ">=2.5.1,<4.0.0", optional = true } + +[tool.poetry.group.lint.dependencies] +black = "^22.6.0" +mypy = "^1.3.0" + +[tool.poetry.group.test.dependencies] +pytest = "^7.3.0" +pytest-cov = "^4.0.0" +""" with run_within_dir(tmp_path): with Path("pyproject.toml").open("w") as f: @@ -30,7 +40,7 @@ def test_dependency_getter(tmp_path: Path) -> None: dev_dependencies = dependencies_extract.dev_dependencies assert len(dependencies) == 3 - assert len(dev_dependencies) == 2 + assert len(dev_dependencies) == 6 assert dependencies[0].name == "bar" assert dependencies[0].is_conditional @@ -56,3 +66,23 @@ def test_dependency_getter(tmp_path: Path) -> None: assert not dev_dependencies[1].is_conditional assert dev_dependencies[1].is_optional assert "qux" in dev_dependencies[1].top_levels + + assert dev_dependencies[2].name == "black" + assert not dev_dependencies[2].is_conditional + assert not dev_dependencies[2].is_optional + assert "black" in dev_dependencies[2].top_levels + + assert dev_dependencies[3].name == "mypy" + assert not dev_dependencies[3].is_conditional + assert not dev_dependencies[3].is_optional + assert "mypy" in dev_dependencies[3].top_levels + + assert dev_dependencies[4].name == "pytest" + assert not dev_dependencies[4].is_conditional + assert not dev_dependencies[4].is_optional + assert "pytest" in dev_dependencies[4].top_levels + + assert dev_dependencies[5].name == "pytest-cov" + assert not dev_dependencies[5].is_conditional + assert not dev_dependencies[5].is_optional + assert "pytest_cov" in dev_dependencies[5].top_levels