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()