Source code for pjkm.core.groups.resolver
"""Group resolver: expand selected groups including transitive dependencies."""
from __future__ import annotations
from pjkm.core.models.group import PackageGroup
from pjkm.core.models.platform import PlatformInfo
[docs]
class GroupResolutionError(Exception):
"""Raised when group resolution fails (missing group, circular deps)."""
[docs]
class GroupResolver:
"""Resolves selected group IDs into a fully-expanded, ordered list of PackageGroups."""
def __init__(self, groups: dict[str, PackageGroup]) -> None:
self._groups = groups
[docs]
def resolve(
self,
selected_ids: list[str],
platform: PlatformInfo | None = None,
) -> list[PackageGroup]:
"""Expand selected groups, resolving requires_groups transitively.
Returns groups in dependency order (dependencies before dependents).
Filters out groups that don't match the current platform.
"""
# Expand transitively
all_ids: set[str] = set()
ordered: list[str] = []
visiting: set[str] = set()
for gid in selected_ids:
self._expand(gid, all_ids, ordered, visiting)
# Filter by platform
result: list[PackageGroup] = []
for gid in ordered:
group = self._groups[gid]
if platform and group.platform_filter:
if group.platform_filter != platform.os:
continue
result.append(group)
return result
def _expand(
self,
group_id: str,
seen: set[str],
ordered: list[str],
visiting: set[str],
) -> None:
if group_id in seen:
return
if group_id in visiting:
msg = f"Circular group dependency detected involving {group_id!r}"
raise GroupResolutionError(msg)
if group_id not in self._groups:
msg = f"Unknown group {group_id!r}"
raise GroupResolutionError(msg)
visiting.add(group_id)
group = self._groups[group_id]
for dep_id in group.requires_groups:
self._expand(dep_id, seen, ordered, visiting)
visiting.discard(group_id)
seen.add(group_id)
ordered.append(group_id)