Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/specify_cli/presets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,12 @@ def _load(self) -> dict:
if not isinstance(data.get("presets"), dict):
data["presets"] = {}
return data
except (json.JSONDecodeError, FileNotFoundError):
except (json.JSONDecodeError, UnicodeDecodeError, FileNotFoundError):
# Corrupted or missing registry, start fresh. A registry whose
# bytes cannot be decoded as UTF-8 is the same corruption class
# as malformed JSON — only the exception type differs. OSError is
# deliberately not caught: the data may be intact on disk, and
# starting fresh would let a later _save() wipe it.
return {
"schema_version": self.SCHEMA_VERSION,
"presets": {}
Expand Down
21 changes: 21 additions & 0 deletions tests/test_presets.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,27 @@ def test_empty_registry(self, temp_dir):
assert registry.list() == {}
assert not registry.is_installed("test-pack")

def test_load_starts_fresh_for_non_utf8_registry(self, temp_dir):
"""A registry file with undecodable bytes must start fresh, not raise.

``_load()`` already treats malformed JSON as "corrupted registry,
start fresh", but a registry whose *bytes* cannot be decoded as UTF-8
raised a raw ``UnicodeDecodeError`` from the same boundary — the same
corruption class reaching a different exception type.
"""
packs_dir = temp_dir / "packs"
packs_dir.mkdir()
(packs_dir / PresetRegistry.REGISTRY_FILE).write_bytes(
b"\xff\xfe not utf-8 \xc3\x28"
)
Comment thread
marcelsafin marked this conversation as resolved.

registry = PresetRegistry(packs_dir)

assert registry.data == {
"schema_version": PresetRegistry.SCHEMA_VERSION,
"presets": {},
}

def test_add_and_get(self, temp_dir):
"""Test adding and retrieving a pack."""
packs_dir = temp_dir / "packs"
Expand Down