From 2f39e51faea515cc7af9cf7eb7770bd85af28648 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Randy=20D=C3=B6ring?=
<30527984+radoering@users.noreply.github.com>
Date: Sat, 11 Nov 2023 19:32:27 +0100
Subject: [PATCH] non-package-mode: - metadata like `name` and `version` is not
required - the root package is never installed (same as `--no-root`) -
building and publishing is not possible
---
src/poetry/console/commands/build.py | 4 +++
src/poetry/console/commands/install.py | 7 +++--
src/poetry/console/commands/publish.py | 4 +++
src/poetry/factory.py | 4 +--
tests/console/commands/test_build.py | 16 +++++++++++
tests/console/commands/test_check.py | 28 +++++++++++++++----
tests/console/commands/test_install.py | 19 ++++++++++++-
tests/console/commands/test_publish.py | 19 +++++++++++++
.../fixtures/non_package_mode/pyproject.toml | 7 +++++
tests/test_factory.py | 6 ++++
10 files changed, 103 insertions(+), 11 deletions(-)
create mode 100644 tests/fixtures/non_package_mode/pyproject.toml
diff --git a/src/poetry/console/commands/build.py b/src/poetry/console/commands/build.py
index 5339a865200..ab0db1ffafd 100644
--- a/src/poetry/console/commands/build.py
+++ b/src/poetry/console/commands/build.py
@@ -23,6 +23,10 @@ class BuildCommand(EnvCommand):
def handle(self) -> int:
from poetry.core.masonry.builder import Builder
+ if not self.poetry.is_package_mode:
+ self.line_error("Building a package is not possible in non-package mode.")
+ return 1
+
with build_environment(poetry=self.poetry, env=self.env, io=self.io) as env:
fmt = self.option("format") or "all"
package = self.poetry.package
diff --git a/src/poetry/console/commands/install.py b/src/poetry/console/commands/install.py
index 8e716caa2b0..869ad09d341 100644
--- a/src/poetry/console/commands/install.py
+++ b/src/poetry/console/commands/install.py
@@ -152,7 +152,7 @@ def handle(self) -> int:
if return_code != 0:
return return_code
- if self.option("no-root"):
+ if self.option("no-root") or not self.poetry.is_package_mode:
return 0
log_install = (
@@ -186,7 +186,10 @@ def handle(self) -> int:
self.line_error(
f"The current project could not be installed: {e}\n"
"If you do not want to install the current project"
- " use --no-root",
+ " use --no-root.\n"
+ "If you want to use Poetry only for dependency management"
+ " and not for packaging, you may also try the non-package mode.\n"
+ "In a future version of Poetry this warning will become an error!",
style="warning",
)
return 0
diff --git a/src/poetry/console/commands/publish.py b/src/poetry/console/commands/publish.py
index a53df8208dd..54306fb4b1e 100644
--- a/src/poetry/console/commands/publish.py
+++ b/src/poetry/console/commands/publish.py
@@ -49,6 +49,10 @@ class PublishCommand(Command):
def handle(self) -> int:
from poetry.publishing.publisher import Publisher
+ if not self.poetry.is_package_mode:
+ self.line_error("Publishing a package is not possible in non-package mode.")
+ return 1
+
publisher = Publisher(self.poetry, self.io)
# Building package first, if told
diff --git a/src/poetry/factory.py b/src/poetry/factory.py
index 15bd004e747..7235ab1d6d1 100644
--- a/src/poetry/factory.py
+++ b/src/poetry/factory.py
@@ -370,9 +370,9 @@ def validate(
dependencies = {canonicalize_name(d) for d in dependencies}
- if canonicalize_name(config["name"]) in dependencies:
+ if (name := config.get("name")) and canonicalize_name(name) in dependencies:
results["errors"].append(
- f"Project name ({config['name']}) is same as one of its dependencies"
+ f"Project name ({name}) is same as one of its dependencies"
)
return results
diff --git a/tests/console/commands/test_build.py b/tests/console/commands/test_build.py
index ed9f3a3c5fa..135db0f064d 100644
--- a/tests/console/commands/test_build.py
+++ b/tests/console/commands/test_build.py
@@ -16,6 +16,22 @@
from tests.types import FixtureDirGetter
+def test_build_not_possible_in_non_package_mode(
+ fixture_dir: FixtureDirGetter,
+ command_tester_factory: CommandTesterFactory,
+) -> None:
+ source_dir = fixture_dir("non_package_mode")
+
+ poetry = Factory().create_poetry(source_dir)
+ tester = command_tester_factory("build", poetry)
+
+ assert tester.execute() == 1
+ assert (
+ tester.io.fetch_error()
+ == "Building a package is not possible in non-package mode.\n"
+ )
+
+
def test_build_with_multiple_readme_files(
fixture_dir: FixtureDirGetter,
tmp_path: Path,
diff --git a/tests/console/commands/test_check.py b/tests/console/commands/test_check.py
index e0d7ece440e..06d044fb785 100644
--- a/tests/console/commands/test_check.py
+++ b/tests/console/commands/test_check.py
@@ -5,6 +5,7 @@
import pytest
from poetry.packages import Locker
+from poetry.toml import TOMLFile
if TYPE_CHECKING:
@@ -67,8 +68,6 @@ def test_check_valid(tester: CommandTester) -> None:
def test_check_invalid(
mocker: MockerFixture, tester: CommandTester, fixture_dir: FixtureDirGetter
) -> None:
- from poetry.toml import TOMLFile
-
mocker.patch(
"poetry.poetry.Poetry.file",
return_value=TOMLFile(fixture_dir("invalid_pyproject") / "pyproject.toml"),
@@ -108,8 +107,27 @@ def test_check_private(
mocker: MockerFixture, tester: CommandTester, fixture_dir: FixtureDirGetter
) -> None:
mocker.patch(
- "poetry.factory.Factory.locate",
- return_value=fixture_dir("private_pyproject") / "pyproject.toml",
+ "poetry.poetry.Poetry.file",
+ return_value=TOMLFile(fixture_dir("private_pyproject") / "pyproject.toml"),
+ new_callable=mocker.PropertyMock,
+ )
+
+ tester.execute()
+
+ expected = """\
+All set!
+"""
+
+ assert tester.io.fetch_output() == expected
+
+
+def test_check_non_package_mode(
+ mocker: MockerFixture, tester: CommandTester, fixture_dir: FixtureDirGetter
+) -> None:
+ mocker.patch(
+ "poetry.poetry.Poetry.file",
+ return_value=TOMLFile(fixture_dir("non_package_mode") / "pyproject.toml"),
+ new_callable=mocker.PropertyMock,
)
tester.execute()
@@ -136,8 +154,6 @@ def test_check_lock_missing(
expected: str,
expected_status: int,
) -> None:
- from poetry.toml import TOMLFile
-
mocker.patch(
"poetry.poetry.Poetry.file",
return_value=TOMLFile(fixture_dir("private_pyproject") / "pyproject.toml"),
diff --git a/tests/console/commands/test_install.py b/tests/console/commands/test_install.py
index bf0789c8616..b1a1e1dca68 100644
--- a/tests/console/commands/test_install.py
+++ b/tests/console/commands/test_install.py
@@ -416,7 +416,7 @@ def test_install_logs_output_decorated(
assert tester.io.fetch_output() == expected
-@pytest.mark.parametrize("with_root", [True])
+@pytest.mark.parametrize("with_root", [True, False])
@pytest.mark.parametrize("error", ["module", "readme", ""])
def test_install_warning_corrupt_root(
command_tester_factory: CommandTesterFactory,
@@ -488,3 +488,20 @@ def test_install_missing_directory_dependency_with_no_directory(
else:
with pytest.raises(ValueError, match="does not exist"):
tester.execute(options)
+
+
+def test_non_package_mode_does_not_try_to_install_root(
+ command_tester_factory: CommandTesterFactory,
+ project_factory: ProjectFactory,
+) -> None:
+ content = """\
+[tool.poetry]
+mode = "non-package"
+"""
+ poetry = project_factory(name="non-package-mode", pyproject_content=content)
+
+ tester = command_tester_factory("install", poetry=poetry)
+ tester.execute()
+
+ assert tester.status_code == 0
+ assert tester.io.fetch_error() == ""
diff --git a/tests/console/commands/test_publish.py b/tests/console/commands/test_publish.py
index 5f230c01ff3..bdfea939609 100644
--- a/tests/console/commands/test_publish.py
+++ b/tests/console/commands/test_publish.py
@@ -7,6 +7,7 @@
import pytest
import requests
+from poetry.factory import Factory
from poetry.publishing.uploader import UploadError
@@ -17,6 +18,24 @@
from pytest_mock import MockerFixture
from tests.helpers import PoetryTestApplication
+ from tests.types import CommandTesterFactory
+ from tests.types import FixtureDirGetter
+
+
+def test_publish_not_possible_in_non_package_mode(
+ fixture_dir: FixtureDirGetter,
+ command_tester_factory: CommandTesterFactory,
+) -> None:
+ source_dir = fixture_dir("non_package_mode")
+
+ poetry = Factory().create_poetry(source_dir)
+ tester = command_tester_factory("publish", poetry)
+
+ assert tester.execute() == 1
+ assert (
+ tester.io.fetch_error()
+ == "Publishing a package is not possible in non-package mode.\n"
+ )
def test_publish_returns_non_zero_code_for_upload_errors(
diff --git a/tests/fixtures/non_package_mode/pyproject.toml b/tests/fixtures/non_package_mode/pyproject.toml
new file mode 100644
index 00000000000..5a5fbee0a1a
--- /dev/null
+++ b/tests/fixtures/non_package_mode/pyproject.toml
@@ -0,0 +1,7 @@
+[tool.poetry]
+mode = "non-package"
+
+[tool.poetry.dependencies]
+python = "^3.8"
+cleo = "^0.6"
+pendulum = { git = "https://github.com/sdispater/pendulum.git", branch = "2.0" }
diff --git a/tests/test_factory.py b/tests/test_factory.py
index 00f563082a4..dcb1e3d4733 100644
--- a/tests/test_factory.py
+++ b/tests/test_factory.py
@@ -224,6 +224,12 @@ def test_create_poetry_with_multi_constraints_dependency(
assert len(package.requires) == 2
+def test_create_poetry_non_package_mode(fixture_dir: FixtureDirGetter) -> None:
+ poetry = Factory().create_poetry(fixture_dir("non_package_mode"))
+
+ assert not poetry.is_package_mode
+
+
def test_poetry_with_default_source_legacy(
fixture_dir: FixtureDirGetter, with_simple_keyring: None
) -> None: