fix: handle keyring store exception (#685)

This commit is contained in:
Chidi Williams 2024-03-14 22:05:42 +00:00 committed by GitHub
parent 939549266e
commit 3280de9742
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 71 additions and 61 deletions

View file

@ -10,7 +10,7 @@ from buzz.model_loader import (
TranscriptionModel,
ModelDownloader,
)
from buzz.store.keyring_store import KeyringStore
from buzz.store.keyring_store import get_password, Key
from buzz.transcriber.transcriber import (
Task,
FileTranscriptionTask,
@ -180,9 +180,7 @@ def parse(app: Application, parser: QCommandLineParser):
model.model_type == ModelType.OPEN_AI_WHISPER_API
and openai_access_token == ""
):
openai_access_token = KeyringStore().get_password(
key=KeyringStore.Key.OPENAI_API_KEY
)
openai_access_token = get_password(key=Key.OPENAI_API_KEY)
if openai_access_token == "":
raise CommandLineError("No OpenAI access token found")

View file

@ -2,27 +2,24 @@ import enum
import logging
import keyring
from keyring.errors import KeyringLocked, KeyringError, PasswordSetError
from buzz.settings.settings import APP_NAME
class KeyringStore:
class Key(enum.Enum):
OPENAI_API_KEY = "OpenAI API key"
class Key(enum.Enum):
OPENAI_API_KEY = "OpenAI API key"
def get_password(self, key: Key) -> str:
try:
password = keyring.get_password(APP_NAME, username=key.value)
if password is None:
return ""
return password
except (KeyringLocked, KeyringError) as exc:
logging.warning("Unable to read from keyring: %s", exc)
def get_password(key: Key) -> str | None:
try:
password = keyring.get_password(APP_NAME, username=key.value)
if password is None:
return ""
return password
except Exception as exc:
logging.warning("Unable to read from keyring: %s", exc)
return ""
def set_password(self, username: Key, password: str) -> None:
try:
keyring.set_password(APP_NAME, username.value, password)
except (KeyringLocked, PasswordSetError) as exc:
logging.error("Unable to write to keyring: %s", exc)
def set_password(username: Key, password: str) -> None:
keyring.set_password(APP_NAME, username.value, password)

View file

@ -20,7 +20,7 @@ from buzz.file_transcriber_queue_worker import FileTranscriberQueueWorker
from buzz.locale import _
from buzz.settings.settings import APP_NAME, Settings
from buzz.settings.shortcut_settings import ShortcutSettings
from buzz.store.keyring_store import KeyringStore
from buzz.store.keyring_store import set_password, Key
from buzz.transcriber.transcriber import (
FileTranscriptionTask,
TranscriptionOptions,
@ -248,7 +248,13 @@ class MainWindow(QMainWindow):
@staticmethod
def on_openai_access_token_changed(access_token: str):
KeyringStore().set_password(KeyringStore.Key.OPENAI_API_KEY, access_token)
try:
set_password(Key.OPENAI_API_KEY, access_token)
except Exception as exc:
logging.error("Unable to write to keyring: %s", exc)
QMessageBox.critical(
None, _("Error"), _("Unable to save OpenAI API key to keyring")
)
def open_transcript_viewer(self):
selected_rows = self.table_widget.selectionModel().selectedRows()

View file

@ -11,7 +11,7 @@ from PyQt6.QtWidgets import (
QVBoxLayout,
)
from buzz.store.keyring_store import KeyringStore
from buzz.store.keyring_store import Key, get_password
from buzz.transcriber.transcriber import (
TranscriptionOptions,
FileTranscriptionOptions,
@ -67,9 +67,7 @@ class FolderWatchPreferencesWidget(QWidget):
output_folder_row.addWidget(self.output_folder_line_edit)
output_folder_row.addWidget(output_folder_browse_button)
openai_access_token = KeyringStore().get_password(
KeyringStore.Key.OPENAI_API_KEY
)
openai_access_token = get_password(Key.OPENAI_API_KEY)
(
transcription_options,
file_transcription_options,

View file

@ -5,7 +5,7 @@ from PyQt6.QtWidgets import QWidget, QFormLayout, QPushButton, QMessageBox
from openai import AuthenticationError, OpenAI
from buzz.settings.settings import Settings
from buzz.store.keyring_store import KeyringStore
from buzz.store.keyring_store import get_password, Key
from buzz.widgets.line_edit import LineEdit
from buzz.widgets.openai_api_key_line_edit import OpenAIAPIKeyLineEdit
@ -15,14 +15,11 @@ class GeneralPreferencesWidget(QWidget):
def __init__(
self,
keyring_store=KeyringStore(),
parent: Optional[QWidget] = None,
):
super().__init__(parent)
self.openai_api_key = keyring_store.get_password(
KeyringStore.Key.OPENAI_API_KEY
)
self.openai_api_key = get_password(Key.OPENAI_API_KEY)
layout = QFormLayout(self)

View file

@ -13,7 +13,7 @@ from buzz.locale import _
from buzz.model_loader import ModelDownloader
from buzz.paths import file_path_as_title
from buzz.settings.settings import Settings
from buzz.store.keyring_store import KeyringStore
from buzz.store.keyring_store import get_password, Key
from buzz.transcriber.transcriber import (
FileTranscriptionOptions,
TranscriptionOptions,
@ -52,9 +52,7 @@ class FileTranscriberWidget(QWidget):
self.setWindowTitle(self.get_title())
openai_access_token = KeyringStore().get_password(
KeyringStore.Key.OPENAI_API_KEY
)
openai_access_token = get_password(Key.OPENAI_API_KEY)
preferences = self.load_preferences()

View file

@ -4,7 +4,7 @@ from typing import Dict
from PyQt6.QtCore import QFileSystemWatcher, pyqtSignal, QObject
from buzz.store.keyring_store import KeyringStore
from buzz.store.keyring_store import Key, get_password
from buzz.transcriber.transcriber import FileTranscriptionTask
from buzz.widgets.preferences_dialog.models.folder_watch_preferences import (
FolderWatchPreferences,
@ -49,9 +49,7 @@ class TranscriptionTaskFolderWatcher(QFileSystemWatcher):
):
continue
openai_access_token = KeyringStore().get_password(
KeyringStore.Key.OPENAI_API_KEY
)
openai_access_token = get_password(Key.OPENAI_API_KEY)
(
transcription_options,
file_transcription_options,

19
poetry.lock generated
View file

@ -1917,6 +1917,23 @@ pytest = ">=4.6"
[package.extras]
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
[[package]]
name = "pytest-mock"
version = "3.12.0"
description = "Thin-wrapper around the mock package for easier use with pytest"
optional = false
python-versions = ">=3.8"
files = [
{ file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9" },
{ file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f" },
]
[package.dependencies]
pytest = ">=5.0"
[package.extras]
dev = ["pre-commit", "pytest-asyncio", "tox"]
[[package]]
name = "pytest-qt"
version = "4.4.0"
@ -2834,4 +2851,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p
[metadata]
lock-version = "2.0"
python-versions = ">=3.9.13,<3.11"
content-hash = "af0faf240d7f659c167f3f644a35ccb17e7c70787be015f2d7276391e38591bb"
content-hash = "91d2a0fc397a34a3808a1ad2d0a5bd3b225b0ea03213ccc7a18e29b5def21bd9"

View file

@ -43,6 +43,7 @@ pytest = "^7.1.3"
pytest-cov = "^4.0.0"
pytest-qt = "^4.1.0"
pytest-xvfb = "^2.0.0"
pytest-mock = "^3.12.0"
pylint = "^2.15.5"
pre-commit = "^2.20.0"
pytest-benchmark = "^4.0.0"

View file

@ -1,16 +1,18 @@
from unittest.mock import Mock
from PyQt6.QtWidgets import QPushButton, QMessageBox, QLineEdit
from buzz.store.keyring_store import KeyringStore
from buzz.widgets.preferences_dialog.general_preferences_widget import (
GeneralPreferencesWidget,
)
class TestGeneralPreferencesWidget:
def test_should_disable_test_button_if_no_api_key(self, qtbot):
widget = GeneralPreferencesWidget(keyring_store=self.get_keyring_store(""))
def test_should_disable_test_button_if_no_api_key(self, qtbot, mocker):
mocker.patch(
"buzz.widgets.preferences_dialog.general_preferences_widget.get_password",
return_value="",
)
widget = GeneralPreferencesWidget()
qtbot.add_widget(widget)
test_button = widget.findChild(QPushButton)
@ -25,10 +27,13 @@ class TestGeneralPreferencesWidget:
assert test_button.isEnabled()
def test_should_test_openai_api_key(self, qtbot):
widget = GeneralPreferencesWidget(
keyring_store=self.get_keyring_store("wrong-api-key"),
def test_should_test_openai_api_key(self, qtbot, mocker):
mocker.patch(
"buzz.widgets.preferences_dialog.general_preferences_widget.get_password",
return_value="wrong-api-key",
)
widget = GeneralPreferencesWidget()
qtbot.add_widget(widget)
test_button = widget.findChild(QPushButton)
@ -36,21 +41,16 @@ class TestGeneralPreferencesWidget:
test_button.click()
mock = Mock()
QMessageBox.warning = mock
message_box_warning_mock = mocker.Mock()
QMessageBox.warning = message_box_warning_mock
def mock_called():
mock.assert_called()
assert mock.call_args[0][1] == "OpenAI API Key Test"
message_box_warning_mock.assert_called()
assert message_box_warning_mock.call_args[0][1] == "OpenAI API Key Test"
assert (
mock.call_args[0][2]
== "Incorrect API key provided: wrong-ap*-key. You can find your API key at https://platform.openai.com/account/api-keys."
message_box_warning_mock.call_args[0][2]
== "Incorrect API key provided: wrong-ap*-key. You can find your "
"API key at https://platform.openai.com/account/api-keys."
)
qtbot.waitUntil(mock_called)
@staticmethod
def get_keyring_store(password: str):
keyring_store = Mock(KeyringStore)
keyring_store.get_password.return_value = password
return keyring_store