From 1faf6116e189b9ca3fc9a47b763c593dfbbaf26b Mon Sep 17 00:00:00 2001 From: KotlinIsland Date: Sun, 28 Aug 2022 19:58:07 +1000 Subject: [PATCH 1/7] Handle multiple extras and invalid extras --- src/poetry_plugin_export/command.py | 12 +++++++++- tests/command/test_command_export.py | 36 ++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/poetry_plugin_export/command.py b/src/poetry_plugin_export/command.py index 5119eb0..2b2353a 100644 --- a/src/poetry_plugin_export/command.py +++ b/src/poetry_plugin_export/command.py @@ -82,9 +82,19 @@ def handle(self) -> None: "" ) + # Checking extras + extras = { + extra for extra_opt in self.option("extras") for extra in extra_opt.split() + } + invalid_extras = extras - self.poetry.package.extras.keys() + if invalid_extras: + raise ValueError( + f"Extra [{', '.join(sorted(invalid_extras))}] is not specified." + ) + exporter = Exporter(self.poetry) exporter.only_groups(list(self.activated_groups)) - exporter.with_extras(self.option("extras")) + exporter.with_extras(list(extras)) exporter.with_hashes(not self.option("without-hashes")) exporter.with_credentials(self.option("with-credentials")) exporter.with_urls(not self.option("without-urls")) diff --git a/tests/command/test_command_export.py b/tests/command/test_command_export.py index a6c44c2..0460adf 100644 --- a/tests/command/test_command_export.py +++ b/tests/command/test_command_export.py @@ -50,6 +50,7 @@ python = "~2.7 || ^3.6" foo = "^1.0" bar = { version = "^1.1", optional = true } +qux = { version = "^1.2", optional = true } [tool.poetry.group.dev.dependencies] baz = "^2.0" @@ -63,6 +64,7 @@ [tool.poetry.extras] feature_bar = ["bar"] +feature_qux = ["qux"] """ @@ -72,6 +74,7 @@ def setup(repo: Repository) -> None: repo.add_package(Package("bar", "1.1.0")) repo.add_package(Package("baz", "2.0.0")) repo.add_package(Package("opt", "2.2.0")) + repo.add_package(Package("qux", "1.2.0")) @pytest.fixture @@ -174,15 +177,40 @@ def test_export_groups( assert tester.io.fetch_output() == expected -def test_export_includes_extras_by_flag(tester: CommandTester, do_lock: None) -> None: - tester.execute("--format requirements.txt --extras feature_bar") - expected = f"""\ +@pytest.mark.parametrize( + "extras, expected", + [ + ( + "feature_bar", + f"""\ bar==1.1.0 ; {MARKER_PY} foo==1.0.0 ; {MARKER_PY} -""" +""", + ), + ( + "feature_bar feature_qux", + f"""\ +bar==1.1.0 ; {MARKER_PY} +foo==1.0.0 ; {MARKER_PY} +qux==1.2.0 ; {MARKER_PY} +""", + ), + ], +) +def test_export_includes_extras_by_flag( + tester: CommandTester, do_lock: None, extras: str, expected: str +) -> None: + tester.execute(f"--format requirements.txt --extras '{extras}'") assert tester.io.fetch_output() == expected +def test_export_reports_invalid_extras(tester: CommandTester, do_lock: None) -> None: + with pytest.raises(ValueError) as error: + tester.execute("--format requirements.txt --extras 'SUS AMONGUS'") + expected = "Extra [AMONGUS, SUS] is not specified." + assert str(error.value) == expected + + def test_export_with_urls( monkeypatch: MonkeyPatch, tester: CommandTester, poetry: Poetry ) -> None: From 842689105864620a21e43aa44cba34716b479158 Mon Sep 17 00:00:00 2001 From: KotlinIsland Date: Mon, 29 Aug 2022 17:36:04 +1000 Subject: [PATCH 2/7] add 'all' and 'all-extras' options --- src/poetry_plugin_export/command.py | 25 ++++++++++++++++++++++++- tests/command/test_command_export.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/poetry_plugin_export/command.py b/src/poetry_plugin_export/command.py index 2b2353a..d23faca 100644 --- a/src/poetry_plugin_export/command.py +++ b/src/poetry_plugin_export/command.py @@ -31,6 +31,16 @@ class ExportCommand(InstallerCommand): None, "Include development dependencies. (Deprecated)", ), + option( + "all", + None, + "Include all groups and extras", + ), + option( + "all-extras", + None, + "Include all extras", + ), *InstallerCommand._group_dependency_options(), option( "extras", @@ -82,6 +92,8 @@ def handle(self) -> None: "" ) + groups = self.activated_groups + # Checking extras extras = { extra for extra_opt in self.option("extras") for extra in extra_opt.split() @@ -92,8 +104,19 @@ def handle(self) -> None: f"Extra [{', '.join(sorted(invalid_extras))}] is not specified." ) + # handle 'all' + if self.option("all"): + extras = set(self.poetry.package.extras.keys()) + groups = self.poetry.package.dependency_group_names(include_optional=True) + + # handle 'all-extras' + if self.option("all-extras"): + if self.option("extras"): + raise ValueError("Can't have --all-extras and --extras together.") + extras = set(self.poetry.package.extras.keys()) + exporter = Exporter(self.poetry) - exporter.only_groups(list(self.activated_groups)) + exporter.only_groups(list(groups)) exporter.with_extras(list(extras)) exporter.with_hashes(not self.option("without-hashes")) exporter.with_credentials(self.option("with-credentials")) diff --git a/tests/command/test_command_export.py b/tests/command/test_command_export.py index 0460adf..87524f2 100644 --- a/tests/command/test_command_export.py +++ b/tests/command/test_command_export.py @@ -211,6 +211,34 @@ def test_export_reports_invalid_extras(tester: CommandTester, do_lock: None) -> assert str(error.value) == expected +def test_all_option(tester: CommandTester, do_lock: None) -> None: + tester.execute("--all") + expected = f"""\ +bar==1.1.0 ; {MARKER_PY} +baz==2.0.0 ; {MARKER_PY} +foo==1.0.0 ; {MARKER_PY} +opt==2.2.0 ; {MARKER_PY} +qux==1.2.0 ; {MARKER_PY} +""" + assert tester.io.fetch_output() == expected + + +def test_all_extras_option(tester: CommandTester, do_lock: None) -> None: + tester.execute("--all-extras") + expected = f"""\ +bar==1.1.0 ; {MARKER_PY} +foo==1.0.0 ; {MARKER_PY} +qux==1.2.0 ; {MARKER_PY} +""" + assert tester.io.fetch_output() == expected + + +def test_all_extras_and_extras(tester: CommandTester, do_lock: None) -> None: + with pytest.raises(ValueError) as error: + tester.execute("--all-extras --extras 'feature_bar'") + assert str(error.value) == "Can't have --all-extras and --extras together." + + def test_export_with_urls( monkeypatch: MonkeyPatch, tester: CommandTester, poetry: Poetry ) -> None: From af26af6275d607827d082df0bd7e23c09f0fa4f9 Mon Sep 17 00:00:00 2001 From: Jordan Wolinsky Date: Fri, 16 Aug 2024 16:29:47 -0400 Subject: [PATCH 3/7] cleanup --- src/poetry_plugin_export/command.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/poetry_plugin_export/command.py b/src/poetry_plugin_export/command.py index f2ba536..9b78855 100644 --- a/src/poetry_plugin_export/command.py +++ b/src/poetry_plugin_export/command.py @@ -1,21 +1,14 @@ from __future__ import annotations from pathlib import Path -from typing import TYPE_CHECKING from cleo.helpers import option -from packaging.utils import NormalizedName -from packaging.utils import canonicalize_name from poetry.console.commands.group_command import GroupCommand from poetry.core.packages.dependency_group import MAIN_GROUP from poetry_plugin_export.exporter import Exporter -if TYPE_CHECKING: - from collections.abc import Iterable - - class ExportCommand(GroupCommand): name = "export" description = "Exports the lock file to alternative formats." @@ -41,7 +34,6 @@ class ExportCommand(GroupCommand): None, "Include development dependencies. (Deprecated)", ), -<<<<<<< HEAD option( "all", None, @@ -52,10 +44,7 @@ class ExportCommand(GroupCommand): None, "Include all extras", ), - *InstallerCommand._group_dependency_options(), -======= *GroupCommand._group_dependency_options(), ->>>>>>> c672530630d79c4c9f29dd54bca28f21a88dfff9 option( "extras", "E", From a8c2fe47ee3d2555cf80c5bd2c8208594b203d87 Mon Sep 17 00:00:00 2001 From: Jordan Wolinsky Date: Fri, 16 Aug 2024 16:47:56 -0400 Subject: [PATCH 4/7] all groups option for peotry export --- src/poetry_plugin_export/command.py | 72 ++++++++++++++++------------ tests/command/test_command_export.py | 52 +++++++++++--------- 2 files changed, 71 insertions(+), 53 deletions(-) diff --git a/src/poetry_plugin_export/command.py b/src/poetry_plugin_export/command.py index 9b78855..e8e1c84 100644 --- a/src/poetry_plugin_export/command.py +++ b/src/poetry_plugin_export/command.py @@ -1,14 +1,21 @@ from __future__ import annotations from pathlib import Path +from typing import TYPE_CHECKING from cleo.helpers import option +from packaging.utils import NormalizedName +from packaging.utils import canonicalize_name from poetry.console.commands.group_command import GroupCommand from poetry.core.packages.dependency_group import MAIN_GROUP from poetry_plugin_export.exporter import Exporter +if TYPE_CHECKING: + from collections.abc import Iterable + + class ExportCommand(GroupCommand): name = "export" description = "Exports the lock file to alternative formats." @@ -34,16 +41,8 @@ class ExportCommand(GroupCommand): None, "Include development dependencies. (Deprecated)", ), - option( - "all", - None, - "Include all groups and extras", - ), - option( - "all-extras", - None, - "Include all extras", - ), + option("all-groups", None, "Include all groups"), + option("all-extras", None, "Include all extras"), *GroupCommand._group_dependency_options(), option( "extras", @@ -52,7 +51,6 @@ class ExportCommand(GroupCommand): flag=False, multiple=True, ), - option("all-extras", None, "Include all sets of extra dependencies."), option("with-credentials", None, "Include credentials for extra indices."), ] @@ -95,28 +93,42 @@ def handle(self) -> int: "" ) - groups = self.activated_groups - - # Checking extras - extras = { - extra for extra_opt in self.option("extras") for extra in extra_opt.split() - } - invalid_extras = extras - self.poetry.package.extras.keys() - if invalid_extras: - raise ValueError( - f"Extra [{', '.join(sorted(invalid_extras))}] is not specified." + if self.option("extras") and self.option("all-extras"): + self.line_error( + "You cannot specify explicit" + " `--extras` while exporting" + " using `--all-extras`." ) + return 1 - # handle 'all' - if self.option("all"): - extras = set(self.poetry.package.extras.keys()) - groups = self.poetry.package.dependency_group_names(include_optional=True) - - # handle 'all-extras' + extras: Iterable[NormalizedName] if self.option("all-extras"): - if self.option("extras"): - raise ValueError("Can't have --all-extras and --extras together.") - extras = set(self.poetry.package.extras.keys()) + extras = self.poetry.package.extras.keys() + else: + extras = { + canonicalize_name(extra) + for extra_opt in self.option("extras") + for extra in extra_opt.split() + } + invalid_extras = extras - self.poetry.package.extras.keys() + if invalid_extras: + raise ValueError( + f"Extra [{', '.join(sorted(invalid_extras))}] is not specified." + ) + + if self.option("with") and self.option("all-groups"): + self.line_error( + "You cannot specify explicit" + " `--with` while exporting" + " using `--all-groups`." + ) + return 1 + + groups = ( + self.poetry.package.dependency_group_names(include_optional=True) + if self.option("all-groups") + else self.activated_groups + ) exporter = Exporter(self.poetry, self.io) exporter.only_groups(list(groups)) diff --git a/tests/command/test_command_export.py b/tests/command/test_command_export.py index df0ee76..c39af80 100644 --- a/tests/command/test_command_export.py +++ b/tests/command/test_command_export.py @@ -216,36 +216,42 @@ def test_export_includes_extras_by_flag( def test_export_reports_invalid_extras(tester: CommandTester, do_lock: None) -> None: with pytest.raises(ValueError) as error: tester.execute("--format requirements.txt --extras 'SUS AMONGUS'") - expected = "Extra [AMONGUS, SUS] is not specified." + expected = "Extra [amongus, sus] is not specified." assert str(error.value) == expected -def test_all_option(tester: CommandTester, do_lock: None) -> None: - tester.execute("--all") - expected = f"""\ -bar==1.1.0 ; {MARKER_PY} -baz==2.0.0 ; {MARKER_PY} -foo==1.0.0 ; {MARKER_PY} -opt==2.2.0 ; {MARKER_PY} -qux==1.2.0 ; {MARKER_PY} -""" - assert tester.io.fetch_output() == expected +def test_export_with_all_extras(tester: CommandTester, do_lock: None) -> None: + tester.execute("--format requirements.txt --all-extras") + output = tester.io.fetch_output() + assert f"bar==1.1.0 ; {MARKER_PY}" in output + assert f"qux==1.2.0 ; {MARKER_PY}" in output -def test_all_extras_option(tester: CommandTester, do_lock: None) -> None: - tester.execute("--all-extras") - expected = f"""\ -bar==1.1.0 ; {MARKER_PY} -foo==1.0.0 ; {MARKER_PY} -qux==1.2.0 ; {MARKER_PY} -""" - assert tester.io.fetch_output() == expected +def test_extras_conflicts_all_extras(tester: CommandTester, do_lock: None) -> None: + tester.execute("--extras bar --all-extras") + assert tester.status_code == 1 + assert ( + "You cannot specify explicit `--extras` while exporting using `--all-extras`.\n" + in tester.io.fetch_error() + ) -def test_all_extras_and_extras(tester: CommandTester, do_lock: None) -> None: - with pytest.raises(ValueError) as error: - tester.execute("--all-extras --extras 'feature_bar'") - assert str(error.value) == "Can't have --all-extras and --extras together." + +def test_export_with_all_groups(tester: CommandTester, do_lock: None) -> None: + tester.execute("--format requirements.txt --all-groups") + output = tester.io.fetch_output() + assert f"baz==2.0.0 ; {MARKER_PY}" in output + assert f"opt==2.2.0 ; {MARKER_PY}" in output + + +def test_with_conflicts_all_groups(tester: CommandTester, do_lock: None) -> None: + tester.execute("--with=bar --all-groups") + + assert tester.status_code == 1 + assert ( + "You cannot specify explicit `--with` while exporting using `--all-groups`.\n" + in tester.io.fetch_error() + ) def test_export_with_urls( From ac221d6310ad0da5be349eb07f37e5cf0825100f Mon Sep 17 00:00:00 2001 From: Jordan Wolinsky Date: Fri, 16 Aug 2024 16:51:40 -0400 Subject: [PATCH 5/7] cleanup --- src/poetry_plugin_export/command.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/poetry_plugin_export/command.py b/src/poetry_plugin_export/command.py index e8e1c84..67a43d0 100644 --- a/src/poetry_plugin_export/command.py +++ b/src/poetry_plugin_export/command.py @@ -41,9 +41,8 @@ class ExportCommand(GroupCommand): None, "Include development dependencies. (Deprecated)", ), - option("all-groups", None, "Include all groups"), - option("all-extras", None, "Include all extras"), *GroupCommand._group_dependency_options(), + option("all-groups", None, "Include all sets of extra groups"), option( "extras", "E", @@ -51,6 +50,7 @@ class ExportCommand(GroupCommand): flag=False, multiple=True, ), + option("all-extras", None, "Include all sets of extra dependencies."), option("with-credentials", None, "Include credentials for extra indices."), ] From 6c6f677722d461bd4d41c6a5d9060cc821ab3464 Mon Sep 17 00:00:00 2001 From: Jordan Wolinsky Date: Mon, 26 Aug 2024 13:26:23 -0400 Subject: [PATCH 6/7] add more checks for without and only, update message --- docs/_index.md | 1 + src/poetry_plugin_export/command.py | 12 ++++++++---- tests/command/test_command_export.py | 18 +++++++++++++++++- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/docs/_index.md b/docs/_index.md index 0837c86..1377110 100644 --- a/docs/_index.md +++ b/docs/_index.md @@ -74,5 +74,6 @@ poetry export --only test,docs * {{< option name="dev" deprecated=true >}}Include development dependencies.{{< /option >}} * `--extras (-E)`: Extra sets of dependencies to include. * `--all-extras`: Include all sets of extra dependencies. +* `--all-groups`: Include all dependency groups when exporting. * `--without-hashes`: Exclude hashes from the exported file. * `--with-credentials`: Include credentials for extra indices. diff --git a/src/poetry_plugin_export/command.py b/src/poetry_plugin_export/command.py index 67a43d0..ec97c5d 100644 --- a/src/poetry_plugin_export/command.py +++ b/src/poetry_plugin_export/command.py @@ -42,7 +42,7 @@ class ExportCommand(GroupCommand): "Include development dependencies. (Deprecated)", ), *GroupCommand._group_dependency_options(), - option("all-groups", None, "Include all sets of extra groups"), + option("all-groups", None, "Include all dependency groups"), option( "extras", "E", @@ -116,11 +116,15 @@ def handle(self) -> int: f"Extra [{', '.join(sorted(invalid_extras))}] is not specified." ) - if self.option("with") and self.option("all-groups"): + if ( + self.option("with") or self.option("without") or self.option("only") + ) and self.option("all-groups"): self.line_error( "You cannot specify explicit" - " `--with` while exporting" - " using `--all-groups`." + " `--with`, " + "`--without`, " + "or `--only` " + "while exporting using `--all-groups`." ) return 1 diff --git a/tests/command/test_command_export.py b/tests/command/test_command_export.py index c39af80..7efaee2 100644 --- a/tests/command/test_command_export.py +++ b/tests/command/test_command_export.py @@ -249,7 +249,23 @@ def test_with_conflicts_all_groups(tester: CommandTester, do_lock: None) -> None assert tester.status_code == 1 assert ( - "You cannot specify explicit `--with` while exporting using `--all-groups`.\n" + "You cannot specify explicit `--with`, `--without`, or `--only` while exporting using `--all-groups`.\n" + in tester.io.fetch_error() + ) + + tester.execute("--without=bar --all-groups") + + assert tester.status_code == 1 + assert ( + "You cannot specify explicit `--with`, `--without`, or `--only` while exporting using `--all-groups`.\n" + in tester.io.fetch_error() + ) + + tester.execute("--only=bar --all-groups") + + assert tester.status_code == 1 + assert ( + "You cannot specify explicit `--with`, `--without`, or `--only` while exporting using `--all-groups`.\n" in tester.io.fetch_error() ) From b97e559c06207a1e30891dd54ef14411dc117309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20D=C3=B6ring?= <30527984+radoering@users.noreply.github.com> Date: Sun, 1 Sep 2024 11:24:07 +0200 Subject: [PATCH 7/7] improve test, add new option to readme --- README.md | 1 + docs/_index.md | 2 +- tests/command/test_command_export.py | 26 +++++++------------------- 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 000d92e..7f3d1c6 100644 --- a/README.md +++ b/README.md @@ -51,5 +51,6 @@ poetry export -f requirements.txt --output requirements.txt * `--dev`: Include development dependencies. (**Deprecated**) * `--extras (-E)`: Extra sets of dependencies to include. * `--all-extras`: Include all sets of extra dependencies. +* `--all-groups`: Include all dependency groups. * `--without-hashes`: Exclude hashes from the exported file. * `--with-credentials`: Include credentials for extra indices. diff --git a/docs/_index.md b/docs/_index.md index 1377110..3b766eb 100644 --- a/docs/_index.md +++ b/docs/_index.md @@ -74,6 +74,6 @@ poetry export --only test,docs * {{< option name="dev" deprecated=true >}}Include development dependencies.{{< /option >}} * `--extras (-E)`: Extra sets of dependencies to include. * `--all-extras`: Include all sets of extra dependencies. -* `--all-groups`: Include all dependency groups when exporting. +* `--all-groups`: Include all dependency groups. * `--without-hashes`: Exclude hashes from the exported file. * `--with-credentials`: Include credentials for extra indices. diff --git a/tests/command/test_command_export.py b/tests/command/test_command_export.py index 7efaee2..b15ee2b 100644 --- a/tests/command/test_command_export.py +++ b/tests/command/test_command_export.py @@ -244,28 +244,16 @@ def test_export_with_all_groups(tester: CommandTester, do_lock: None) -> None: assert f"opt==2.2.0 ; {MARKER_PY}" in output -def test_with_conflicts_all_groups(tester: CommandTester, do_lock: None) -> None: - tester.execute("--with=bar --all-groups") - - assert tester.status_code == 1 - assert ( - "You cannot specify explicit `--with`, `--without`, or `--only` while exporting using `--all-groups`.\n" - in tester.io.fetch_error() - ) - - tester.execute("--without=bar --all-groups") - - assert tester.status_code == 1 - assert ( - "You cannot specify explicit `--with`, `--without`, or `--only` while exporting using `--all-groups`.\n" - in tester.io.fetch_error() - ) - - tester.execute("--only=bar --all-groups") +@pytest.mark.parametrize("flag", ["--with", "--without", "--only"]) +def test_with_conflicts_all_groups( + tester: CommandTester, do_lock: None, flag: str +) -> None: + tester.execute(f"{flag}=bar --all-groups") assert tester.status_code == 1 assert ( - "You cannot specify explicit `--with`, `--without`, or `--only` while exporting using `--all-groups`.\n" + "You cannot specify explicit `--with`, `--without`," + " or `--only` while exporting using `--all-groups`.\n" in tester.io.fetch_error() )