diff --git a/docs/cli.md b/docs/cli.md
index 69476c852b5..65f085a814b 100644
--- a/docs/cli.md
+++ b/docs/cli.md
@@ -190,11 +190,13 @@ poetry install --only dev
```
You can also specify the extras you want installed
-by passing the `-E|--extras` option (See [Extras]({{< relref "pyproject#extras" >}}) for more info)
+by passing the `-E|--extras` option (See [Extras]({{< relref "pyproject#extras" >}}) for more info).
+Pass `--all-extras` to install all defined extras for a project.
```bash
poetry install --extras "mysql pgsql"
poetry install -E mysql -E pgsql
+poetry install --all-extras
```
By default `poetry` will install your project's package every time you run `install`:
@@ -227,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).
+* `--all-extras`: Install all extra features (conflicts with --extras).
* `--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/docs/pyproject.md b/docs/pyproject.md
index 7c5cc90d3af..e1d526aea02 100644
--- a/docs/pyproject.md
+++ b/docs/pyproject.md
@@ -384,6 +384,12 @@ poetry install --extras "mysql pgsql"
poetry install -E mysql -E pgsql
```
+Or, you can install all extras with the `--all-extras` option:
+
+```bash
+poetry install --all-extras
+```
+
When installing or specifying Poetry-built packages, the extras defined in this section can be activated
as described in [PEP 508](https://www.python.org/dev/peps/pep-0508/#extras).
diff --git a/src/poetry/console/commands/install.py b/src/poetry/console/commands/install.py
index a7389bf28b8..e2776d4e004 100644
--- a/src/poetry/console/commands/install.py
+++ b/src/poetry/console/commands/install.py
@@ -52,6 +52,7 @@ class InstallCommand(InstallerCommand):
flag=False,
multiple=True,
),
+ option("all-extras", None, "Install all extra dependencies."),
]
help = """The install command reads the poetry.lock> file from
@@ -79,12 +80,23 @@ def handle(self) -> int:
self.poetry.config.get("experimental.new-installer", False)
)
- extras = []
- for extra in self.option("extras", []):
- if " " in extra:
- extras += [e.strip() for e in extra.split(" ")]
- else:
- extras.append(extra)
+ if self.option("extras") and self.option("all-extras"):
+ self.line_error(
+ "You cannot specify explicit"
+ " `--extras>` while installing"
+ " using `--all-extras>`."
+ )
+ return 1
+
+ if self.option("all-extras"):
+ extras = list(self.poetry.package.extras.keys())
+ else:
+ extras = []
+ for extra in self.option("extras", []):
+ if " " in extra:
+ extras += [e.strip() for e in extra.split(" ")]
+ else:
+ extras.append(extra)
self._installer.extras(extras)
diff --git a/tests/console/commands/test_install.py b/tests/console/commands/test_install.py
index 027b3de2f92..2be8a4a5e3c 100644
--- a/tests/console/commands/test_install.py
+++ b/tests/console/commands/test_install.py
@@ -30,6 +30,8 @@
[tool.poetry.dependencies]
python = "~2.7 || ^3.4"
+fizz = { version = "^1.0", optional = true }
+buzz = { version = "^2.0", optional = true }
[tool.poetry.group.foo.dependencies]
foo = "^1.0"
@@ -48,6 +50,10 @@
[tool.poetry.group.bam.dependencies]
bam = "^1.4"
+
+[tool.poetry.extras]
+extras_a = [ "fizz" ]
+extras_b = [ "buzz" ]
"""
@@ -132,3 +138,43 @@ def test_sync_option_is_passed_to_the_installer(
tester.execute("--sync")
assert tester.command.installer._requires_synchronization
+
+
+def test_no_all_extras_doesnt_populate_installer(
+ tester: CommandTester, mocker: MockerFixture
+):
+ """
+ Not passing --all-extras means the installer doesn't see any extras.
+ """
+ mocker.patch.object(tester.command.installer, "run", return_value=1)
+
+ tester.execute()
+
+ assert not tester.command.installer._extras
+
+
+def test_all_extras_populates_installer(tester: CommandTester, mocker: MockerFixture):
+ """
+ The --all-extras option results in extras passed to the installer.
+ """
+ mocker.patch.object(tester.command.installer, "run", return_value=1)
+
+ tester.execute("--all-extras")
+
+ assert tester.command.installer._extras == ["extras_a", "extras_b"]
+
+
+def test_extras_conlicts_all_extras(tester: CommandTester, mocker: MockerFixture):
+ """
+ The --extras doesn't make sense with --all-extras.
+ """
+ mocker.patch.object(tester.command.installer, "run", return_value=0)
+
+ tester.execute("--extras foo --all-extras")
+
+ assert tester.status_code == 1
+ assert (
+ tester.io.fetch_error()
+ == "You cannot specify explicit `--extras` while installing using"
+ " `--all-extras`.\n"
+ )