From befb7d100ef091c7e06d2853327c9a0bb35c4e97 Mon Sep 17 00:00:00 2001 From: Mathieu Kniewallner Date: Mon, 6 Jan 2025 02:00:37 +0100 Subject: [PATCH] test: add Poetry PEP 621 unit and functional tests --- .../project_with_poetry_pep_621/poetry.toml | 2 + .../pyproject.toml | 34 ++++++ .../project_with_poetry_pep_621/src/main.py | 10 ++ .../src/notebook.ipynb | 37 ++++++ .../functional/cli/test_cli_poetry_pep_621.py | 108 ++++++++++++++++++ tests/functional/utils.py | 1 + tests/unit/dependency_getter/test_poetry.py | 82 ++++++++++++- 7 files changed, 271 insertions(+), 3 deletions(-) create mode 100644 tests/fixtures/project_with_poetry_pep_621/poetry.toml create mode 100644 tests/fixtures/project_with_poetry_pep_621/pyproject.toml create mode 100644 tests/fixtures/project_with_poetry_pep_621/src/main.py create mode 100644 tests/fixtures/project_with_poetry_pep_621/src/notebook.ipynb create mode 100644 tests/functional/cli/test_cli_poetry_pep_621.py diff --git a/tests/fixtures/project_with_poetry_pep_621/poetry.toml b/tests/fixtures/project_with_poetry_pep_621/poetry.toml new file mode 100644 index 00000000..ab1033bd --- /dev/null +++ b/tests/fixtures/project_with_poetry_pep_621/poetry.toml @@ -0,0 +1,2 @@ +[virtualenvs] +in-project = true diff --git a/tests/fixtures/project_with_poetry_pep_621/pyproject.toml b/tests/fixtures/project_with_poetry_pep_621/pyproject.toml new file mode 100644 index 00000000..a7990dfe --- /dev/null +++ b/tests/fixtures/project_with_poetry_pep_621/pyproject.toml @@ -0,0 +1,34 @@ +[project] +name = "foo" +version = "0.0.1" +description = "" +authors = [] +requires-python = ">=3.9" +dependencies = [ + "arrow==1.3.0", + "pkginfo==1.11.2", + "requests", +] + +[tool.poetry.dependencies] +requests = { git = "https://github.com/psf/requests", tag = "v2.32.3" } + +[project.optional-dependencies] +foo = [ + "click==8.1.7", + "isort==5.13.2", +] +bar = ["urllib3==2.2.3"] + +[tool.poetry.dev-dependencies] +black = "24.10.0" + +[tool.poetry.group.lint.dependencies] +mypy = "1.13.0" + +[tool.poetry.group.test.dependencies] +pytest = "8.3.3" +pytest-cov = "5.0.0" + +[tool.deptry.per_rule_ignores] +DEP002 = ["pkginfo"] diff --git a/tests/fixtures/project_with_poetry_pep_621/src/main.py b/tests/fixtures/project_with_poetry_pep_621/src/main.py new file mode 100644 index 00000000..7d5e2312 --- /dev/null +++ b/tests/fixtures/project_with_poetry_pep_621/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/fixtures/project_with_poetry_pep_621/src/notebook.ipynb b/tests/fixtures/project_with_poetry_pep_621/src/notebook.ipynb new file mode 100644 index 00000000..42be444d --- /dev/null +++ b/tests/fixtures/project_with_poetry_pep_621/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 arrow" + ] + } + ], + "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_pep_621.py b/tests/functional/cli/test_cli_poetry_pep_621.py new file mode 100644 index 00000000..f06aadf5 --- /dev/null +++ b/tests/functional/cli/test_cli_poetry_pep_621.py @@ -0,0 +1,108 @@ +from __future__ import annotations + +import uuid +from pathlib import Path +from typing import TYPE_CHECKING + +import pytest + +from tests.functional.utils import Project +from tests.utils import get_issues_report + +if TYPE_CHECKING: + from tests.utils import PoetryVenvFactory + + +@pytest.mark.xdist_group(name=Project.POETRY_PEP_621) +def test_cli_with_poetry_pep_621(poetry_venv_factory: PoetryVenvFactory) -> None: + with poetry_venv_factory(Project.POETRY_PEP_621) as virtual_env: + issue_report = f"{uuid.uuid4()}.json" + result = virtual_env.run(f"deptry . -o {issue_report}") + + assert result.returncode == 1 + assert get_issues_report(Path(issue_report)) == [ + { + "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": "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": "DEP004", + "message": "'black' imported but declared as a dev dependency", + }, + "module": "black", + "location": { + "file": str(Path("src/main.py")), + "line": 4, + "column": 8, + }, + }, + { + "error": { + "code": "DEP004", + "message": "'mypy' imported but declared as a dev dependency", + }, + "module": "mypy", + "location": { + "file": str(Path("src/main.py")), + "line": 6, + "column": 8, + }, + }, + { + "error": { + "code": "DEP004", + "message": "'pytest' imported but declared as a dev dependency", + }, + "module": "pytest", + "location": { + "file": str(Path("src/main.py")), + "line": 7, + "column": 8, + }, + }, + { + "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": 8, + }, + }, + { + "error": { + "code": "DEP001", + "message": "'white' imported but missing from the dependency definitions", + }, + "module": "white", + "location": { + "file": str(Path("src/main.py")), + "line": 9, + "column": 8, + }, + }, + ] diff --git a/tests/functional/utils.py b/tests/functional/utils.py index e38fdb2d..b207e01e 100644 --- a/tests/functional/utils.py +++ b/tests/functional/utils.py @@ -13,6 +13,7 @@ class Project(str, Enum): NAMESPACE = "project_using_namespace" PDM = "project_with_pdm" POETRY = "project_with_poetry" + POETRY_PEP_621 = "project_with_poetry_pep_621" PYPROJECT_DIFFERENT_DIRECTORY = "project_with_pyproject_different_directory" REQUIREMENTS_TXT = "project_with_requirements_txt" REQUIREMENTS_IN = "project_with_requirements_in" diff --git a/tests/unit/dependency_getter/test_poetry.py b/tests/unit/dependency_getter/test_poetry.py index 35fc7a1b..4403836d 100644 --- a/tests/unit/dependency_getter/test_poetry.py +++ b/tests/unit/dependency_getter/test_poetry.py @@ -13,13 +13,13 @@ def test_dependency_getter(tmp_path: Path) -> None: [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" } +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" } fox-python = "*" # top level module is called "fox" [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" @@ -73,6 +73,82 @@ def test_dependency_getter(tmp_path: Path) -> None: assert "pytest_cov" in dev_dependencies[5].top_levels +def test_dependency_getter_pep_621(tmp_path: Path) -> None: + fake_pyproject_toml = """ +[project] +name = "foo" +requires-python = ">=3.7,<4.0" +dependencies = [ + "bar>=2.5.1,<4.0.0", + "fox-python", +] + +[project.optional-dependencies] +an-extra = ["foobar>=2.5.1,<4.0.0"] + +[tool.poetry.dependencies] +fox-python = { git = "https://example.com/foo/bar.git", tag = "v1.2.3" } +# This dependency will not be used by Poetry nor extracted by deptry, as `[project.dependencies]` defines at least once +# dependency, and in this case, dependencies in `[tool.poetry.dependencies]` only enrich existing dependencies from +# `[project.dependencies]`, so deptry will completely skip `[tool.poetry.dependencies]`. +skipped-dependency = "1.2.3" + +[tool.poetry.dev-dependencies] +toml = "^0.10.2" +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: + f.write(fake_pyproject_toml) + + getter = PoetryDependencyGetter( + config=Path("pyproject.toml"), + package_module_name_map={"fox-python": ("fox",)}, + ) + dependencies_extract = getter.get() + dependencies = dependencies_extract.dependencies + dev_dependencies = dependencies_extract.dev_dependencies + + assert len(dependencies) == 3 + assert len(dev_dependencies) == 6 + + assert dependencies[0].name == "bar" + assert "bar" in dependencies[0].top_levels + + assert dependencies[1].name == "fox-python" + assert "fox" in dependencies[1].top_levels + + assert dependencies[2].name == "foobar" + assert "foobar" in dependencies[2].top_levels + + assert dev_dependencies[0].name == "toml" + assert "toml" in dev_dependencies[0].top_levels + + assert dev_dependencies[1].name == "qux" + assert "qux" in dev_dependencies[1].top_levels + + assert dev_dependencies[2].name == "black" + assert "black" in dev_dependencies[2].top_levels + + assert dev_dependencies[3].name == "mypy" + assert "mypy" in dev_dependencies[3].top_levels + + assert dev_dependencies[4].name == "pytest" + assert "pytest" in dev_dependencies[4].top_levels + + assert dev_dependencies[5].name == "pytest-cov" + assert "pytest_cov" in dev_dependencies[5].top_levels + + def test_dependency_getter_empty_dependencies(tmp_path: Path) -> None: fake_pyproject_toml = """ [tool.poetry]