diff --git a/src/poetry/console/commands/init.py b/src/poetry/console/commands/init.py index 48ea6e206ed..5410ca557ef 100644 --- a/src/poetry/console/commands/init.py +++ b/src/poetry/console/commands/init.py @@ -39,7 +39,13 @@ class InitCommand(Command): options: ClassVar[list[Option]] = [ option("name", None, "Name of the package.", flag=False), option("description", None, "Description of the package.", flag=False), - option("author", None, "Author name of the package.", flag=False), + option( + "author", + None, + "Author name of the package.", + flag=False, + multiple=True, + ), option("python", None, "Compatible Python versions.", flag=False), option( "dependency", @@ -149,20 +155,77 @@ def _init_pyproject( description = self.ask(self.create_question("Description []: ", default="")) author = self.option("author") + author_from_cli = bool(author) if not author and vcs_config.get("user.name"): - author = vcs_config["user.name"] + author = [vcs_config["user.name"]] author_email = vcs_config.get("user.email") if author_email: - author += f" <{author_email}>" + author[0] += f" <{author_email}>" + elif not author: + author = [] if is_interactive: - question = self.create_question( - f"Author [{author}, n to skip]: ", default=author - ) - question.set_validator(lambda v: self._validate_author(v, author)) - author = self.ask(question) + # When --author was provided via CLI, use it as default but still prompt + # so the user can confirm or override. Do not ask for additional authors + # since the user already expressed their intent via the CLI flag. + if author_from_cli: + default_author = author[0] if author else "" + question = self.create_question( + f"Author [{default_author}, n to skip]: ", + default=default_author, + ) + question.set_validator( + lambda v: self._validate_author(v, default_author) + ) + first_author = self.ask(question) + if first_author: + author = [first_author] + else: + author = [] + else: + # No --author from CLI: interactive flow allows multiple authors + default_author = author[0] if author else "" + question = self.create_question( + f"Author [{default_author}, n to skip]: ", + default=default_author, + ) + question.set_validator( + lambda v: self._validate_author(v, default_author) + ) + first_author = self.ask(question) + if first_author: + author = [first_author] + else: + author = [] - authors = [author] if author else [] + # Allow additional authors interactively + while first_author: + question = self.create_question( + "Add another author? [n to skip]: ", + default="n", + ) + another = self.ask(question) + if not another or another.lower() == "n": + break + question = self.create_question( + "Author [, n to skip]: ", + default="", + ) + question.set_validator( + lambda v: self._validate_author(v, "") + ) + additional = self.ask(question) + if additional: + author.append(additional) + else: + break + + # Validate all authors from CLI + authors = [] + for a in author: + validated = self._validate_author(a, a) + if validated: + authors.append(validated) license_name = self.option("license") if not license_name and is_interactive: @@ -237,7 +300,7 @@ def _init_pyproject( name, version, description=description, - author=authors[0] if authors else None, + authors=authors, readme_format=readme_format, license=license_name, python=python, diff --git a/src/poetry/layouts/layout.py b/src/poetry/layouts/layout.py index 768b3737191..bf4e4d1be4a 100644 --- a/src/poetry/layouts/layout.py +++ b/src/poetry/layouts/layout.py @@ -66,6 +66,8 @@ def __init__( description: str = "", readme_format: str = "md", author: str | None = None, + # TODO: Deprecate `author` in a future major version; use `authors` only. + authors: list[str] | None = None, license: str | None = None, python: str | None = None, dependencies: Mapping[str, str | Mapping[str, Any]] | None = None, @@ -86,10 +88,12 @@ def __init__( self._dependencies = dependencies or {} self._dev_dependencies = dev_dependencies or {} - if not author: - author = "Your Name " - - self._author = author + if authors: + self._authors = authors + elif author: + self._authors = [author] + else: + self._authors = ["Your Name "] @property def basedir(self) -> Path: @@ -147,15 +151,16 @@ def generate_project_content( project_content["name"] = self._project project_content["version"] = self._version project_content["description"] = self._description - m = AUTHOR_REGEX.match(self._author) - if m is None: - # This should not happen because author has been validated before. - raise ValueError(f"Invalid author: {self._author}") - else: - author = {"name": m.group("name")} - if email := m.group("email"): - author["email"] = email - project_content["authors"].append(author) + for author_str in self._authors: + m = AUTHOR_REGEX.match(author_str) + if m is None: + # This should not happen because author has been validated before. + raise ValueError(f"Invalid author: {author_str}") + else: + author = {"name": m.group("name")} + if email := m.group("email"): + author["email"] = email + project_content["authors"].append(author) if self._license: project_content["license"] = self._license diff --git a/tests/console/commands/test_init.py b/tests/console/commands/test_init.py index d4904ecd4af..668315ed5bb 100644 --- a/tests/console/commands/test_init.py +++ b/tests/console/commands/test_init.py @@ -1215,3 +1215,29 @@ def test_init_does_not_add_readme_key_when_readme_missing( # Assert pyproject = (tmp_path / "pyproject.toml").read_text(encoding="utf-8") assert "readme =" not in pyproject + + +def test_multiple_authors_via_cli(tester: CommandTester, source_dir: Path) -> None: + """Test that multiple --author flags are supported in non-interactive mode.""" + tester.execute( + "--name my-package " + "--author 'Alice ' " + "--author 'Bob '", + interactive=False, + ) + + pyproject = (source_dir / "pyproject.toml").read_text(encoding="utf-8") + assert '{name = "Alice",email = "alice@example.com"}' in pyproject + assert '{name = "Bob",email = "bob@example.com"}' in pyproject + + +def test_single_author_via_cli_unchanged(tester: CommandTester, source_dir: Path) -> None: + """Test that single --author behavior is unchanged.""" + tester.execute( + "--name my-package " + "--author 'Alice '", + interactive=False, + ) + + pyproject = (source_dir / "pyproject.toml").read_text(encoding="utf-8") + assert '{name = "Alice",email = "alice@example.com"}' in pyproject