From 70ad2ccbdc403a35dc2c48612e69509abbf6fbca Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Thu, 26 May 2022 15:53:53 +0200 Subject: [PATCH 1/2] packages: unify python project check --- src/poetry/core/packages/dependency.py | 7 +++-- .../core/packages/directory_dependency.py | 26 ++++++++----------- src/poetry/core/packages/utils/utils.py | 25 ++++++++++++------ tests/packages/utils/test_utils.py | 19 ++++++++++++++ 4 files changed, 50 insertions(+), 27 deletions(-) diff --git a/src/poetry/core/packages/dependency.py b/src/poetry/core/packages/dependency.py index 363235dd8..298b0b80c 100644 --- a/src/poetry/core/packages/dependency.py +++ b/src/poetry/core/packages/dependency.py @@ -490,7 +490,7 @@ def create_from_pep_508( from poetry.core.packages.url_dependency import URLDependency from poetry.core.packages.utils.link import Link from poetry.core.packages.utils.utils import is_archive_file - from poetry.core.packages.utils.utils import is_installable_dir + from poetry.core.packages.utils.utils import is_python_project from poetry.core.packages.utils.utils import is_url from poetry.core.packages.utils.utils import path_to_url from poetry.core.packages.utils.utils import strip_extras @@ -521,10 +521,9 @@ def create_from_pep_508( path_str = os.path.normpath(os.path.abspath(name)) p, extras = strip_extras(path_str) if os.path.isdir(p) and (os.path.sep in name or name.startswith(".")): - - if not is_installable_dir(p): + if not is_python_project(Path(name)): raise ValueError( - f"Directory {name!r} is not installable. File 'setup.py' " + f"Directory {name!r} is not installable. File 'setup.[py|cfg]' " "not found." ) link = Link(path_to_url(p)) diff --git a/src/poetry/core/packages/directory_dependency.py b/src/poetry/core/packages/directory_dependency.py index 05ad95ea6..9ae1c96bc 100644 --- a/src/poetry/core/packages/directory_dependency.py +++ b/src/poetry/core/packages/directory_dependency.py @@ -1,14 +1,19 @@ from __future__ import annotations +import functools + from pathlib import Path from typing import TYPE_CHECKING from typing import Iterable +from poetry.core.pyproject.toml import PyProjectTOML + if TYPE_CHECKING: from poetry.core.semver.version_constraint import VersionConstraint from poetry.core.packages.dependency import Dependency +from poetry.core.packages.utils.utils import is_python_project from poetry.core.packages.utils.utils import path_to_url @@ -23,7 +28,6 @@ def __init__( develop: bool = False, extras: Iterable[str] | None = None, ) -> None: - from poetry.core.pyproject.toml import PyProjectTOML self._path = path self._base = base or Path.cwd() @@ -43,18 +47,7 @@ def __init__( if self._full_path.is_file(): raise ValueError(f"{self._path} is a file, expected a directory") - # Checking content to determine actions - setup_py = self._full_path / "setup.py" - setup_cfg = self._full_path / "setup.cfg" - setuptools_project = setup_py.exists() or setup_cfg.exists() - pyproject = PyProjectTOML(self._full_path / "pyproject.toml") - - self._supports_pep517 = ( - setuptools_project or pyproject.is_build_system_defined() - ) - self._supports_poetry = pyproject.is_poetry_project() - - if not (self._supports_pep517 or self._supports_poetry): + if not is_python_project(self._full_path): raise ValueError( f"Directory {self._full_path} does not seem to be a Python package" ) @@ -70,6 +63,9 @@ def __init__( extras=extras, ) + # cache this function to avoid multiple IO reads and parsing + self.supports_poetry = functools.lru_cache(maxsize=1)(self._supports_poetry) + @property def path(self) -> Path: return self._path @@ -86,8 +82,8 @@ def base(self) -> Path: def develop(self) -> bool: return self._develop - def supports_poetry(self) -> bool: - return self._supports_poetry + def _supports_poetry(self) -> bool: + return PyProjectTOML(self._full_path / "pyproject.toml").is_poetry_project() def is_directory(self) -> bool: return True diff --git a/src/poetry/core/packages/utils/utils.py b/src/poetry/core/packages/utils/utils.py index 2dba9037f..701cc1b5f 100644 --- a/src/poetry/core/packages/utils/utils.py +++ b/src/poetry/core/packages/utils/utils.py @@ -1,6 +1,6 @@ from __future__ import annotations -import os +import functools import posixpath import re import sys @@ -14,6 +14,7 @@ from urllib.parse import urlsplit from urllib.request import url2pathname +from poetry.core.pyproject.toml import PyProjectTOML from poetry.core.semver.version_range import VersionRange from poetry.core.version.markers import dnf @@ -119,14 +120,22 @@ def strip_extras(path: str) -> tuple[str, str | None]: return path_no_extras, extras -def is_installable_dir(path: str) -> bool: - """Return True if `path` is a directory containing a setup.py file.""" - if not os.path.isdir(path): +@functools.lru_cache(maxsize=None) +def is_python_project(path: Path) -> bool: + """Return true if the directory is a Python project""" + if not path.is_dir(): return False - setup_py = os.path.join(path, "setup.py") - if os.path.isfile(setup_py): - return True - return False + + setup_py = path / "setup.py" + setup_cfg = path / "setup.cfg" + setuptools_project = setup_py.exists() or setup_cfg.exists() + + pyproject = PyProjectTOML(path / "pyproject.toml") + + supports_pep517 = setuptools_project or pyproject.is_build_system_defined() + supports_poetry = pyproject.is_poetry_project() + + return supports_pep517 or supports_poetry def is_archive_file(name: str) -> bool: diff --git a/tests/packages/utils/test_utils.py b/tests/packages/utils/test_utils.py index d8bfcbf0c..6cd911b43 100644 --- a/tests/packages/utils/test_utils.py +++ b/tests/packages/utils/test_utils.py @@ -1,9 +1,12 @@ from __future__ import annotations +from pathlib import Path + import pytest from poetry.core.packages.utils.utils import convert_markers from poetry.core.packages.utils.utils import get_python_constraint_from_marker +from poetry.core.packages.utils.utils import is_python_project from poetry.core.semver.helpers import parse_constraint from poetry.core.version.markers import parse_marker @@ -132,3 +135,19 @@ def test_get_python_constraint_from_marker(marker: str, constraint: str) -> None marker_parsed = parse_marker(marker) constraint_parsed = parse_constraint(constraint) assert get_python_constraint_from_marker(marker_parsed) == constraint_parsed + + +@pytest.mark.parametrize( + ("fixture", "result"), + [ + ("simple_project", True), + ("project_with_setup_cfg_only", True), + ("project_with_setup", True), + ("project_with_pep517_non_poetry", True), + ("project_without_pep517", False), + ("does_not_exists", False), + ], +) +def test_package_utils_is_python_project(fixture: str, result: bool) -> None: + path = Path(__file__).parent.parent.parent / "fixtures" / fixture + assert is_python_project(path) == result From 5c8048076a7edf81fc2054e40e975919a29e492c Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Thu, 26 May 2022 19:46:35 +0200 Subject: [PATCH 2/2] Update tests/packages/utils/test_utils.py Co-authored-by: Bjorn Neergaard --- tests/packages/utils/test_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/packages/utils/test_utils.py b/tests/packages/utils/test_utils.py index 6cd911b43..032ccf4dc 100644 --- a/tests/packages/utils/test_utils.py +++ b/tests/packages/utils/test_utils.py @@ -145,7 +145,7 @@ def test_get_python_constraint_from_marker(marker: str, constraint: str) -> None ("project_with_setup", True), ("project_with_pep517_non_poetry", True), ("project_without_pep517", False), - ("does_not_exists", False), + ("does_not_exist", False), ], ) def test_package_utils_is_python_project(fixture: str, result: bool) -> None: