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
8 changes: 4 additions & 4 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -465,9 +465,9 @@ poetry init

* `--name`: Name of the package.
* `--description`: Description of the package.
* `--author`: Author of the package.
* `--author`: Author of the package. Can be specified multiple times.
* `--python` Compatible Python versions.
* `--dependency`: Package to require with a version constraint. Should be in format `foo:1.0.0`.
* `--dependency`: Package to require with a version constraint. Should be in the format `foo:1.0.0`.
* `--dev-dependency`: Development requirements, see `--dependency`.

## install
Expand Down Expand Up @@ -717,9 +717,9 @@ my-package
keep the [recommendations for a PyPI-friendly README](https://packaging.python.org/en/latest/guides/making-a-pypi-friendly-readme/)
in mind.
* `--description`: Description of the package.
* `--author`: Author of the package.
* `--author`: Author of the package. Can be specified multiple times.
* `--python` Compatible Python versions.
* `--dependency`: Package to require with a version constraint. Should be in format `foo:1.0.0`.
* `--dependency`: Package to require with a version constraint. Should be in the format `foo:1.0.0`.
* `--dev-dependency`: Development requirements, see `--dependency`.

## publish
Expand Down
37 changes: 25 additions & 12 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. Can be specified multiple times.",
flag=False,
multiple=True,
),
option("python", None, "Compatible Python versions.", flag=False),
option(
"dependency",
Expand Down Expand Up @@ -148,21 +154,28 @@ def _init_pyproject(
if not description and is_interactive:
description = self.ask(self.create_question("Description []: ", default=""))

author = self.option("author")
if not author and vcs_config.get("user.name"):
author = vcs_config["user.name"]
provided_authors = self.option("author")
default_author = provided_authors[0] if provided_authors else None
if not default_author and vcs_config.get("user.name"):
default_author = vcs_config["user.name"]
author_email = vcs_config.get("user.email")
if author_email:
author += f" <{author_email}>"
default_author += f" <{author_email}>"

if is_interactive:
if is_interactive and len(provided_authors) < 2:
question = self.create_question(
f"Author [<comment>{author}</comment>, n to skip]: ", default=author
f"Author [<comment>{default_author}</comment>, n to skip]: ",
default=default_author,
)
question.set_validator(lambda v: self._validate_author(v, author))
author = self.ask(question)

authors = [author] if author else []
question.set_validator(
lambda v: self._validate_author(v, default_author or "")
)
selected_author = self.ask(question)
authors = [selected_author] if selected_author else []
else:
authors = list(provided_authors) if provided_authors else []
if not authors and default_author:
authors = [default_author]

license_name = self.option("license")
if not license_name and is_interactive:
Expand Down Expand Up @@ -237,7 +250,7 @@ def _init_pyproject(
name,
version,
description=description,
author=authors[0] if authors else None,
authors=authors or None,
readme_format=readme_format,
license=license_name,
python=python,
Expand Down
26 changes: 14 additions & 12 deletions src/poetry/layouts/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

if TYPE_CHECKING:
from collections.abc import Mapping
from collections.abc import Sequence

from tomlkit.items import InlineTable
from tomlkit.toml_document import TOMLDocument
Expand Down Expand Up @@ -66,6 +67,7 @@ def __init__(
description: str = "",
readme_format: str = "md",
author: str | None = None,
authors: Sequence[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,9 @@ def __init__(
self._dependencies = dependencies or {}
self._dev_dependencies = dev_dependencies or {}

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

self._author = author
self._authors = list(authors or ([author] if author else []))
if not self._authors:
self._authors = ["Your Name <you@example.com>"]

@property
def basedir(self) -> Path:
Expand Down Expand Up @@ -147,15 +148,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")}
for author in self._authors:
m = AUTHOR_REGEX.match(author)
if m is None:
# This should not happen because authors are validated before.
raise ValueError(f"Invalid author: {author}")

author_content = {"name": m.group("name")}
if email := m.group("email"):
author["email"] = email
project_content["authors"].append(author)
author_content["email"] = email
project_content["authors"].append(author_content)

if self._license:
project_content["license"] = self._license
Expand Down
19 changes: 19 additions & 0 deletions tests/console/commands/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from poetry.console.application import Application
from poetry.console.commands.init import InitCommand
from poetry.repositories import RepositoryPool
from poetry.toml import TOMLFile
from tests.helpers import get_package


Expand Down Expand Up @@ -879,6 +880,24 @@ def test_predefined_all_options(tester: CommandTester, repo: DummyRepository) ->
assert expected in output


def test_noninteractive_multiple_authors(
tester: CommandTester, source_dir: Path
) -> None:
tester.execute(
"--name my-package "
"--author 'Foo Bar <foo@example.com>' "
"--author 'Baz Qux <baz@example.com>'",
interactive=False,
)

Comment thread
sourcery-ai[bot] marked this conversation as resolved.
pyproject_file = source_dir / "pyproject.toml"
authors = TOMLFile(pyproject_file).read()["project"]["authors"]
assert [dict(author) for author in authors] == [
{"name": "Foo Bar", "email": "foo@example.com"},
{"name": "Baz Qux", "email": "baz@example.com"},
]


def test_add_package_with_extras_and_whitespace(tester: CommandTester) -> None:
command = tester.command
assert isinstance(command, InitCommand)
Expand Down
18 changes: 18 additions & 0 deletions tests/console/commands/test_new.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from poetry.core.utils.helpers import module_name

from poetry.factory import Factory
from poetry.toml import TOMLFile


if TYPE_CHECKING:
Expand Down Expand Up @@ -199,6 +200,23 @@ def test_command_new_with_readme(
assert project_section["readme"] == readme_file.name


def test_command_new_multiple_authors(tester: CommandTester, tmp_path: Path) -> None:
path = tmp_path / "package"

tester.execute(
"--author 'Foo Bar <foo@example.com>' "
"--author 'Baz Qux <baz@example.com>' "
f"{path.as_posix()}"
)

pyproject_file = path / "pyproject.toml"
authors = TOMLFile(pyproject_file).read()["project"]["authors"]
assert [dict(author) for author in authors] == [
{"name": "Foo Bar", "email": "foo@example.com"},
{"name": "Baz Qux", "email": "baz@example.com"},
]


@pytest.mark.parametrize(
["use_poetry_python", "python"],
[
Expand Down
Loading