Skip to content

Commit 29a081f

Browse files
authored
feat: Option to exclude optional groups from install/sync -G:all (#2655)
* feature #2258 option to exclude groups from sync/install -G:all * doc #2258 news fragment for option to exclude groups from sync/install -G:all * misc #2258 changed flag aliases
1 parent 429df86 commit 29a081f

File tree

5 files changed

+49
-0
lines changed

5 files changed

+49
-0
lines changed

news/2258.feature.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add option to exclude group(s) when running ```pdm sync/install -G:all``` by adding flag ```--without group1,group2,...```

src/pdm/cli/filters.py

+4
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ def __init__(
2121
dev: bool | None = None,
2222
groups: Sequence[str] = (),
2323
group: str | None = None,
24+
excluded_groups: Sequence[str] = (),
2425
):
2526
self.project = project
2627
self.groups = groups
2728
self.group = group
2829
self.default = default
2930
self.dev = dev
31+
self.excluded_groups = excluded_groups
3032

3133
@classmethod
3234
def from_options(cls, project: Project, options: argparse.Namespace) -> GroupSelection:
@@ -37,6 +39,7 @@ def from_options(cls, project: Project, options: argparse.Namespace) -> GroupSel
3739
default=options.default,
3840
dev=options.dev,
3941
groups=options.groups,
42+
excluded_groups=options.excluded_groups,
4043
)
4144

4245
def one(self) -> str:
@@ -83,6 +86,7 @@ def _translated_groups(self) -> list[str]:
8386
groups_set.update(dev_groups)
8487
if ":all" in groups:
8588
groups_set.discard(":all")
89+
optional_groups -= set(self.excluded_groups)
8690
groups_set.update(optional_groups)
8791

8892
invalid_groups = groups_set - set(project.iter_groups())

src/pdm/cli/options.py

+9
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ def no_isolation_option(
220220
groups_group.add_argument(
221221
"-G",
222222
"--group",
223+
"--with",
223224
dest="groups",
224225
metavar="GROUP",
225226
action=split_lists(","),
@@ -228,6 +229,14 @@ def no_isolation_option(
228229
'use ":all" to include all groups under the same species.',
229230
default=[],
230231
)
232+
groups_group.add_argument(
233+
"--without",
234+
dest="excluded_groups",
235+
metavar="",
236+
action=split_lists(","),
237+
help="Exclude groups of optional-dependencies when using :all",
238+
default=[],
239+
)
231240
groups_group.add_argument(
232241
"--no-default",
233242
dest="default",

tests/cli/test_install.py

+21
Original file line numberDiff line numberDiff line change
@@ -312,3 +312,24 @@ def test_fix_package_type_and_update(fixture_project, pdm, working_set):
312312
pdm(["fix", "package-type"], obj=project, strict=True)
313313
pdm(["update"], obj=project, strict=True)
314314
assert "test-package-type-fixer" not in working_set
315+
316+
317+
def test_install_all_with_excluded_groups(project, working_set, pdm):
318+
project.add_dependencies({"urllib3": parse_requirement("urllib3")}, "url")
319+
project.add_dependencies({"pytz": parse_requirement("pytz")}, "tz")
320+
project.add_dependencies({"pyopenssl": parse_requirement("pyopenssl")}, "ssl")
321+
pdm(["install", "-G", ":all", "--without", "tz,ssl"], obj=project, strict=True)
322+
assert "urllib3" in working_set
323+
assert "pytz" not in working_set
324+
assert "pyopenssl" not in working_set
325+
326+
327+
def test_sync_all_with_excluded_groups(project, working_set, pdm):
328+
project.add_dependencies({"urllib3": parse_requirement("urllib3")}, "url")
329+
project.add_dependencies({"pytz": parse_requirement("pytz")}, "tz")
330+
project.add_dependencies({"pyopenssl": parse_requirement("pyopenssl")}, "ssl")
331+
pdm(["lock", "-G:all"], obj=project, strict=True)
332+
pdm(["sync", "-G", ":all", "--without", "url,tz"], obj=project, strict=True)
333+
assert "urllib3" not in working_set
334+
assert "pytz" not in working_set
335+
assert "pyopenssl" in working_set

tests/test_utils.py

+14
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,20 @@ def test_dependency_group_selection(project, args, golden):
509509
assert sorted(golden) == sorted(selection)
510510

511511

512+
@pytest.mark.parametrize(
513+
"args,golden",
514+
[
515+
({"groups": [":all"], "excluded_groups": ["web"]}, ["default", "auth", "test", "doc"]),
516+
({"groups": [":all"], "excluded_groups": ["web", "auth"]}, ["default", "test", "doc"]),
517+
({"groups": [":all"], "excluded_groups": ["default", "test"]}, ["default", "web", "auth", "test", "doc"]),
518+
],
519+
)
520+
def test_exclude_optional_groups_from_all(project, args, golden):
521+
setup_dependencies(project)
522+
selection = GroupSelection(project, **args)
523+
assert sorted(golden) == sorted(selection)
524+
525+
512526
def test_prod_should_not_be_with_dev(project):
513527
setup_dependencies(project)
514528
selection = GroupSelection(project, default=True, dev=False, groups=["test"])

0 commit comments

Comments
 (0)