Skip to content

Commit

Permalink
If corrupt user prefs are encountered, reset prefs instead of excepting
Browse files Browse the repository at this point in the history
  • Loading branch information
MHendricks committed Dec 1, 2023
1 parent 3428664 commit c980705
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 2 deletions.
13 changes: 11 additions & 2 deletions hab/user_prefs.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def filename(self, value):

def load(self, force=False):
"""Load user preferences from self.filename if they haven't already
been loaded.
been loaded. Does not use pyjson5 as this file is entirely managed by hab.
Args:
force (bool, optional): Force re-loading of preferences.
Expand All @@ -108,7 +108,16 @@ def load(self, force=False):
return True

with self.filename.open() as fle:
data = utils.json.load(fle)
try:
# NOTE: This file is always saved using the native json library
# there is no reason to support json5 encoded files here.
data = json.load(fle)
except ValueError as error:
# When encountering corrupt saved user pref data, clear and
# reset the user prefs. Warn about it this isn't entirely hidden
logger.warning("User pref file corrupt, resetting.")
logger.info("User pref exception suppressed:", exc_info=error)
data = {}
self.update(data)
self._is_loaded = True
return True
Expand Down
58 changes: 58 additions & 0 deletions tests/test_user_prefs.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import datetime
import logging

import pytest

Expand Down Expand Up @@ -157,3 +158,60 @@ def test_uri(resolver, tmpdir, monkeypatch):
# Check UriObj.__str__ returns a string even if uri is None
prefs_g = user_prefs.UriObj()
assert isinstance(prefs_g.__str__(), str)


@pytest.mark.parametrize(
"test_text",
(
# File was opened in write mode but no contents written
None,
# Process was killed half way through writing json output.
'{\n "uri": "app',
),
)
def test_corruption(resolver, tmpdir, monkeypatch, caplog, test_text):
"""Check how UserPrefs handles trying to load an incomplete or empty existing
json document.
"""
caplog.set_level(logging.INFO)

def assert_log_exists(level, msg):
for record in caplog.records:
if record.levelname != level:
continue
if msg in record.message:
print(record)
break
else:
raise AssertionError(
f"No logging message was made matching: {level} and {msg}"
)

# TODO: This duplicated code should probably be moved into a fixture
# Force the prefs to be saved into the test directory.
if utils.Platform.name() == "windows":
monkeypatch.setenv("LOCALAPPDATA", str(tmpdir))
else:
monkeypatch.setenv("HOME", str(tmpdir))
# Ensure we are using the modified filepath
prefs = user_prefs.UserPrefs(resolver)
assert prefs.filename.parent == tmpdir

prefs_file = tmpdir / ".hab_user_prefs.json"

# Create a empty file that won't resolve properly for json.
with prefs_file.open("w") as fle:
if test_text:
fle.write(test_text)

# Check that the expected log messages are emitted when invalid
# json file contents are encountered
caplog.clear()
prefs = user_prefs.UserPrefs(resolver)
# Even with invalid contents True will be returned
assert prefs.load()
# When corrupt prefs are encountered, default empty dict results
assert prefs == {}

assert_log_exists("WARNING", "User pref file corrupt, resetting.")
assert_log_exists("INFO", "User pref exception suppressed:")

0 comments on commit c980705

Please sign in to comment.