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.
This commit is contained in:
@@ -854,6 +854,7 @@ def status(palace_path: str):
|
|||||||
|
|
||||||
wing_rooms = defaultdict(lambda: defaultdict(int))
|
wing_rooms = defaultdict(lambda: defaultdict(int))
|
||||||
for m in metas:
|
for m in metas:
|
||||||
|
m = m or {}
|
||||||
wing_rooms[m.get("wing", "?")][m.get("room", "?")] += 1
|
wing_rooms[m.get("wing", "?")][m.get("room", "?")] += 1
|
||||||
|
|
||||||
print(f"\n{'=' * 55}")
|
print(f"\n{'=' * 55}")
|
||||||
|
|||||||
@@ -343,6 +343,36 @@ def test_status_missing_palace_does_not_create_empty_collection(tmp_path, capsys
|
|||||||
assert not palace_path.exists()
|
assert not palace_path.exists()
|
||||||
|
|
||||||
|
|
||||||
|
def test_status_handles_none_metadata_without_crash(tmp_path, capsys):
|
||||||
|
"""status must not crash when col.get returns a None entry in metadatas.
|
||||||
|
|
||||||
|
Palaces can contain drawers whose metadata was never set (older mining
|
||||||
|
paths, drawers written by third-party tools). Before the guard, status
|
||||||
|
crashed mid-tally with ``AttributeError: 'NoneType' object has no
|
||||||
|
attribute 'get'`` at the wing/room histogram line."""
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
class FakeCol:
|
||||||
|
def count(self):
|
||||||
|
return 2
|
||||||
|
|
||||||
|
def get(self, *args, **kwargs):
|
||||||
|
return {
|
||||||
|
"ids": ["a", "b"],
|
||||||
|
"documents": ["doc a", "doc b"],
|
||||||
|
"metadatas": [{"wing": "proj", "room": "r"}, None],
|
||||||
|
}
|
||||||
|
|
||||||
|
with patch("mempalace.miner.get_collection", return_value=FakeCol()):
|
||||||
|
status(str(tmp_path))
|
||||||
|
|
||||||
|
out = capsys.readouterr().out
|
||||||
|
# No crash; the None-metadata row is counted under the ?/? fallback
|
||||||
|
# alongside the real wing=proj row.
|
||||||
|
assert "WING: ?" in out
|
||||||
|
assert "WING: proj" in out
|
||||||
|
|
||||||
|
|
||||||
# ── normalize_version schema gate ───────────────────────────────────────
|
# ── normalize_version schema gate ───────────────────────────────────────
|
||||||
#
|
#
|
||||||
# When the normalization pipeline changes shape (e.g., strip_noise lands),
|
# When the normalization pipeline changes shape (e.g., strip_noise lands),
|
||||||
|
|||||||
Reference in New Issue
Block a user