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