| 117 | |
| 118 | |
| 119 | def _resolve_dependency_group( |
| 120 | dependency_groups: dict[str, list[str] | _IncludeGroup], |
| 121 | group: str, |
| 122 | original_names_lookup: dict[str, str], |
| 123 | past_groups: tuple[str, ...] = (), |
| 124 | ) -> set[Requirement]: |
| 125 | if group in past_groups: |
| 126 | original_group = original_names_lookup.get(group, group) |
| 127 | original_past_groups = tuple(original_names_lookup.get(g, g) for g in past_groups) |
| 128 | msg = f"Cyclic dependency group include: {original_group!r} -> {original_past_groups!r}" |
| 129 | raise Fail(msg) |
| 130 | if group not in dependency_groups: |
| 131 | original_group = original_names_lookup.get(group, group) |
| 132 | msg = f"dependency group {original_group!r} not found" |
| 133 | raise Fail(msg) |
| 134 | raw_group = dependency_groups[group] |
| 135 | if not isinstance(raw_group, list): |
| 136 | original_group = original_names_lookup.get(group, group) |
| 137 | msg = f"dependency group {original_group!r} is not a list" |
| 138 | raise Fail(msg) |
| 139 | |
| 140 | result = set() |
| 141 | for item in raw_group: |
| 142 | if isinstance(item, str): |
| 143 | # packaging.requirements.Requirement parsing ensures that this is a valid |
| 144 | # PEP 508 Dependency Specifier |
| 145 | # raises InvalidRequirement on failure |
| 146 | try: |
| 147 | result.add(Requirement(item)) |
| 148 | except InvalidRequirement as exc: |
| 149 | msg = f"{item!r} is not valid requirement due to {exc}" |
| 150 | raise Fail(msg) from exc |
| 151 | elif isinstance(item, dict) and tuple(item.keys()) == ("include-group",): |
| 152 | include_group = canonicalize_name(str(next(iter(item.values())))) |
| 153 | result = result.union( |
| 154 | _resolve_dependency_group( |
| 155 | dependency_groups, include_group, original_names_lookup, (*past_groups, group) |
| 156 | ) |
| 157 | ) |
| 158 | else: |
| 159 | msg = f"invalid dependency group item: {item!r}" |
| 160 | raise Fail(msg) |
| 161 | return result |
| 162 | |
| 163 | |
| 164 | __all__ = [ |