diff --git a/poetry/console/commands/add.py b/poetry/console/commands/add.py index 89a6483ba44..3ad77ad686b 100644 --- a/poetry/console/commands/add.py +++ b/poetry/console/commands/add.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +from typing import Dict +from typing import List + from cleo import argument from cleo import option @@ -86,18 +90,19 @@ def handle(self): if section not in poetry_content: poetry_content[section] = {} - for name in packages: - for key in poetry_content[section]: - if key.lower() == name.lower(): - pair = self._parse_requirements([name])[0] - if ( - "git" in pair - or "url" in pair - or pair.get("version") == "latest" - ): - continue + existing_packages = self.get_existing_packages_from_input( + packages, poetry_content, section + ) - raise ValueError("Package {} is already present".format(name)) + if existing_packages: + self.notify_about_existing_packages(existing_packages) + + packages = [name for name in packages if name not in existing_packages] + + if not packages: + self.poetry.file.write(content) + self.line("Nothing to add.") + return 0 requirements = self._determine_requirements( packages, @@ -184,3 +189,28 @@ def handle(self): self.poetry.file.write(original_content) return status + + def get_existing_packages_from_input( + self, packages, poetry_content, target_section + ): # type: (List[str], Dict, str) -> List[str] + existing_packages = [] + + for name in packages: + for key in poetry_content[target_section]: + if key.lower() == name.lower(): + existing_packages.append(name) + + return existing_packages + + def notify_about_existing_packages( + self, existing_packages + ): # type: (List[str]) -> None + self.line( + "The following packages are already present in the pyproject.toml and will be skipped:\n" + ) + for name in existing_packages: + self.line(" • {name}".format(name=name)) + self.line( + "\nIf you want to update it to the latest compatible version, you can use `poetry update package`.\n" + "If you prefer to upgrade it to the latest available version, you can use `poetry add package@latest`.\n" + ) diff --git a/tests/console/commands/test_add.py b/tests/console/commands/test_add.py index 3a0f3400403..af2e093cd04 100644 --- a/tests/console/commands/test_add.py +++ b/tests/console/commands/test_add.py @@ -714,7 +714,7 @@ def test_add_should_not_select_prereleases(app, repo, tester): assert content["dependencies"]["pyyaml"] == "^3.13" -def test_add_should_display_an_error_when_adding_existing_package_with_no_constraint( +def test_add_should_skip_when_adding_existing_package_with_no_constraint( app, repo, tester ): content = app.poetry.file.read() @@ -722,11 +722,18 @@ def test_add_should_display_an_error_when_adding_existing_package_with_no_constr app.poetry.file.write(content) repo.add_package(get_package("foo", "1.1.2")) + tester.execute("foo") - with pytest.raises(ValueError) as e: - tester.execute("foo") + expected = """\ +The following packages are already present in the pyproject.toml and will be skipped: + + • foo - assert "Package foo is already present" == str(e.value) +If you want to update it to the latest compatible version, you can use `poetry update package`. +If you prefer to upgrade it to the latest available version, you can use `poetry add package@latest`. +""" + + assert expected in tester.io.fetch_output() def test_add_should_work_when_adding_existing_package_with_latest_constraint( @@ -1527,7 +1534,7 @@ def test_add_should_not_select_prereleases_old_installer( assert content["dependencies"]["pyyaml"] == "^3.13" -def test_add_should_display_an_error_when_adding_existing_package_with_no_constraint_old_installer( +def test_add_should_skip_when_adding_existing_package_with_no_constraint_old_installer( app, repo, installer, old_tester ): content = app.poetry.file.read() @@ -1536,10 +1543,18 @@ def test_add_should_display_an_error_when_adding_existing_package_with_no_constr repo.add_package(get_package("foo", "1.1.2")) - with pytest.raises(ValueError) as e: - old_tester.execute("foo") + old_tester.execute("foo") + + expected = """\ +The following packages are already present in the pyproject.toml and will be skipped: + + • foo - assert "Package foo is already present" == str(e.value) +If you want to update it to the latest compatible version, you can use `poetry update package`. +If you prefer to upgrade it to the latest available version, you can use `poetry add package@latest`. +""" + + assert expected in old_tester.io.fetch_output() def test_add_should_work_when_adding_existing_package_with_latest_constraint_old_installer(