Skip to content

Commit d4c5aeb

Browse files
committed
feat: add validation for import-names and import-namespaces to prevent duplicates
1 parent e9fc036 commit d4c5aeb

2 files changed

Lines changed: 77 additions & 0 deletions

File tree

src/poetry/core/factory.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,25 @@ def _configure_package_metadata(
326326
package.import_names = project.get("import-names")
327327
package.import_namespaces = project.get("import-namespaces")
328328

329+
@classmethod
330+
def _validate_import_names(
331+
cls, project: dict[str, Any], result: dict[str, list[str]]
332+
) -> None:
333+
if "import-namespaces" in project and len(project["import-namespaces"]) == 0:
334+
result["errors"].append("import-namespaces must not be an empty array.")
335+
336+
import_names = {
337+
name.split(";")[0].strip() for name in project.get("import-names", [])
338+
}
339+
import_namespaces = {
340+
name.split(";")[0].strip() for name in project.get("import-namespaces", [])
341+
}
342+
343+
if duplicates := import_names & import_namespaces:
344+
result["errors"].append(
345+
f"Import names found in both import-names and import-namespaces: {', '.join(duplicates)}"
346+
)
347+
329348
@classmethod
330349
def _configure_entry_points(
331350
cls,
@@ -733,6 +752,8 @@ def validate(
733752
for e in validate_object(project, "project-schema")
734753
]
735754
result["errors"] += project_validation_errors
755+
cls._validate_import_names(project, result)
756+
736757
# With PEP 621 [tool.poetry] is not mandatory anymore. We still create and
737758
# validate it so that default values (e.g. for package-mode) are set.
738759
tool_poetry = toml_data.setdefault("tool", {}).setdefault("poetry", {})

tests/test_factory.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,62 @@ def test_create_poetry_with_import_names() -> None:
335335
assert package.import_namespaces == ["my_package"]
336336

337337

338+
@pytest.mark.parametrize(
339+
["namespace_name", "import_name"],
340+
[
341+
("my_package", "my_package"),
342+
("my_package; private", "my_package"),
343+
("my_package ; private", "my_package"),
344+
("my_package; private", "my_package; private"),
345+
],
346+
)
347+
def test_create_poetry_raise_on_name_in_import_names_and_spaces(
348+
namespace_name: str, import_name: str, tmp_path: Path
349+
) -> None:
350+
content = f"""
351+
[project]
352+
name = "my-package"
353+
version = "1.2.3"
354+
description = "Some description."
355+
requires-python = ">=3.6"
356+
357+
import-namespaces = ["{namespace_name}", "another_package"]
358+
import-names = ["{import_name}"]
359+
360+
dependencies = []
361+
"""
362+
pyproject = tmp_path / "pyproject.toml"
363+
pyproject.write_text(content)
364+
365+
with pytest.raises(
366+
RuntimeError,
367+
match="Import names found in both import-names and import-namespaces: my_package",
368+
):
369+
_ = Factory().create_poetry(pyproject)
370+
371+
372+
def test_create_poetry_raise_on_empty_import_namespaces(tmp_path: Path) -> None:
373+
content = """
374+
[project]
375+
name = "my-package"
376+
version = "1.2.3"
377+
description = "Some description."
378+
requires-python = ">=3.6"
379+
380+
import-namespaces = []
381+
382+
dependencies = []
383+
"""
384+
pyproject = tmp_path / "pyproject.toml"
385+
pyproject.write_text(content)
386+
387+
with pytest.raises(
388+
RuntimeError,
389+
match="import-namespaces must not be an empty array",
390+
):
391+
_ = Factory().create_poetry(pyproject)
392+
393+
338394
@pytest.mark.parametrize(
339395
"project", ["sample_project_with_groups", "sample_project_with_groups_new"]
340396
)

0 commit comments

Comments
 (0)