Skip to content

Commit

Permalink
Try to load API key only when needed (#420)
Browse files Browse the repository at this point in the history
  • Loading branch information
chidiwilliams authored Apr 26, 2023
1 parent bf76627 commit 0a4be2b
Show file tree
Hide file tree
Showing 7 changed files with 38 additions and 33 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ jobs:
benchmark:
runs-on: ${{ matrix.os }}
if: github.ref == 'refs/heads/master'
if: github.ref == 'refs/heads/main'
strategy:
fail-fast: false
matrix:
Expand Down
28 changes: 11 additions & 17 deletions buzz/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,12 +211,14 @@ class FileTranscriberWidget(QWidget):
openai_access_token_changed = pyqtSignal(str)
settings = Settings()

def __init__(self, file_paths: List[str], openai_access_token: Optional[str] = None,
parent: Optional[QWidget] = None, flags: Qt.WindowType = Qt.WindowType.Widget) -> None:
def __init__(self, file_paths: List[str], parent: Optional[QWidget] = None,
flags: Qt.WindowType = Qt.WindowType.Widget) -> None:
super().__init__(parent, flags)

self.setWindowTitle(file_paths_as_title(file_paths))

openai_access_token = KeyringStore().get_password(KeyringStore.Key.OPENAI_API_KEY)

self.file_paths = file_paths
default_language = self.settings.value(key=Settings.Key.FILE_TRANSCRIBER_LANGUAGE, default_value='')
self.transcription_options = TranscriptionOptions(
Expand Down Expand Up @@ -909,8 +911,6 @@ def __init__(self, tasks_cache=TasksCache()):

self.tasks_cache = tasks_cache

self.openai_access_token = KeyringStore.get_password(KeyringStore.Key.OPENAI_API_KEY)

self.settings = Settings()

self.shortcut_settings = ShortcutSettings(settings=self.settings)
Expand All @@ -927,7 +927,7 @@ def __init__(self, tasks_cache=TasksCache()):
self.addToolBar(self.toolbar)
self.setUnifiedTitleAndToolBarOnMac(True)

self.menu_bar = MenuBar(shortcuts=self.shortcuts, openai_api_key=self.openai_access_token, parent=self)
self.menu_bar = MenuBar(shortcuts=self.shortcuts, parent=self)
self.menu_bar.import_action_triggered.connect(
self.on_new_transcription_action_triggered)
self.menu_bar.shortcuts_changed.connect(self.on_shortcuts_changed)
Expand Down Expand Up @@ -1022,17 +1022,16 @@ def on_new_transcription_action_triggered(self):

def open_file_transcriber_widget(self, file_paths: List[str]):
file_transcriber_window = FileTranscriberWidget(file_paths=file_paths,
openai_access_token=self.openai_access_token, parent=self,
parent=self,
flags=Qt.WindowType.Window)
file_transcriber_window.triggered.connect(
self.on_file_transcriber_triggered)
file_transcriber_window.openai_access_token_changed.connect(self.on_openai_access_token_changed)
file_transcriber_window.show()

def on_openai_access_token_changed(self, access_token: str):
self.openai_access_token = access_token
self.menu_bar.set_openai_api_key(self.openai_access_token)
KeyringStore.set_password(KeyringStore.Key.OPENAI_API_KEY, access_token)
@staticmethod
def on_openai_access_token_changed(access_token: str):
KeyringStore().set_password(KeyringStore.Key.OPENAI_API_KEY, access_token)

def open_transcript_viewer(self):
selected_rows = self.table_widget.selectionModel().selectedRows()
Expand Down Expand Up @@ -1361,11 +1360,10 @@ class MenuBar(QMenuBar):
shortcuts_changed = pyqtSignal(dict)
openai_api_key_changed = pyqtSignal(str)

def __init__(self, shortcuts: Dict[str, str], openai_api_key: str, parent: QWidget):
def __init__(self, shortcuts: Dict[str, str], parent: QWidget):
super().__init__(parent)

self.shortcuts = shortcuts
self.openai_api_key = openai_api_key

self.import_action = QAction(_("Import Media File..."), self)
self.import_action.triggered.connect(
Expand Down Expand Up @@ -1394,8 +1392,7 @@ def on_about_action_triggered(self):
about_dialog.open()

def on_preferences_action_triggered(self):
preferences_dialog = PreferencesDialog(shortcuts=self.shortcuts, openai_api_key=self.openai_api_key,
parent=self)
preferences_dialog = PreferencesDialog(shortcuts=self.shortcuts, parent=self)
preferences_dialog.shortcuts_changed.connect(self.shortcuts_changed)
preferences_dialog.openai_api_key_changed.connect(self.openai_api_key_changed)
preferences_dialog.open()
Expand All @@ -1406,9 +1403,6 @@ def set_shortcuts(self, shortcuts: Dict[str, str]):
self.import_action.setShortcut(QKeySequence.fromString(shortcuts[Shortcut.OPEN_IMPORT_WINDOW.name]))
self.preferences_action.setShortcut(QKeySequence.fromString(shortcuts[Shortcut.OPEN_PREFERENCES_WINDOW.name]))

def set_openai_api_key(self, key: str):
self.openai_api_key = key


class Application(QApplication):
window: MainWindow
Expand Down
11 changes: 6 additions & 5 deletions buzz/store/keyring_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,17 @@ class KeyringStore:
class Key(enum.Enum):
OPENAI_API_KEY = 'OpenAI API key'

@staticmethod
def get_password(username: Key) -> str:
def get_password(self, username: Key) -> str:
try:
return keyring.get_password(APP_NAME, username=username.value)
password = keyring.get_password(APP_NAME, username=username.value)
if password is None:
return ''
return password
except (KeyringLocked, KeyringError) as exc:
logging.error('Unable to read from keyring: %s', exc)
return ''

@staticmethod
def set_password(username: Key, password: str) -> None:
def set_password(self, username: Key, password: str) -> None:
try:
keyring.set_password(APP_NAME, username.value, password)
except (KeyringLocked, PasswordSetError) as exc:
Expand Down
10 changes: 6 additions & 4 deletions buzz/widgets/general_preferences_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,24 @@

import openai
from PyQt6.QtCore import QRunnable, QObject, pyqtSignal, QThreadPool
from PyQt6.QtWidgets import QWidget, QFormLayout, QLineEdit, QPushButton, QMessageBox
from PyQt6.QtWidgets import QWidget, QFormLayout, QPushButton, QMessageBox
from openai.error import AuthenticationError

from buzz.store.keyring_store import KeyringStore
from buzz.widgets.openai_api_key_line_edit import OpenAIAPIKeyLineEdit


class GeneralPreferencesWidget(QWidget):
openai_api_key_changed = pyqtSignal(str)

def __init__(self, openai_api_key: str, parent: Optional[QWidget] = None):
def __init__(self, keyring_store=KeyringStore(), parent: Optional[QWidget] = None):
super().__init__(parent)
self.openai_api_key = openai_api_key

self.openai_api_key = keyring_store.get_password(KeyringStore.Key.OPENAI_API_KEY)

layout = QFormLayout(self)

self.openai_api_key_line_edit = OpenAIAPIKeyLineEdit(openai_api_key, self)
self.openai_api_key_line_edit = OpenAIAPIKeyLineEdit(self.openai_api_key, self)
self.openai_api_key_line_edit.key_changed.connect(self.on_openai_api_key_changed)

self.test_openai_api_key_button = QPushButton('Test')
Expand Down
6 changes: 3 additions & 3 deletions buzz/widgets/preferences_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from PyQt6.QtWidgets import QDialog, QWidget, QVBoxLayout, QTabWidget, QDialogButtonBox

from buzz.locale import _
from buzz.store.keyring_store import KeyringStore
from buzz.widgets.general_preferences_widget import GeneralPreferencesWidget
from buzz.widgets.shortcuts_editor_preferences_widget import ShortcutsEditorPreferencesWidget

Expand All @@ -12,16 +13,15 @@ class PreferencesDialog(QDialog):
shortcuts_changed = pyqtSignal(dict)
openai_api_key_changed = pyqtSignal(str)

def __init__(self, shortcuts: Dict[str, str], openai_api_key: str, parent: Optional[QWidget] = None) -> None:
def __init__(self, shortcuts: Dict[str, str], parent: Optional[QWidget] = None) -> None:
super().__init__(parent)

self.setWindowTitle('Preferences')

layout = QVBoxLayout(self)
tab_widget = QTabWidget(self)

general_tab_widget = GeneralPreferencesWidget(openai_api_key=openai_api_key,
parent=self)
general_tab_widget = GeneralPreferencesWidget(parent=self)
general_tab_widget.openai_api_key_changed.connect(self.openai_api_key_changed)
tab_widget.addTab(general_tab_widget, _('General'))

Expand Down
12 changes: 10 additions & 2 deletions tests/widgets/general_preferences_widget_test.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
from unittest.mock import Mock

import pytest
from PyQt6.QtWidgets import QPushButton, QMessageBox, QLineEdit

from buzz.store.keyring_store import KeyringStore
from buzz.widgets.general_preferences_widget import GeneralPreferencesWidget


class TestGeneralPreferencesWidget:
def test_should_disable_test_button_if_no_api_key(self, qtbot):
widget = GeneralPreferencesWidget(openai_api_key='')
widget = GeneralPreferencesWidget(keyring_store=self.get_keyring_store(''))
qtbot.add_widget(widget)

test_button = widget.findChild(QPushButton)
Expand All @@ -23,7 +25,7 @@ def test_should_disable_test_button_if_no_api_key(self, qtbot):
assert test_button.isEnabled()

def test_should_test_openai_api_key(self, qtbot):
widget = GeneralPreferencesWidget(openai_api_key='wrong-api-key')
widget = GeneralPreferencesWidget(keyring_store=self.get_keyring_store('wrong-api-key'))
qtbot.add_widget(widget)

test_button = widget.findChild(QPushButton)
Expand All @@ -41,3 +43,9 @@ def mock_called():
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
2 changes: 1 addition & 1 deletion tests/widgets/preferences_dialog_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

class TestPreferencesDialog:
def test_create(self, qtbot: QtBot):
dialog = PreferencesDialog(shortcuts={}, openai_api_key='')
dialog = PreferencesDialog(shortcuts={})
qtbot.add_widget(dialog)

assert dialog.windowTitle() == 'Preferences'
Expand Down

0 comments on commit 0a4be2b

Please sign in to comment.