diff --git a/docs/cli.md b/docs/cli.md index b71a3949646..3624d09d5db 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -213,6 +213,12 @@ poetry install --no-root Installation of your project's package is also skipped when the `--only` option is used. +If you do not want to install the current project in [editable](https://pip.pypa.io/en/stable/cli/pip_install/#install-editable) mode, run the `install` command with the `--no-editable` flag: + +```bash +poetry install --no-editable +``` + ### Options * `--without`: The dependency groups to ignore for installation. @@ -223,6 +229,7 @@ option is used. * `--no-root`: Do not install the root package (your project). * `--dry-run`: Output the operations but do not execute anything (implicitly enables --verbose). * `--extras (-E)`: Features to install (multiple values allowed). +* `--no-editable`: Do not install the root package in editable mode. * `--no-dev`: Do not install dev dependencies. (**Deprecated**) * `--dev-only`: Only install dev dependencies. (**Deprecated**) * `--remove-untracked`: Remove dependencies not presented in the lock file. (**Deprecated**) diff --git a/src/poetry/console/commands/install.py b/src/poetry/console/commands/install.py index c150e1bd203..669f7a7e5af 100644 --- a/src/poetry/console/commands/install.py +++ b/src/poetry/console/commands/install.py @@ -70,6 +70,12 @@ class InstallCommand(InstallerCommand): flag=False, multiple=True, ), + option( + "--no-editable", + None, + "Do not install the root package in editable mode.", + flag=True, + ), ] help = """The install command reads the poetry.lock file from @@ -89,9 +95,11 @@ class InstallCommand(InstallerCommand): _loggers = ["poetry.repositories.pypi_repository", "poetry.inspection.info"] def handle(self) -> int: + from poetry.core.masonry.builders.sdist import SdistBuilder from poetry.core.masonry.utils.module import ModuleOrPackageNotFound from poetry.masonry.builders import EditableBuilder + from poetry.utils.pip import pip_install self._installer.use_executor( self.poetry.config.get("experimental.new-installer", False) @@ -174,13 +182,17 @@ def handle(self) -> int: if self.option("no-root") or self.option("only"): return 0 - try: - builder = EditableBuilder(self.poetry, self._env, self._io) - except ModuleOrPackageNotFound: - # This is likely due to the fact that the project is an application - # not following the structure expected by Poetry - # If this is a true error it will be picked up later by build anyway. - return 0 + if self.option("no-editable"): + builder = SdistBuilder(self.poetry) + else: + try: + builder = EditableBuilder(self.poetry, self._env, self._io) + except ModuleOrPackageNotFound: + # This is likely due to the fact that the project is an + # application not following the structure expected by Poetry + # If this is a true error it will be picked up later by build + # anyway. + return 0 log_install = ( "Installing the current project:" @@ -197,7 +209,16 @@ def handle(self) -> int: self.line("") return 0 - builder.build() + if self.option("no-editable"): + with builder.setup_py(): + pip_install( + path=self.poetry.package.root_dir, + environment=self._env, + deps=False, + upgrade=True, + ) + else: + builder.build() if overwrite: self.overwrite(log_install.format(tag="success")) diff --git a/tests/console/commands/test_install.py b/tests/console/commands/test_install.py index 485207d5274..4d59e649c7c 100644 --- a/tests/console/commands/test_install.py +++ b/tests/console/commands/test_install.py @@ -41,3 +41,22 @@ def test_sync_option_is_passed_to_the_installer( tester.execute("--sync") assert tester.command.installer._requires_synchronization + + +def test_no_editable_option(tester: "CommandTester", mocker: "MockerFixture"): + mocker.patch.object(tester.command.installer, "run", return_value=0) + pip_install_mock = mocker.patch("poetry.utils.pip.pip_install") + builder_mock = mocker.patch("poetry.core.masonry.builders.sdist.SdistBuilder") + editable_builder_mock = mocker.patch("poetry.masonry.builders.EditableBuilder") + + tester.execute("--no-editable") + + pip_install_mock.assert_called_once_with( + path=tester.command.poetry.package.root_dir, + environment=tester.command.env, + deps=False, + upgrade=True, + ) + builder_mock.assert_called_once_with(tester.command.poetry) + builder_mock.return_value.setup_py.assert_called_once() + editable_builder_mock.assert_not_called()