Browse Source

fix: preserve settings save errors

master
Yutsuo 4 days ago
parent
commit
ea54c950a3
  1. 6
      README.md
  2. 5
      src/voice_transcriptor/services/settings.py
  3. 23
      tests/test_settings.py

6
README.md

@ -1,6 +0,0 @@
# Voice Transcriptor
The editable default transcription model is `gpt-4o-transcribe`. New installations use
Brazilian Portuguese (`pt-BR`) and save output to the user's Documents folder when it is
available, otherwise to the home folder. API keys are stored only in the operating-system
keyring and are never written to the settings file.

5
src/voice_transcriptor/services/settings.py

@ -66,7 +66,10 @@ class SettingsRepository:
raise SettingsError("Unable to save settings.") from exc
finally:
if temporary_path is not None and temporary_path.exists():
temporary_path.unlink(missing_ok=True)
try:
temporary_path.unlink(missing_ok=True)
except OSError:
pass
@staticmethod
def _settings_from_payload(payload: object) -> AppSettings:

23
tests/test_settings.py

@ -4,7 +4,7 @@ from pathlib import Path
import pytest
from voice_transcriptor.models import AppSettings
from voice_transcriptor.services.settings import SettingsRepository
from voice_transcriptor.services.settings import SettingsError, SettingsRepository
def test_load_missing_file_returns_documented_defaults(monkeypatch, tmp_path: Path) -> None:
@ -78,6 +78,27 @@ def test_save_replaces_existing_file_atomically(monkeypatch, tmp_path: Path) ->
assert json.loads(path.read_text(encoding="utf-8"))["model"] == "model"
def test_save_preserves_settings_error_when_replace_and_cleanup_fail(
monkeypatch, tmp_path: Path
) -> None:
path = tmp_path / "settings.json"
replacement_failure = OSError("target is locked")
def failing_replace(source: Path, destination: Path) -> Path:
raise replacement_failure
def failing_unlink(path: Path, missing_ok: bool = False) -> None:
raise OSError("temporary file is locked")
monkeypatch.setattr(Path, "replace", failing_replace)
monkeypatch.setattr(Path, "unlink", failing_unlink)
with pytest.raises(SettingsError) as caught:
SettingsRepository(path).save(AppSettings("model", "pt-BR", tmp_path / "out"))
assert caught.value.__cause__ is replacement_failure
def test_saved_settings_never_include_api_key_fields(tmp_path: Path) -> None:
path = tmp_path / "settings.json"

Loading…
Cancel
Save