From 2b6f82e9494b99bdb04d0dac90f87eb23548e4a5 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 13 Oct 2020 02:53:56 +0200 Subject: [PATCH] locker: ensure correct handling of extras export Previously, when determining nested dependencies, the check for activated extras/features of top level dependencies were done after the nested dependencies were processed. This lead to exports containing in active extras. This change resolves this by pre-selecting top level packages prior to identifying nested dependencies. --- poetry/packages/locker.py | 22 ++++++++++++++++------ tests/utils/test_exporter.py | 27 +++++++++++++++------------ 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/poetry/packages/locker.py b/poetry/packages/locker.py index 8eccacd0c97..50074a4417a 100644 --- a/poetry/packages/locker.py +++ b/poetry/packages/locker.py @@ -318,20 +318,30 @@ def get_project_dependency_packages( ) ) - for dependency in self.get_project_dependencies( - project_requires=project_requires, - locked_packages=repository.packages, - with_nested=True, - ): + # If a package is optional and we haven't opted in to it, do not select + selected = [] + for dependency in project_requires: try: package = repository.find_packages(dependency=dependency)[0] except IndexError: continue - # If a package is optional and we haven't opted in to it, continue if extra_package_names is not None and ( package.optional and package.name not in extra_package_names ): + # a package is locked as optional, but is not activated via extras + continue + + selected.append(dependency) + + for dependency in self.get_project_dependencies( + project_requires=selected, + locked_packages=repository.packages, + with_nested=True, + ): + try: + package = repository.find_packages(dependency=dependency)[0] + except IndexError: continue for extra in dependency.extras: diff --git a/tests/utils/test_exporter.py b/tests/utils/test_exporter.py index 66b9e81ff53..0cf6f9a202e 100644 --- a/tests/utils/test_exporter.py +++ b/tests/utils/test_exporter.py @@ -544,8 +544,17 @@ def test_exporter_exports_requirements_txt_without_optional_packages(tmp_dir, po assert expected == content -def test_exporter_exports_requirements_txt_with_optional_packages_if_opted_in( - tmp_dir, poetry +@pytest.mark.parametrize( + "extras,lines", + [ + (None, ["foo==1.2.3"]), + (False, ["foo==1.2.3"]), + (True, ["bar==4.5.6", "foo==1.2.3", "spam==0.1.0"]), + (["feature_bar"], ["bar==4.5.6", "foo==1.2.3", "spam==0.1.0"]), + ], +) +def test_exporter_exports_requirements_txt_with_optional_packages( + tmp_dir, poetry, extras, lines ): poetry.locker.mock_lock_data( { @@ -590,22 +599,16 @@ def test_exporter_exports_requirements_txt_with_optional_packages_if_opted_in( Path(tmp_dir), "requirements.txt", dev=True, - extras=["feature_bar"], + with_hashes=False, + extras=extras, ) with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: content = f.read() - expected = """\ -bar==4.5.6 \\ - --hash=sha256:67890 -foo==1.2.3 \\ - --hash=sha256:12345 -spam==0.1.0 \\ - --hash=sha256:abcde -""" + expected = "\n".join(lines) - assert expected == content + assert content.strip() == expected def test_exporter_can_export_requirements_txt_with_git_packages(tmp_dir, poetry):