mirror of
https://gitlab.com/khumba/nvd.git
synced 2024-11-27 07:03:49 +01:00
Rename "package manifests" to "package sets".
There are two styles of coding that are used currently. First, the PackageManifest{,Pair} classes track an arbitrary list of store paths (not necessarily a closure) as parsed Package and Version objects. These are used to track and compare the sets of selected packages. Then for everything else like version diffing, we just store raw nested data structures holding package names and versions, e.g. dict[str, list[optional[str]]] for mapping package names to lists of available versions (and then parse Versions on the fly to do comparisons, which is inefficient). This is the beginning of clean-up to make the latter use PackageSet, Package, and Version as well.
This commit is contained in:
parent
9594ab31ef
commit
682570f054
1 changed files with 74 additions and 70 deletions
144
src/nvd
144
src/nvd
|
@ -31,10 +31,10 @@
|
||||||
# -: Package is newly unselected.
|
# -: Package is newly unselected.
|
||||||
# *: Package is selected; state unchanged.
|
# *: Package is selected; state unchanged.
|
||||||
# .: Not in environment.systemPackages; dependency.
|
# .: Not in environment.systemPackages; dependency.
|
||||||
# l: Package manifest only available on the left; not selected there.
|
# l: Selected package set only available on the left; not selected there.
|
||||||
# L: Package manifest only available on the left; selected there.
|
# L: Selected package set only available on the left; selected there.
|
||||||
# r: Package manifest only available on the right; not selected there.
|
# r: Selected package set only available on the right; not selected there.
|
||||||
# R: Package manifest only available on the right; selected there.
|
# R: Selected package set only available on the right; selected there.
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import fnmatch
|
import fnmatch
|
||||||
|
@ -89,13 +89,13 @@ SEL_SELECTED = "*"
|
||||||
SEL_UNSELECTED = "."
|
SEL_UNSELECTED = "."
|
||||||
SEL_NEWLY_SELECTED = "+"
|
SEL_NEWLY_SELECTED = "+"
|
||||||
SEL_NEWLY_UNSELECTED = "-"
|
SEL_NEWLY_UNSELECTED = "-"
|
||||||
# There's no way to reach these selection states currently, since manifests are
|
# There's no way to reach these selection states currently, since selected sets
|
||||||
# always computed.
|
# are always computed.
|
||||||
SEL_LEFT_ONLY_SELECTED = "L"
|
SEL_LEFT_ONLY_SELECTED = "L"
|
||||||
SEL_LEFT_ONLY_UNSELECTED = "l"
|
SEL_LEFT_ONLY_UNSELECTED = "l"
|
||||||
SEL_RIGHT_ONLY_SELECTED = "R"
|
SEL_RIGHT_ONLY_SELECTED = "R"
|
||||||
SEL_RIGHT_ONLY_UNSELECTED = "r"
|
SEL_RIGHT_ONLY_UNSELECTED = "r"
|
||||||
SEL_NO_MANIFESTS = ""
|
SEL_NO_SELECTED_SETS = ""
|
||||||
|
|
||||||
NIX_STORE_PATH_REGEX = re.compile(r"^/nix/store/[a-z0-9]+-(.+?)(-([0-9].*?))?(\.drv)?$")
|
NIX_STORE_PATH_REGEX = re.compile(r"^/nix/store/[a-z0-9]+-(.+?)(-([0-9].*?))?(\.drv)?$")
|
||||||
|
|
||||||
|
@ -222,7 +222,7 @@ class Package:
|
||||||
def store_path(self) -> StorePath:
|
def store_path(self) -> StorePath:
|
||||||
return self._store_path
|
return self._store_path
|
||||||
|
|
||||||
class PackageManifest:
|
class PackageSet:
|
||||||
def __init__(self, packages: List[Package]):
|
def __init__(self, packages: List[Package]):
|
||||||
assert isinstance(packages, List)
|
assert isinstance(packages, List)
|
||||||
assert all(isinstance(package, Package) for package in packages)
|
assert all(isinstance(package, Package) for package in packages)
|
||||||
|
@ -234,27 +234,31 @@ class PackageManifest:
|
||||||
self._packages_by_pname.setdefault(entry.pname(), []).append(entry)
|
self._packages_by_pname.setdefault(entry.pname(), []).append(entry)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_tree(root: Path) -> "PackageManifest":
|
def from_direct_dependencies(path: Path) -> "PackageSet":
|
||||||
direct_deps_str: str = subprocess.run(
|
return PackageSet.from_nix_query(["--references", str(path)])
|
||||||
[make_nix_bin_path("nix-store"), "--query", "--references", str(root)],
|
|
||||||
|
@staticmethod
|
||||||
|
def from_nix_query(nix_query_args: List[str]) -> "PackageSet":
|
||||||
|
result_paths_str: str = subprocess.run(
|
||||||
|
[make_nix_bin_path("nix-store"), "--query"] + nix_query_args,
|
||||||
stdout=PIPE,
|
stdout=PIPE,
|
||||||
text=True,
|
text=True,
|
||||||
check=True,
|
check=True,
|
||||||
).stdout.rstrip("\n")
|
).stdout.rstrip("\n")
|
||||||
|
|
||||||
direct_deps: List[str] = \
|
result_paths: List[str] = \
|
||||||
direct_deps_str.split("\n") if direct_deps_str else []
|
result_paths_str.split("\n") if result_paths_str else []
|
||||||
|
|
||||||
packages = []
|
packages = []
|
||||||
for dep_path in direct_deps:
|
for result_path in result_paths:
|
||||||
pname, version = parse_pname_version(dep_path)
|
pname, version = parse_pname_version(result_path)
|
||||||
packages.append(Package(
|
packages.append(Package(
|
||||||
pname=pname,
|
pname=pname,
|
||||||
version=Version(version),
|
version=Version(version),
|
||||||
store_path=StorePath(dep_path),
|
store_path=StorePath(result_path),
|
||||||
))
|
))
|
||||||
|
|
||||||
return PackageManifest(packages)
|
return PackageSet(packages)
|
||||||
|
|
||||||
def contains_pname(self, pname: str) -> bool:
|
def contains_pname(self, pname: str) -> bool:
|
||||||
return pname in self._packages_by_pname
|
return pname in self._packages_by_pname
|
||||||
|
@ -262,61 +266,61 @@ class PackageManifest:
|
||||||
def all_pnames(self):
|
def all_pnames(self):
|
||||||
return self._packages_by_pname.keys()
|
return self._packages_by_pname.keys()
|
||||||
|
|
||||||
class PackageManifestPair:
|
class PackageSetPair:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
left_manifest: Optional[PackageManifest],
|
left_set: Optional[PackageSet],
|
||||||
right_manifest: Optional[PackageManifest]
|
right_set: Optional[PackageSet]
|
||||||
):
|
):
|
||||||
assert left_manifest is None or isinstance(left_manifest, PackageManifest)
|
assert left_set is None or isinstance(left_set, PackageSet)
|
||||||
assert right_manifest is None or isinstance(right_manifest, PackageManifest)
|
assert right_set is None or isinstance(right_set, PackageSet)
|
||||||
self._left_manifest = left_manifest
|
self._left_set = left_set
|
||||||
self._right_manifest = right_manifest
|
self._right_set = right_set
|
||||||
|
|
||||||
def get_left_manifest(self) -> Optional[PackageManifest]:
|
def get_left_set(self) -> Optional[PackageSet]:
|
||||||
return self._left_manifest
|
return self._left_set
|
||||||
|
|
||||||
def get_right_manifest(self) -> Optional[PackageManifest]:
|
def get_right_set(self) -> Optional[PackageSet]:
|
||||||
return self._right_manifest
|
return self._right_set
|
||||||
|
|
||||||
def get_selection_state(self, pname: str) -> str:
|
def get_selection_state(self, pname: str) -> str:
|
||||||
left_manifest = self._left_manifest
|
left_set = self._left_set
|
||||||
right_manifest = self._right_manifest
|
right_set = self._right_set
|
||||||
|
|
||||||
if left_manifest is not None and right_manifest is not None:
|
if left_set is not None and right_set is not None:
|
||||||
in_left_manifest = left_manifest.contains_pname(pname)
|
in_left_set = left_set.contains_pname(pname)
|
||||||
in_right_manifest = right_manifest.contains_pname(pname)
|
in_right_set = right_set.contains_pname(pname)
|
||||||
selection_state_str = [
|
selection_state_str = [
|
||||||
SEL_UNSELECTED,
|
SEL_UNSELECTED,
|
||||||
SEL_NEWLY_UNSELECTED,
|
SEL_NEWLY_UNSELECTED,
|
||||||
SEL_NEWLY_SELECTED,
|
SEL_NEWLY_SELECTED,
|
||||||
SEL_SELECTED,
|
SEL_SELECTED,
|
||||||
][int(in_left_manifest) + 2 * int(in_right_manifest)]
|
][int(in_left_set) + 2 * int(in_right_set)]
|
||||||
elif left_manifest is not None:
|
elif left_set is not None:
|
||||||
in_left_manifest = left_manifest.contains_pname(pname)
|
in_left_set = left_set.contains_pname(pname)
|
||||||
selection_state_str = [
|
selection_state_str = [
|
||||||
SEL_LEFT_ONLY_UNSELECTED,
|
SEL_LEFT_ONLY_UNSELECTED,
|
||||||
SEL_LEFT_ONLY_SELECTED,
|
SEL_LEFT_ONLY_SELECTED,
|
||||||
][int(in_left_manifest)]
|
][int(in_left_set)]
|
||||||
elif right_manifest is not None:
|
elif right_set is not None:
|
||||||
in_right_manifest = right_manifest.contains_pname(pname)
|
in_right_set = right_set.contains_pname(pname)
|
||||||
selection_state_str = [
|
selection_state_str = [
|
||||||
SEL_RIGHT_ONLY_UNSELECTED,
|
SEL_RIGHT_ONLY_UNSELECTED,
|
||||||
SEL_RIGHT_ONLY_SELECTED,
|
SEL_RIGHT_ONLY_SELECTED,
|
||||||
][int(in_right_manifest)]
|
][int(in_right_set)]
|
||||||
else:
|
else:
|
||||||
selection_state_str = SEL_NO_MANIFESTS
|
selection_state_str = SEL_NO_SELECTED_SETS
|
||||||
|
|
||||||
return selection_state_str
|
return selection_state_str
|
||||||
|
|
||||||
def is_selection_state_changed(self, pname: str) -> str:
|
def is_selection_state_changed(self, pname: str) -> str:
|
||||||
in_left_manifest = \
|
in_left_set = \
|
||||||
self._left_manifest is not None and \
|
self._left_set is not None and \
|
||||||
self._left_manifest.contains_pname(pname)
|
self._left_set.contains_pname(pname)
|
||||||
in_right_manifest = \
|
in_right_set = \
|
||||||
self._right_manifest is not None and \
|
self._right_set is not None and \
|
||||||
self._right_manifest.contains_pname(pname)
|
self._right_set.contains_pname(pname)
|
||||||
return in_left_manifest != in_right_manifest
|
return in_left_set != in_right_set
|
||||||
|
|
||||||
def parse_pname_version(path: str) -> Tuple[str, Optional[str]]:
|
def parse_pname_version(path: str) -> Tuple[str, Optional[str]]:
|
||||||
base_path = str(StorePath(path).to_base_path().path())
|
base_path = str(StorePath(path).to_base_path().path())
|
||||||
|
@ -387,7 +391,7 @@ def render_versions(
|
||||||
def print_package_list(
|
def print_package_list(
|
||||||
*,
|
*,
|
||||||
pnames: List[str],
|
pnames: List[str],
|
||||||
manifest_pair: PackageManifestPair,
|
selected_sets: PackageSetPair,
|
||||||
left_versions_map: Dict[str, List[str]],
|
left_versions_map: Dict[str, List[str]],
|
||||||
right_versions_map: Optional[Dict[str, List[str]]] = None,
|
right_versions_map: Optional[Dict[str, List[str]]] = None,
|
||||||
fixed_install_state: Optional[str] = None,
|
fixed_install_state: Optional[str] = None,
|
||||||
|
@ -425,11 +429,11 @@ def print_package_list(
|
||||||
install_state_str = INST_CHANGED
|
install_state_str = INST_CHANGED
|
||||||
|
|
||||||
selected_at_all = (
|
selected_at_all = (
|
||||||
manifest_pair.get_left_manifest() is not None
|
selected_sets.get_left_set() is not None
|
||||||
and manifest_pair.get_left_manifest().contains_pname(pname)
|
and selected_sets.get_left_set().contains_pname(pname)
|
||||||
) or (
|
) or (
|
||||||
manifest_pair.get_right_manifest() is not None
|
selected_sets.get_right_set() is not None
|
||||||
and manifest_pair.get_right_manifest().contains_pname(pname)
|
and selected_sets.get_right_set().contains_pname(pname)
|
||||||
)
|
)
|
||||||
if selected_at_all:
|
if selected_at_all:
|
||||||
bold_if_selected_sgr = sgr(SGR_BOLD)
|
bold_if_selected_sgr = sgr(SGR_BOLD)
|
||||||
|
@ -449,7 +453,7 @@ def print_package_list(
|
||||||
|
|
||||||
status_str = "[{}{}]".format(
|
status_str = "[{}{}]".format(
|
||||||
bold_if_selected_sgr + install_state_str + sgr(SGR_RESET),
|
bold_if_selected_sgr + install_state_str + sgr(SGR_RESET),
|
||||||
pname_sgr + manifest_pair.get_selection_state(pname) + sgr(SGR_RESET),
|
pname_sgr + selected_sets.get_selection_state(pname) + sgr(SGR_RESET),
|
||||||
)
|
)
|
||||||
count_str = count_format_str.format(count)
|
count_str = count_format_str.format(count)
|
||||||
pname_str = pname_format_str.format(pname)
|
pname_str = pname_format_str.format(pname)
|
||||||
|
@ -612,9 +616,9 @@ def run_list(*, root, only_selected, name_patterns):
|
||||||
|
|
||||||
path = path.resolve()
|
path = path.resolve()
|
||||||
if (path / "sw").is_dir():
|
if (path / "sw").is_dir():
|
||||||
manifest = PackageManifest.parse_tree((path / "sw").resolve())
|
selected_set = PackageSet.from_direct_dependencies((path / "sw").resolve())
|
||||||
else:
|
else:
|
||||||
manifest = PackageManifest.parse_tree(path / "sw")
|
selected_set = PackageSet.from_direct_dependencies(path / "sw")
|
||||||
|
|
||||||
closure_paths: List[str] = subprocess.run(
|
closure_paths: List[str] = subprocess.run(
|
||||||
[make_nix_bin_path("nix-store"), "-qR", str(path)],
|
[make_nix_bin_path("nix-store"), "-qR", str(path)],
|
||||||
|
@ -626,7 +630,7 @@ def run_list(*, root, only_selected, name_patterns):
|
||||||
closure_map: Dict[str, List[str]] = closure_paths_to_map(closure_paths)
|
closure_map: Dict[str, List[str]] = closure_paths_to_map(closure_paths)
|
||||||
|
|
||||||
if only_selected:
|
if only_selected:
|
||||||
target_pnames = list(manifest.all_pnames())
|
target_pnames = list(selected_set.all_pnames())
|
||||||
else:
|
else:
|
||||||
target_pnames = list(closure_map.keys())
|
target_pnames = list(closure_map.keys())
|
||||||
|
|
||||||
|
@ -640,7 +644,7 @@ def run_list(*, root, only_selected, name_patterns):
|
||||||
|
|
||||||
print_package_list(
|
print_package_list(
|
||||||
pnames=target_pnames,
|
pnames=target_pnames,
|
||||||
manifest_pair=PackageManifestPair(manifest, manifest),
|
selected_sets=PackageSetPair(selected_set, selected_set),
|
||||||
left_versions_map=closure_map,
|
left_versions_map=closure_map,
|
||||||
right_versions_map=None,
|
right_versions_map=None,
|
||||||
fixed_install_state=INST_INSTALLED,
|
fixed_install_state=INST_INSTALLED,
|
||||||
|
@ -669,17 +673,17 @@ def run_diff(
|
||||||
print(f"<<< {left_path}")
|
print(f"<<< {left_path}")
|
||||||
print(f">>> {right_path}")
|
print(f">>> {right_path}")
|
||||||
|
|
||||||
left_manifest: Optional[PackageManifest] = None
|
left_selected_set: Optional[PackageSet] = None
|
||||||
right_manifest: Optional[PackageManifest] = None
|
right_selected_set: Optional[PackageSet] = None
|
||||||
|
|
||||||
if (left_resolved / "sw").is_dir() and (right_resolved / "sw").is_dir():
|
if (left_resolved / "sw").is_dir() and (right_resolved / "sw").is_dir():
|
||||||
left_manifest = PackageManifest.parse_tree((left_resolved / "sw").resolve())
|
left_selected_set = PackageSet.from_direct_dependencies((left_resolved / "sw").resolve())
|
||||||
right_manifest = PackageManifest.parse_tree((right_resolved / "sw").resolve())
|
right_selected_set = PackageSet.from_direct_dependencies((right_resolved / "sw").resolve())
|
||||||
else:
|
else:
|
||||||
left_manifest = PackageManifest.parse_tree(left_resolved)
|
left_selected_set = PackageSet.from_direct_dependencies(left_resolved)
|
||||||
right_manifest = PackageManifest.parse_tree(right_resolved)
|
right_selected_set = PackageSet.from_direct_dependencies(right_resolved)
|
||||||
|
|
||||||
manifest_pair = PackageManifestPair(left_manifest, right_manifest)
|
selected_sets = PackageSetPair(left_selected_set, right_selected_set)
|
||||||
|
|
||||||
left_closure_paths: List[str] = subprocess.run(
|
left_closure_paths: List[str] = subprocess.run(
|
||||||
[make_nix_bin_path("nix-store"), "-qR", str(left_resolved)],
|
[make_nix_bin_path("nix-store"), "-qR", str(left_resolved)],
|
||||||
|
@ -711,7 +715,7 @@ def run_diff(
|
||||||
for pname in common_package_names:
|
for pname in common_package_names:
|
||||||
if left_closure_map[pname] != right_closure_map[pname]:
|
if left_closure_map[pname] != right_closure_map[pname]:
|
||||||
package_names_with_changed_versions.append(pname)
|
package_names_with_changed_versions.append(pname)
|
||||||
elif manifest_pair.is_selection_state_changed(pname):
|
elif selected_sets.is_selection_state_changed(pname):
|
||||||
package_names_with_changed_selection_states.append(pname)
|
package_names_with_changed_selection_states.append(pname)
|
||||||
|
|
||||||
any_changes_displayed = False
|
any_changes_displayed = False
|
||||||
|
@ -722,7 +726,7 @@ def run_diff(
|
||||||
print("Version changes:")
|
print("Version changes:")
|
||||||
print_package_list(
|
print_package_list(
|
||||||
pnames=package_names_with_changed_versions,
|
pnames=package_names_with_changed_versions,
|
||||||
manifest_pair=manifest_pair,
|
selected_sets=selected_sets,
|
||||||
left_versions_map=left_closure_map,
|
left_versions_map=left_closure_map,
|
||||||
right_versions_map=right_closure_map,
|
right_versions_map=right_closure_map,
|
||||||
no_version_suffix_highlight=no_version_suffix_highlight,
|
no_version_suffix_highlight=no_version_suffix_highlight,
|
||||||
|
@ -734,7 +738,7 @@ def run_diff(
|
||||||
print("Selection state changes:")
|
print("Selection state changes:")
|
||||||
print_package_list(
|
print_package_list(
|
||||||
pnames=package_names_with_changed_selection_states,
|
pnames=package_names_with_changed_selection_states,
|
||||||
manifest_pair=manifest_pair,
|
selected_sets=selected_sets,
|
||||||
left_versions_map=left_closure_map,
|
left_versions_map=left_closure_map,
|
||||||
fixed_install_state=INST_CHANGED,
|
fixed_install_state=INST_CHANGED,
|
||||||
)
|
)
|
||||||
|
@ -745,7 +749,7 @@ def run_diff(
|
||||||
print("Added packages:")
|
print("Added packages:")
|
||||||
print_package_list(
|
print_package_list(
|
||||||
pnames=right_only_package_names,
|
pnames=right_only_package_names,
|
||||||
manifest_pair=manifest_pair,
|
selected_sets=selected_sets,
|
||||||
left_versions_map=right_closure_map, # Yes, this is correct.
|
left_versions_map=right_closure_map, # Yes, this is correct.
|
||||||
fixed_install_state=INST_ADDED,
|
fixed_install_state=INST_ADDED,
|
||||||
)
|
)
|
||||||
|
@ -756,7 +760,7 @@ def run_diff(
|
||||||
print("Removed packages:")
|
print("Removed packages:")
|
||||||
print_package_list(
|
print_package_list(
|
||||||
pnames=left_only_package_names,
|
pnames=left_only_package_names,
|
||||||
manifest_pair=manifest_pair,
|
selected_sets=selected_sets,
|
||||||
left_versions_map=left_closure_map,
|
left_versions_map=left_closure_map,
|
||||||
fixed_install_state=INST_REMOVED,
|
fixed_install_state=INST_REMOVED,
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue