diff --git a/src/poetry/core/factory.py b/src/poetry/core/factory.py index 8c369f4a8..d119229c8 100644 --- a/src/poetry/core/factory.py +++ b/src/poetry/core/factory.py @@ -12,6 +12,7 @@ from packaging.utils import canonicalize_name from poetry.core.utils.helpers import combine_unicode +from poetry.core.utils.helpers import module_name from poetry.core.utils.helpers import readme_content_type @@ -50,6 +51,10 @@ def create_poetry( # Checking validity check_result = self.validate(pyproject.data) + src_check_results = self.validate_project_in_src(pyproject.data, cwd) + check_result["errors"].extend(src_check_results["errors"]) + check_result["warnings"].extend(src_check_results["warnings"]) + if check_result["errors"]: message = "" for error in check_result["errors"]: @@ -582,6 +587,47 @@ def validate( return result + @classmethod + def validate_project_in_src( + cls, toml_data: dict[str, Any], base_path: Path | None + ) -> dict[str, list[str]]: + """ + Checks the validity of projects located under src/ subdirectory. + """ + result: dict[str, list[str]] = {"errors": [], "warnings": []} + + project_name = toml_data.get("project", {}).get("name") or toml_data.get( + "tool", {} + ).get("poetry", {}).get("name") + + if project_name is None or base_path is None: + return result + + project_name = module_name(project_name) + + project_dir = base_path / project_name + src_project_dir = base_path / "src" / project_name + + project_dir_empty = ( + project_dir.exists() + and project_dir.is_dir() + and not any(project_dir.iterdir()) + ) + src_dir_not_empty = ( + src_project_dir.exists() + and src_project_dir.is_dir() + and any(src_project_dir.iterdir()) + ) + + if project_dir_empty and src_dir_not_empty: + result["warnings"].append( + f"Found empty directory '{project_name}' in project root while the actual package is in " + f"'src/{project_name}'. This may cause issues with package installation. " + "Consider removing the empty directory." + ) + + return result + @classmethod def _validate_legacy_vs_project( cls, toml_data: dict[str, Any], result: dict[str, list[str]] diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/fixtures/project_with_src_folder/pyproject.toml b/tests/fixtures/project_with_src_folder/pyproject.toml new file mode 100644 index 000000000..4fefde218 --- /dev/null +++ b/tests/fixtures/project_with_src_folder/pyproject.toml @@ -0,0 +1,9 @@ +[tool.poetry] +name = "project-with-src-folder" +version = "1.2.3" +description = "This is a description" +authors = ["Your Name "] +license = "MIT" + +[tool.poetry.dependencies] +python = "*" diff --git a/tests/fixtures/project_with_src_folder/src/project_with_src_folder/__init__.py b/tests/fixtures/project_with_src_folder/src/project_with_src_folder/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/test_factory.py b/tests/test_factory.py index 5da4a1aaf..b6b2c925f 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -533,6 +533,25 @@ def test_create_poetry_empty_readme(tmp_path: Path) -> None: assert not poetry.package.readmes +def test_create_poetry_src_folder(tmp_path: Path) -> None: + cwd = fixtures_dir / "project_with_src_folder" + + (cwd / "project_with_src_folder").mkdir(exist_ok=True) + + pyproject = cwd / "pyproject.toml" + with pyproject.open("rb") as f: + content = tomllib.load(f) + + assert Factory.validate_project_in_src(content, cwd) == { + "errors": [], + "warnings": [ + "Found empty directory 'project_with_src_folder' in project root while the actual package is in " + "'src/project_with_src_folder'. This may cause issues with package installation. " + "Consider removing the empty directory." + ], + } + + def test_validate() -> None: complete = fixtures_dir / "complete.toml" with complete.open("rb") as f: