Skip to content

Commit feba7e8

Browse files
committed
fix(miner): same None-metadata guard for status() histogram loop
`status()` walks `col.get(include=["metadatas"])` and buckets each drawer into a `wing_rooms[wing][room]` histogram. The same ChromaDB return shape fixed in the search print path — `None` entries in the `metadatas` list for drawers with no stored metadata — crashes the status command with: AttributeError: 'NoneType' object has no attribute 'get' Applies the matching ``m = m or {}`` guard so None-metadata drawers roll up under the existing `?/?` fallback bucket instead of killing the command mid-tally. Reproduced on a 135K-drawer palace where two drawers had `metadata=None`; both now show under `WING: ? / ROOM: ?` in the tally while the command prints the full histogram as designed. Adds a regression test that feeds `status()` a fake collection whose `get()` returns a `None` in the middle of the metadatas list and asserts both the fallback bucket and the real wing render.
1 parent a3c7782 commit feba7e8

2 files changed

Lines changed: 31 additions & 0 deletions

File tree

mempalace/miner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,7 @@ def status(palace_path: str):
854854

855855
wing_rooms = defaultdict(lambda: defaultdict(int))
856856
for m in metas:
857+
m = m or {}
857858
wing_rooms[m.get("wing", "?")][m.get("room", "?")] += 1
858859

859860
print(f"\n{'=' * 55}")

tests/test_miner.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,36 @@ def test_status_missing_palace_does_not_create_empty_collection(tmp_path, capsys
343343
assert not palace_path.exists()
344344

345345

346+
def test_status_handles_none_metadata_without_crash(tmp_path, capsys):
347+
"""status must not crash when col.get returns a None entry in metadatas.
348+
349+
Palaces can contain drawers whose metadata was never set (older mining
350+
paths, drawers written by third-party tools). Before the guard, status
351+
crashed mid-tally with ``AttributeError: 'NoneType' object has no
352+
attribute 'get'`` at the wing/room histogram line."""
353+
from unittest.mock import patch
354+
355+
class FakeCol:
356+
def count(self):
357+
return 2
358+
359+
def get(self, *args, **kwargs):
360+
return {
361+
"ids": ["a", "b"],
362+
"documents": ["doc a", "doc b"],
363+
"metadatas": [{"wing": "proj", "room": "r"}, None],
364+
}
365+
366+
with patch("mempalace.miner.get_collection", return_value=FakeCol()):
367+
status(str(tmp_path))
368+
369+
out = capsys.readouterr().out
370+
# No crash; the None-metadata row is counted under the ?/? fallback
371+
# alongside the real wing=proj row.
372+
assert "WING: ?" in out
373+
assert "WING: proj" in out
374+
375+
346376
# ── normalize_version schema gate ───────────────────────────────────────
347377
#
348378
# When the normalization pipeline changes shape (e.g., strip_noise lands),

0 commit comments

Comments
 (0)