diff --git a/docs/docs/managing-environments.md b/docs/docs/managing-environments.md
index 8b626cd813a..8b6094c6d46 100644
--- a/docs/docs/managing-environments.md
+++ b/docs/docs/managing-environments.md
@@ -119,3 +119,5 @@ poetry env remove test-O3eWbxRl-py3.7
```
If you remove the currently activated virtual environment, it will be automatically deactivated.
+
+You can delete all virtual environments at once using `env remove --all`.
\ No newline at end of file
diff --git a/poetry/console/commands/env/remove.py b/poetry/console/commands/env/remove.py
index 5f208851deb..56c3139ee2a 100644
--- a/poetry/console/commands/env/remove.py
+++ b/poetry/console/commands/env/remove.py
@@ -1,4 +1,6 @@
from cleo import argument
+from cleo import option
+from clikit.api.args.exceptions import CannotParseArgsException
from ..command import Command
@@ -9,13 +11,31 @@ class EnvRemoveCommand(Command):
description = "Removes a specific virtualenv associated with the project."
arguments = [
- argument("python", "The python executable to remove the virtualenv for.")
+ argument(
+ "python",
+ "The python executable to remove the virtualenv for.",
+ optional=True,
+ )
]
+ options = [option("all", None, "Remove of virtualenvs of this project.")]
+
def handle(self):
from poetry.utils.env import EnvManager
manager = EnvManager(self.poetry)
- venv = manager.remove(self.argument("python"))
-
- self.line("Deleted virtualenv: {}".format(venv.path))
+ if self.option("all"):
+ for venv in manager.list():
+ manager.remove(venv.path.name)
+ self.line("Deleted virtualenv: {}".format(venv.path))
+ else:
+ # simulate a non-optional argument - it needs to be optional for --all to work
+ # but required for deleting individual environments
+ if self.argument("python") is None:
+ raise CannotParseArgsException(
+ 'Not enough arguments (missing: "python").'
+ )
+
+ venv = manager.remove(self.argument("python"))
+
+ self.line("Deleted virtualenv: {}".format(venv.path))
diff --git a/tests/console/commands/env/test_remove.py b/tests/console/commands/env/test_remove.py
index bf3c3a6c4fb..c102468315c 100644
--- a/tests/console/commands/env/test_remove.py
+++ b/tests/console/commands/env/test_remove.py
@@ -1,4 +1,6 @@
from cleo.testers import CommandTester
+from clikit.api.args.exceptions import CannotParseArgsException
+from pytest import raises
from poetry.utils._compat import Path
from poetry.utils.env import EnvManager
@@ -55,3 +57,43 @@ def test_remove_by_name(app, tmp_dir):
)
assert expected == tester.io.fetch_output()
+
+
+def test_remove_no_python(app, tmp_dir):
+ # additional test for the "required optional argument"
+ app.poetry.config.merge({"virtualenvs": {"path": str(tmp_dir)}})
+
+ venv_name = EnvManager.generate_env_name(
+ "simple-project", str(app.poetry.file.parent)
+ )
+ (Path(tmp_dir) / "{}-py3.7".format(venv_name)).mkdir()
+ (Path(tmp_dir) / "{}-py3.6".format(venv_name)).mkdir()
+
+ command = app.find("env remove")
+ tester = CommandTester(command)
+ with raises(CannotParseArgsException):
+ tester.execute()
+
+
+def test_remove_all(app, tmp_dir):
+ app.poetry.config.merge({"virtualenvs": {"path": str(tmp_dir)}})
+
+ venv_name = EnvManager.generate_env_name(
+ "simple-project", str(app.poetry.file.parent)
+ )
+ (Path(tmp_dir) / "{}-py3.7".format(venv_name)).mkdir()
+ (Path(tmp_dir) / "{}-py3.6".format(venv_name)).mkdir()
+
+ command = app.find("env remove")
+ tester = CommandTester(command)
+ tester.execute("--all")
+
+ assert not (Path(tmp_dir) / "{}-py3.6".format(venv_name)).exists()
+ assert not (Path(tmp_dir) / "{}-py3.7".format(venv_name)).exists()
+
+ expected = "Deleted virtualenv: {}\nDeleted virtualenv: {}\n".format(
+ (Path(tmp_dir) / "{}-py3.6".format(venv_name)),
+ (Path(tmp_dir) / "{}-py3.7".format(venv_name)),
+ )
+
+ assert expected == tester.io.fetch_output()