diff --git a/src/poetry/console/commands/init.py b/src/poetry/console/commands/init.py index 1897829a4a4..5501624c22f 100644 --- a/src/poetry/console/commands/init.py +++ b/src/poetry/console/commands/init.py @@ -118,12 +118,9 @@ def handle(self) -> int: ) version = self.ask(question) - description = self.option("description") or "" - question = self.create_question( - f"Description [{description}]: ", - default=description, - ) - description = self.ask(question) + description = self.option("description") + if not description: + description = self.ask(self.create_question("Description []: ", default="")) author = self.option("author") if not author and vcs_config.get("user.name"): @@ -143,13 +140,9 @@ def handle(self) -> int: else: authors = [author] - license = self.option("license") or "" - - question = self.create_question( - f"License [{license}]: ", default=license - ) - question.set_validator(self._validate_license) - license = self.ask(question) + license = self.option("license") + if not license: + license = self.ask(self.create_question("License []: ", default="")) python = self.option("python") if not python: @@ -175,7 +168,7 @@ def handle(self) -> int: question = "Would you like to define your main dependencies interactively?" help_message = """\ You can specify a package in the following forms: - - A single name (requests) + - A single name (requests): this will search for matches on PyPI - A name and a constraint (requests@^2.23.0) - A git url (git+https://github.com/python-poetry/poetry.git) - A git url with a revision\ @@ -272,9 +265,12 @@ def _determine_requirements( if not requires: result = [] - package = self.ask( - "Search for package to add (or leave blank to continue):" + question = self.create_question( + "Package to add or search for (leave blank to skip):" ) + question.set_validator(self._validate_package) + + package = self.ask(question) while package: constraint = self._parse_requirements([package])[0] if ( @@ -457,13 +453,12 @@ def _validate_author(self, author: str, default: str) -> str | None: return author - def _validate_license(self, license: str) -> str: - from poetry.core.spdx.helpers import license_by_id - - if license: - license_by_id(license) + @staticmethod + def _validate_package(package: str | None) -> str | None: + if package and len(package.split()) > 2: + raise ValueError("Invalid package definition.") - return license + return package def _get_pool(self) -> Pool: from poetry.repositories import Pool diff --git a/tests/console/commands/test_init.py b/tests/console/commands/test_init.py index c733bba2dfb..b43c03bc760 100644 --- a/tests/console/commands/test_init.py +++ b/tests/console/commands/test_init.py @@ -11,6 +11,7 @@ from cleo.testers.command_tester import CommandTester +from poetry.console.commands.init import InitCommand from poetry.repositories import Pool from poetry.utils._compat import decode from poetry.utils.helpers import canonicalize_name @@ -587,6 +588,56 @@ def test_interactive_with_file_dependency( assert expected in tester.io.fetch_output() +def test_interactive_with_wrong_dependency_inputs( + tester: CommandTester, repo: TestRepository +): + repo.add_package(get_package("pendulum", "2.0.0")) + repo.add_package(get_package("pytest", "3.6.0")) + + inputs = [ + "my-package", # Package name + "1.2.3", # Version + "This is a description", # Description + "n", # Author + "MIT", # License + "^3.8", # Python + "", # Interactive packages + "pendulum 2.0.0 foo", # Package name and constraint (invalid) + "pendulum 2.0.0", # Package name and constraint (invalid) + "pendulum 2.0.0", # Package name and constraint (invalid) + "pendulum 2.0.0", # Package name and constraint (invalid) + "pendulum@^2.0.0", # Package name and constraint (valid) + "", # End package selection + "", # Interactive dev packages + "pytest 3.6.0 foo", # Dev package name and constraint (invalid) + "pytest 3.6.0", # Dev package name and constraint (invalid) + "pytest@3.6.0", # Dev package name and constraint (valid) + "", # End package selection + "\n", # Generate + ] + tester.execute(inputs="\n".join(inputs)) + + expected = """\ +[tool.poetry] +name = "my-package" +version = "1.2.3" +description = "This is a description" +authors = ["Your Name "] +license = "MIT" +readme = "README.md" +packages = [{include = "my_package"}] + +[tool.poetry.dependencies] +python = "^3.8" +pendulum = "^2.0.0" + +[tool.poetry.group.dev.dependencies] +pytest = "3.6.0" +""" + + assert expected in tester.io.fetch_output() + + def test_python_option(tester: CommandTester): inputs = [ "my-package", # Package name @@ -779,6 +830,51 @@ def test_predefined_and_interactive_dev_dependencies( assert 'pytest = "^3.6.0"' in output +def test_predefined_all_options(tester: CommandTester, repo: TestRepository): + repo.add_package(get_package("pendulum", "2.0.0")) + repo.add_package(get_package("pytest", "3.6.0")) + + inputs = [ + "1.2.3", # Version + "", # Author + "n", # Interactive packages + "n", # Interactive dev packages + "\n", # Generate + ] + + tester.execute( + "--name my-package " + "--description 'This is a description' " + "--author 'Foo Bar ' " + "--python '^3.8' " + "--license MIT " + "--dependency pendulum " + "--dev-dependency pytest", + inputs="\n".join(inputs), + ) + + expected = """\ +[tool.poetry] +name = "my-package" +version = "1.2.3" +description = "This is a description" +authors = ["Foo Bar "] +license = "MIT" +readme = "README.md" +packages = [{include = "my_package"}] + +[tool.poetry.dependencies] +python = "^3.8" +pendulum = "^2.0.0" + +[tool.poetry.group.dev.dependencies] +pytest = "^3.6.0" +""" + + output = tester.io.fetch_output() + assert expected in output + + def test_add_package_with_extras_and_whitespace(tester: CommandTester): result = tester.command._parse_requirements(["databases[postgresql, sqlite]"]) @@ -861,3 +957,29 @@ def test_init_existing_pyproject_with_build_system_fails( == "A pyproject.toml file with a defined build-system already exists." ) assert existing_section in pyproject_file.read_text() + + +@pytest.mark.parametrize( + "name", + [ + None, + "", + "foo", + " foo ", + "foo==2.0", + "foo@2.0", + " foo@2.0 ", + "foo 2.0", + " foo 2.0 ", + ], +) +def test__validate_package_valid(name: str | None): + assert InitCommand._validate_package(name) == name + + +@pytest.mark.parametrize( + "name", ["foo bar 2.0", " foo bar 2.0 ", "foo bar foobar 2.0"] +) +def test__validate_package_invalid(name: str): + with pytest.raises(ValueError): + assert InitCommand._validate_package(name)