From 091c2fe1c63e16fde476af0eb0bbf0b7dd649b95 Mon Sep 17 00:00:00 2001 From: Mikhail Valentsev Date: Mon, 13 Apr 2026 02:23:44 +0500 Subject: [PATCH] fix: mine --dry-run TypeError on files with room=None (#586) (#687) * fix: return "general" room from process_file error paths (#586) process_file() returned (0, None) for already-mined, unreadable, and too-short files. In --dry-run mode the caller always enters the room_counts branch, so None ended up as a dict key and crashed the summary printer with "unsupported format string passed to NoneType.__format__". Returning "general" instead of None makes the function contract explicit: it always yields (int, str). This matches the consensus fix discussed in the issue thread. * style: apply ruff format to test_miner.py --- mempalace/miner.py | 6 +++--- tests/test_miner.py | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/mempalace/miner.py b/mempalace/miner.py index 112de68..75ddcd9 100644 --- a/mempalace/miner.py +++ b/mempalace/miner.py @@ -416,16 +416,16 @@ def process_file( # Skip if already filed source_file = str(filepath) if not dry_run and file_already_mined(collection, source_file, check_mtime=True): - return 0, None + return 0, "general" try: content = filepath.read_text(encoding="utf-8", errors="replace") except OSError: - return 0, None + return 0, "general" content = content.strip() if len(content) < MIN_CHUNK_SIZE: - return 0, None + return 0, "general" room = detect_room(filepath, content, rooms, project_path) chunks = chunk_text(content, source_file) diff --git a/tests/test_miner.py b/tests/test_miner.py index 7851787..ea2f2a9 100644 --- a/tests/test_miner.py +++ b/tests/test_miner.py @@ -262,6 +262,32 @@ def test_file_already_mined_check_mtime(): shutil.rmtree(tmpdir, ignore_errors=True) +def test_mine_dry_run_with_tiny_file_no_crash(): + """Dry-run must not crash when process_file returns 0 drawers (room was None).""" + tmpdir = tempfile.mkdtemp() + try: + project_root = Path(tmpdir).resolve() + + # One normal file and one that falls below MIN_CHUNK_SIZE + write_file(project_root / "good.py", "def main():\n print('hello world')\n" * 20) + write_file(project_root / "tiny.txt", "x") + + with open(project_root / "mempalace.yaml", "w") as f: + yaml.dump( + { + "wing": "test_project", + "rooms": [{"name": "general", "description": "General"}], + }, + f, + ) + + palace_path = project_root / "palace" + # Should not raise TypeError on the summary print + mine(str(project_root), str(palace_path), dry_run=True) + finally: + shutil.rmtree(tmpdir, ignore_errors=True) + + def test_status_missing_palace_does_not_create_empty_collection(tmp_path, capsys): palace_path = tmp_path / "missing-palace"