diff --git a/docs/managing-environments.md b/docs/managing-environments.md index a663c806397..be89afc7c80 100644 --- a/docs/managing-environments.md +++ b/docs/managing-environments.md @@ -160,3 +160,12 @@ poetry env remove --all ``` If you remove the currently activated virtual environment, it will be automatically deactivated. + +{{% note %}} +If you use the [`virtualenvs.in-project`]({{< relref "configuration#virtualenvsin-project" >}}) configuration, you +can simply use the command as shown below. + +```bash +poetry env remove +``` +{{% /note %}} diff --git a/src/poetry/console/commands/env/remove.py b/src/poetry/console/commands/env/remove.py index 4371ec4f84b..af6957ffa68 100644 --- a/src/poetry/console/commands/env/remove.py +++ b/src/poetry/console/commands/env/remove.py @@ -7,6 +7,7 @@ from cleo.helpers import option from poetry.console.commands.command import Command +from poetry.utils.helpers import is_relative_to if TYPE_CHECKING: @@ -39,9 +40,12 @@ class EnvRemoveCommand(Command): def handle(self) -> int: from poetry.utils.env import EnvManager + is_in_project = self.poetry.config.get("virtualenvs.in-project") + pythons = self.argument("python") - all = self.option("all") - if not (pythons or all): + remove_all_envs = self.option("all") + + if not (pythons or remove_all_envs or is_in_project): self.line("No virtualenv provided.") manager = EnvManager(self.poetry) @@ -49,14 +53,17 @@ def handle(self) -> int: for python in pythons: venv = manager.remove(python) self.line(f"Deleted virtualenv: {venv.path}") - if all: + if remove_all_envs or is_in_project: for venv in manager.list(): - manager.remove_venv(venv.path) - self.line(f"Deleted virtualenv: {venv.path}") + if not is_in_project or is_relative_to( + venv.path, self.poetry.pyproject_path.parent + ): + manager.remove_venv(venv.path) + self.line(f"Deleted virtualenv: {venv.path}") # Since we remove all the virtualenvs, we can also remove the entry # in the envs file. (Strictly speaking, we should do this explicitly, # in case it points to a virtualenv that had been removed manually before.) - if manager.envs_file.exists(): + if remove_all_envs and manager.envs_file.exists(): manager.envs_file.remove_section(manager.base_env_name) return 0 diff --git a/src/poetry/utils/helpers.py b/src/poetry/utils/helpers.py index fbc29e5b4dd..b59cec82f65 100644 --- a/src/poetry/utils/helpers.py +++ b/src/poetry/utils/helpers.py @@ -371,3 +371,13 @@ def extractall(source: Path, dest: Path, zip: bool) -> None: archive.extractall(dest, filter="data") else: archive.extractall(dest) + + +def is_relative_to(this: Path, other: Path) -> bool: + """ + Return whether `this` path is relative to the `other` path. + """ + with suppress(ValueError): + this.relative_to(other) + return True + return False diff --git a/tests/console/commands/env/conftest.py b/tests/console/commands/env/conftest.py index f118b5f05f1..7fb3b1502a3 100644 --- a/tests/console/commands/env/conftest.py +++ b/tests/console/commands/env/conftest.py @@ -65,7 +65,8 @@ def venvs_in_project_dir(app: PoetryTestApplication) -> Iterator[Path]: try: yield venv_dir finally: - venv_dir.rmdir() + if venv_dir.exists(): + venv_dir.rmdir() @pytest.fixture diff --git a/tests/console/commands/env/test_remove.py b/tests/console/commands/env/test_remove.py index 38998f92634..8b2163369f3 100644 --- a/tests/console/commands/env/test_remove.py +++ b/tests/console/commands/env/test_remove.py @@ -137,3 +137,27 @@ def test_remove_multiple( for name in remaining_envs: assert (venv_cache / name).exists() assert set(tester.io.fetch_output().split("\n")) == expected + + +def test_remove_in_project(tester: CommandTester, venvs_in_project_dir: Path) -> None: + assert venvs_in_project_dir.exists() + + tester.execute() + + assert not venvs_in_project_dir.exists() + + expected = f"Deleted virtualenv: {venvs_in_project_dir}\n" + assert tester.io.fetch_output() == expected + + +def test_remove_in_project_all( + tester: CommandTester, venvs_in_project_dir: Path +) -> None: + assert venvs_in_project_dir.exists() + + tester.execute("--all") + + assert not venvs_in_project_dir.exists() + + expected = f"Deleted virtualenv: {venvs_in_project_dir}\n" + assert tester.io.fetch_output() == expected