Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 73 additions & 10 deletions src/poetry/console/commands/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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 [<comment>{author}</comment>, 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 [<comment>{default_author}</comment>, 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 [<comment>{default_author}</comment>, 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? [<comment>n</comment> to skip]: ",
default="n",
)
another = self.ask(question)
if not another or another.lower() == "n":
break
question = self.create_question(
"Author [<comment></comment>, 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:
Expand Down Expand Up @@ -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,
Expand Down
31 changes: 18 additions & 13 deletions src/poetry/layouts/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -86,10 +88,12 @@ def __init__(
self._dependencies = dependencies or {}
self._dev_dependencies = dev_dependencies or {}

if not author:
author = "Your Name <you@example.com>"

self._author = author
if authors:
self._authors = authors
elif author:
self._authors = [author]
else:
self._authors = ["Your Name <you@example.com>"]

@property
def basedir(self) -> Path:
Expand Down Expand Up @@ -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
Expand Down
26 changes: 26 additions & 0 deletions tests/console/commands/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 <alice@example.com>' "
"--author 'Bob <bob@example.com>'",
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 <alice@example.com>'",
interactive=False,
)

pyproject = (source_dir / "pyproject.toml").read_text(encoding="utf-8")
assert '{name = "Alice",email = "alice@example.com"}' in pyproject
Loading