From f61fa9f433a666ac41e33a93b0dda8c6b24b2cec Mon Sep 17 00:00:00 2001 From: Brecht Machiels Date: Fri, 16 Sep 2022 22:01:15 +0200 Subject: [PATCH 1/2] Handle circular dependency on root package (fix #118) --- src/poetry_plugin_export/exporter.py | 1 + src/poetry_plugin_export/walker.py | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/src/poetry_plugin_export/exporter.py b/src/poetry_plugin_export/exporter.py index 8eda1dc..2cd3d7b 100644 --- a/src/poetry_plugin_export/exporter.py +++ b/src/poetry_plugin_export/exporter.py @@ -95,6 +95,7 @@ def _export_generic_txt( for dependency_package in get_project_dependency_packages( self._poetry.locker, project_requires=root.all_requires, + root_package_name=root.name, project_python_marker=root.python_marker, extras=self._extras, ): diff --git a/src/poetry_plugin_export/walker.py b/src/poetry_plugin_export/walker.py index 4d295d7..d75927e 100644 --- a/src/poetry_plugin_export/walker.py +++ b/src/poetry_plugin_export/walker.py @@ -52,6 +52,7 @@ def get_python_version_region_markers(packages: list[Package]) -> list[BaseMarke def get_project_dependency_packages( locker: Locker, project_requires: list[Dependency], + root_package_name: NormalizedName, project_python_marker: BaseMarker | None = None, extras: Collection[NormalizedName] = (), ) -> Iterator[DependencyPackage]: @@ -96,6 +97,7 @@ def get_project_dependency_packages( for package, dependency in get_project_dependencies( project_requires=selected, locked_packages=repository.packages, + root_package_name=root_package_name, ): yield DependencyPackage(dependency=dependency, package=package) @@ -103,6 +105,7 @@ def get_project_dependency_packages( def get_project_dependencies( project_requires: list[Dependency], locked_packages: list[Package], + root_package_name: NormalizedName, ) -> Iterable[tuple[Package, Dependency]]: # group packages entries by name, this is required because requirement might use # different constraints. @@ -122,6 +125,7 @@ def get_project_dependencies( nested_dependencies = walk_dependencies( dependencies=project_requires, packages_by_name=packages_by_name, + root_package_name=root_package_name, ) return nested_dependencies.items() @@ -130,6 +134,7 @@ def get_project_dependencies( def walk_dependencies( dependencies: list[Dependency], packages_by_name: dict[str, list[Package]], + root_package_name: NormalizedName, ) -> dict[Package, Dependency]: nested_dependencies: dict[Package, Dependency] = {} @@ -138,6 +143,8 @@ def walk_dependencies( requirement = dependencies.pop(0) if (requirement, requirement.marker) in visited: continue + if requirement.name == root_package_name: + continue visited.add((requirement, requirement.marker)) locked_package = get_locked_package( From 86afed1c38285c6268492c0dfcc6e7f9677d551d Mon Sep 17 00:00:00 2001 From: Brecht Machiels Date: Wed, 7 Dec 2022 12:27:19 +0100 Subject: [PATCH 2/2] Test case for circular dependency on root package --- tests/test_exporter.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/test_exporter.py b/tests/test_exporter.py index ed9dc4d..a0afd85 100644 --- a/tests/test_exporter.py +++ b/tests/test_exporter.py @@ -1115,6 +1115,43 @@ def test_exporter_can_export_requirements_txt_with_nested_packages_cyclic( assert content == expected +def test_exporter_can_export_requirements_txt_with_circular_root_dependency( + tmp_path: Path, poetry: Poetry +) -> None: + poetry.locker.mock_lock_data( # type: ignore[attr-defined] + { + "package": [ + { + "name": "foo", + "version": "1.2.3", + "category": "main", + "optional": False, + "python-versions": "*", + "dependencies": {poetry.package.pretty_name: {"version": "1.2.3"}}, + }, + ], + "metadata": { + "python-versions": "*", + "content-hash": "123456789", + "files": {"foo": []}, + }, + } + ) + set_package_requires(poetry) + + exporter = Exporter(poetry, NullIO()) + exporter.export("requirements.txt", tmp_path, "requirements.txt") + + with (tmp_path / "requirements.txt").open(encoding="utf-8") as f: + content = f.read() + + expected = f"""\ +foo==1.2.3 ; {MARKER_PY} +""" + + assert content == expected + + def test_exporter_can_export_requirements_txt_with_nested_packages_and_multiple_markers( tmp_path: Path, poetry: Poetry ) -> None: