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
7 changes: 6 additions & 1 deletion src/poetry/console/commands/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class InitCommand(Command):
flag=False,
multiple=True,
),
option("disable-package-mode", "-N", "Disables package mode.", flag=True),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering about the short name. Does it stand for "non-package-mode"? If that is the case, should we name the option non-package-mode?

option("license", "l", "License of the package.", flag=False),
]

Expand Down Expand Up @@ -141,9 +142,12 @@ def _init_pyproject(

if is_interactive:
question = self.create_question(
f"Version [<comment>{version}</comment>]: ", default=version
f"Version [<comment>{version}</comment>]: ",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If package-mode is disabled, this now shows a version, which is not the default. We should probably set the version some lines above depending on the disable-package-mode option.

default=version if not self.option("disable-package-mode") else "",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Swap if/else branches of if expression to remove negation (swap-if-expression)

Suggested change
default=version if not self.option("disable-package-mode") else "",
default="" if self.option("disable-package-mode") else version,


ExplanationNegated conditions are more difficult to read than positive ones, so it is best
to avoid them where we can. By swapping the if and else conditions around we
can invert the condition and make it positive.

)
version = self.ask(question)
Comment on lines 143 to 148
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Setting version default to empty string may cause confusion in interactive mode.

Consider updating the prompt to clarify that versioning is skipped when package mode is disabled, to avoid user confusion.

Suggested change
if is_interactive:
question = self.create_question(
f"Version [<comment>{version}</comment>]: ", default=version
f"Version [<comment>{version}</comment>]: ",
default=version if not self.option("disable-package-mode") else "",
)
version = self.ask(question)
if is_interactive:
if self.option("disable-package-mode"):
question = self.create_question(
"Version (skipped, package mode disabled): ",
default="",
)
else:
question = self.create_question(
f"Version [<comment>{version}</comment>]: ",
default=version,
)
version = self.ask(question)

else:
version = version if not self.option("disable-package-mode") else ""
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Swap if/else branches of if expression to remove negation (swap-if-expression)

Suggested change
version = version if not self.option("disable-package-mode") else ""
version = "" if self.option("disable-package-mode") else version


ExplanationNegated conditions are more difficult to read than positive ones, so it is best
to avoid them where we can. By swapping the if and else conditions around we
can invert the condition and make it positive.


description = self.option("description") or ""
if not description and is_interactive:
Expand Down Expand Up @@ -244,6 +248,7 @@ def _init_pyproject(
python=python,
dependencies=requirements,
dev_dependencies=dev_requirements,
package_mode=not self.option("disable-package-mode"),
)

create_layout = not project_path.exists() or (
Expand Down
17 changes: 16 additions & 1 deletion src/poetry/layouts/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
[tool.poetry.group.dev.dependencies]
"""

POETRY_DEFAULT_AUTHOR = "Your Name <[email protected]>"

poetry_core_version = Version.parse(importlib.metadata.version("poetry-core"))

BUILD_SYSTEM_MIN_VERSION: str | None = Version.from_parts(
Expand All @@ -68,6 +70,7 @@ def __init__(
python: str | None = None,
dependencies: Mapping[str, str | Mapping[str, Any]] | None = None,
dev_dependencies: Mapping[str, str | Mapping[str, Any]] | None = None,
package_mode: bool = True,
) -> None:
self._project = canonicalize_name(project)
self._package_path_relative = Path(
Expand All @@ -83,9 +86,10 @@ def __init__(
self._python = python
self._dependencies = dependencies or {}
self._dev_dependencies = dev_dependencies or {}
self._package_mode = package_mode

if not author:
author = "Your Name <[email protected]>"
author = POETRY_DEFAULT_AUTHOR

self._author = author

Expand Down Expand Up @@ -152,6 +156,14 @@ def generate_project_content(self) -> TOMLDocument:
if email := m.group("email"):
author["email"] = email
project_content["authors"].append(author)
if self._author == POETRY_DEFAULT_AUTHOR and not self._package_mode:
project_content.remove("authors")

if not self._package_mode:
if not project_content["version"]:
project_content.remove("version")
if not project_content["description"]:
project_content.remove("description")

if self._license:
project_content["license"]["text"] = self._license
Expand Down Expand Up @@ -185,6 +197,9 @@ def generate_project_content(self) -> TOMLDocument:
else:
del poetry_content["group"]

if not self._package_mode:
poetry_content["package-mode"] = False

if not poetry_content:
del content["tool"]["poetry"]

Expand Down
105 changes: 105 additions & 0 deletions tests/console/commands/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -1207,3 +1207,108 @@ def test_init_does_not_create_project_structure_in_non_empty_directory(
# Existing files should remain
assert (source_dir / "existing_file.txt").exists()
assert (source_dir / "existing_dir").exists()


def test_init_with_no_package_mode_flag_active(
tester: CommandTester, source_dir: Path
) -> None:
Comment on lines +1211 to +1214
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Consider adding a test for interactive vs non-interactive mode with the new flag.

Please add a test for non-interactive mode to verify that the --disable-package-mode flag works correctly when all options are provided via CLI flags and no prompts are shown.

Suggested change
def test_init_with_no_package_mode_flag_active(
tester: CommandTester, source_dir: Path
) -> None:
def test_init_with_no_package_mode_flag_active_non_interactive(
tester: CommandTester, source_dir: Path
) -> None:
"""Test that poetry init with --disable-package-mode in non-interactive mode sets package-mode = false."""
# Provide all required options via CLI flags to avoid prompts
package_name = "my-noninteractive-package"
version = "1.2.3"
description = "A non-interactive test package"
author = "Test Author <[email protected]>"
license_ = "MIT"
python = "^3.8"
# Compose the command with all options and the --disable-package-mode flag
command = [
"--name", package_name,
"--version", version,
"--description", description,
"--author", author,
"--license", license_,
"--python", python,
"--disable-package-mode",
"--no-interaction",
]
tester.execute(" ".join(command))
expected = (
"[tool.poetry]\n"
"name = \"my-noninteractive-package\"\n"
"version = \"1.2.3\"\n"
"description = \"A non-interactive test package\"\n"
"authors = [\"Test Author <[email protected]>\"]\n"
"license = \"MIT\"\n"
"python = \"^3.8\"\n"
"package-mode = false\n"
)
pyproject_content = (source_dir / "pyproject.toml").read_text(encoding="utf-8")
assert (source_dir / "pyproject.toml").exists()
assert "package-mode = false" in pyproject_content
assert f'name = "{package_name}"' in pyproject_content
assert f'version = "{version}"' in pyproject_content
assert f'description = "{description}"' in pyproject_content
assert f'authors = ["{author}"]' in pyproject_content
assert f'license = "{license_}"' in pyproject_content
assert f'python = "{python}"' in pyproject_content
def test_init_with_no_package_mode_flag_active(
tester: CommandTester, source_dir: Path
) -> None:

"""Test that poetry init add package-mode = false to pyproject.toml"""
inputs = [
"my-package", # Package name
"", # Version
"", # Description
"n", # Author
"", # License
"", # Python
"n", # Interactive packages
"n", # Interactive dev packages
"\n", # Generate
]

expected = """\
[tool.poetry]
package-mode = false
"""
tester.execute("--disable-package-mode", inputs="\n".join(inputs))

assert (source_dir / "pyproject.toml").exists()
assert expected in (source_dir / "pyproject.toml").read_text(encoding="utf-8")


def test_init_with_no_package_mode_flag_active_short_flag(
tester: CommandTester, source_dir: Path
) -> None:
"""Test that poetry init add package-mode = false to pyproject.toml"""
inputs = [
"my-package", # Package name
"", # Version
"", # Description
"n", # Author
"", # License
"", # Python
"n", # Interactive packages
"n", # Interactive dev packages
"\n", # Generate
]

expected = """\
[tool.poetry]
package-mode = false
"""
tester.execute("-N", inputs="\n".join(inputs))

assert (source_dir / "pyproject.toml").exists()
assert expected in (source_dir / "pyproject.toml").read_text(encoding="utf-8")


def test_init_with_no_package_mode_flag_active_remove_optional_fields(
tester: CommandTester, source_dir: Path
) -> None:
"""Test that poetry init add package-mode = false to pyproject.toml"""
inputs = [
"my-package", # Package name
"", # Version
"", # Description
"n", # Author
"", # License
"", # Python
"n", # Interactive packages
"n", # Interactive dev packages
"\n", # Generate
]

tester.execute("-N", inputs="\n".join(inputs))

assert (source_dir / "pyproject.toml").exists()
toml_content = (source_dir / "pyproject.toml").read_text(encoding="utf-8")
assert "version = " not in toml_content
assert "description =" not in toml_content
assert "authors =" not in toml_content


def test_init_with_no_package_mode_flag_active_remove_optional_fields_with_email(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should test all fields that may be removed, not just authors but also version and description.

tester: CommandTester, source_dir: Path
) -> None:
"""Test that poetry init add package-mode = false to pyproject.toml"""
inputs = [
"my-package", # Package name
"", # Version
"", # Description
"poetry <[email protected]>", # Author
"", # License
"", # Python
"n", # Interactive packages
"n", # Interactive dev packages
"\n", # Generate
]

tester.execute("-N", inputs="\n".join(inputs))
expected = """
authors = [
{name = "poetry",email = "[email protected]"}
]
"""

assert (source_dir / "pyproject.toml").exists()
toml_content = (source_dir / "pyproject.toml").read_text(encoding="utf-8")
assert expected in toml_content