Skip to content

Commit ac4dcc5

Browse files
committed
fix: label pip_packages by source instead of merging
pip_packages is now a dict keyed by source (e.g. '/opt/dynamo/venv/bin/python3', 'python3', 'pip', 'uv') so you can see which packages come from which environment. Diff/check logic flattens for comparison.
1 parent f47061d commit ac4dcc5

1 file changed

Lines changed: 37 additions & 22 deletions

File tree

src/srtctl/core/fingerprint.py

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -262,16 +262,22 @@ def probe_frameworks() -> ProbeResult:
262262

263263

264264
def probe_pip_packages() -> ProbeResult:
265-
"""Get installed pip packages, sorted alphabetically (case-insensitive)."""
266-
out = _run_cmd("python3 -m pip freeze 2>/dev/null") or _run_cmd("pip freeze")
267-
if out is None:
268-
return ProbeResult.failure("pip freeze failed")
269-
270-
packages = sorted(
271-
[line.strip() for line in out.splitlines() if line.strip()],
272-
key=lambda s: s.lower(),
273-
)
274-
return ProbeResult.success(packages)
265+
"""Get installed pip packages from multiple sources, labeled by source."""
266+
result: dict[str, list[str]] = {}
267+
for label, cmd in [
268+
("python3", "python3 -m pip freeze 2>/dev/null"),
269+
("pip", "pip freeze 2>/dev/null"),
270+
("uv", "uv pip freeze 2>/dev/null"),
271+
]:
272+
out = _run_cmd(cmd)
273+
if out:
274+
pkgs = sorted(
275+
[line.strip() for line in out.splitlines() if line.strip() and not line.startswith("#")],
276+
key=lambda s: s.lower(),
277+
)
278+
if pkgs:
279+
result[label] = pkgs
280+
return ProbeResult.success(result) if result else ProbeResult.failure("all pip freeze variants failed")
275281

276282

277283
# ============================================================================
@@ -363,13 +369,23 @@ def load_fingerprint(path: Path) -> dict[str, Any] | None:
363369
]
364370

365371

366-
def _parse_pip_packages(packages: list[str]) -> dict[str, str]:
372+
def _parse_pip_packages(packages: list[str] | dict[str, list[str]]) -> dict[str, str]:
367373
"""Parse pip freeze output into {package_name: version} dict.
368374
375+
Accepts either a flat list (legacy) or a labeled dict of lists (new format).
376+
When given a dict, merges all sources (later sources overwrite earlier).
377+
369378
Handles both == and @ formats:
370379
torch==2.6.0 -> {"torch": "2.6.0"}
371380
foo @ file:///... -> {"foo": "file:///..."}
372381
"""
382+
# Normalize: flatten labeled dict into a single list
383+
if isinstance(packages, dict):
384+
flat: list[str] = []
385+
for pkg_list in packages.values():
386+
flat.extend(pkg_list)
387+
packages = flat
388+
373389
result = {}
374390
for line in packages:
375391
if "==" in line:
@@ -603,20 +619,19 @@ def find_python():
603619
PY = find_python()
604620
605621
def pip_pkgs():
606-
pkgs = set()
607-
for cmd in [
608-
f'{{PY}} -m pip freeze 2>/dev/null'.format(PY=PY),
609-
'python3 -m pip freeze 2>/dev/null',
610-
'pip freeze 2>/dev/null',
611-
'uv pip freeze 2>/dev/null',
622+
result = {{}}
623+
for label, cmd in [
624+
(PY, f'{{PY}} -m pip freeze 2>/dev/null'.format(PY=PY)),
625+
('python3', 'python3 -m pip freeze 2>/dev/null'),
626+
('pip', 'pip freeze 2>/dev/null'),
627+
('uv', 'uv pip freeze 2>/dev/null'),
612628
]:
613629
out = run(cmd)
614630
if out:
615-
for line in out.splitlines():
616-
line = line.strip()
617-
if line and not line.startswith('#'):
618-
pkgs.add(line)
619-
return sorted(pkgs, key=lambda s: s.lower())
631+
pkgs = sorted([l.strip() for l in out.splitlines() if l.strip() and not l.startswith('#')], key=lambda s: s.lower())
632+
if pkgs:
633+
result[label] = pkgs
634+
return result
620635
621636
def gpu_info():
622637
out = run('nvidia-smi --query-gpu=name,driver_version,memory.total --format=csv,noheader')

0 commit comments

Comments
 (0)