1
0

Merge pull request #9769 from f321x/fix_plugins_dialog_bad_state

fix: PluginsDialog bugs causing exceptions
This commit is contained in:
ThomasV
2025-04-30 13:52:19 +02:00
committed by GitHub
2 changed files with 30 additions and 10 deletions

View File

@@ -4,12 +4,13 @@ import shutil
import os import os
from PyQt6.QtWidgets import QLabel, QVBoxLayout, QGridLayout, QPushButton, QWidget, QScrollArea, QFormLayout, QFileDialog, QMenu, QApplication from PyQt6.QtWidgets import QLabel, QVBoxLayout, QGridLayout, QPushButton, QWidget, QScrollArea, QFormLayout, QFileDialog, QMenu, QApplication
from PyQt6.QtCore import Qt from PyQt6.QtCore import QTimer
from electrum.i18n import _ from electrum.i18n import _
from electrum.logging import get_logger
from .util import WindowModalDialog, Buttons, CloseButton, WWLabel, insert_spaces, MessageBoxMixin, EnterButton from .util import (WindowModalDialog, Buttons, CloseButton, WWLabel, insert_spaces, MessageBoxMixin,
from .util import read_QIcon_from_bytes, IconLabel EnterButton, read_QIcon_from_bytes, IconLabel, RunCoroutineDialog)
if TYPE_CHECKING: if TYPE_CHECKING:
@@ -88,7 +89,7 @@ class PluginDialog(WindowModalDialog):
_('Settings'), _('Settings'),
partial(p.settings_dialog, self)) partial(p.settings_dialog, self))
buttons.insert(1, settings_button) buttons.insert(1, settings_button)
# add buttonss # add buttons
vbox.addLayout(Buttons(*buttons)) vbox.addLayout(Buttons(*buttons))
def do_toggle(self): def do_toggle(self):
@@ -150,6 +151,7 @@ class PluginStatusButton(QPushButton):
class PluginsDialog(WindowModalDialog, MessageBoxMixin): class PluginsDialog(WindowModalDialog, MessageBoxMixin):
_logger = get_logger(__name__)
def __init__(self, config: 'SimpleConfig', plugins:'Plugins', *, gui_object: Optional['ElectrumGui'] = None): def __init__(self, config: 'SimpleConfig', plugins:'Plugins', *, gui_object: Optional['ElectrumGui'] = None):
WindowModalDialog.__init__(self, None, _('Electrum Plugins')) WindowModalDialog.__init__(self, None, _('Electrum Plugins'))
@@ -252,7 +254,8 @@ class PluginsDialog(WindowModalDialog, MessageBoxMixin):
return return
coro = self.plugins.download_external_plugin(url) coro = self.plugins.download_external_plugin(url)
try: try:
path = self.window.run_coroutine_dialog(coro, _("Downloading plugin...")) d = RunCoroutineDialog(self, _("Downloading plugin..."), coro)
path = d.run()
except UserCancelled: except UserCancelled:
return return
except Exception as e: except Exception as e:
@@ -264,7 +267,10 @@ class PluginsDialog(WindowModalDialog, MessageBoxMixin):
self.show_error(f"{e}") self.show_error(f"{e}")
success = False success = False
if not success: if not success:
os.unlink(path) try:
os.unlink(path)
except FileNotFoundError:
self._logger.debug("", exc_info=True)
def add_plugin_dialog(self): def add_plugin_dialog(self):
pubkey, salt = self.plugins.get_pubkey_bytes() pubkey, salt = self.plugins.get_pubkey_bytes()
@@ -276,6 +282,9 @@ class PluginsDialog(WindowModalDialog, MessageBoxMixin):
return return
plugins_dir = self.plugins.get_external_plugin_dir() plugins_dir = self.plugins.get_external_plugin_dir()
path = os.path.join(plugins_dir, os.path.basename(filename)) path = os.path.join(plugins_dir, os.path.basename(filename))
if os.path.exists(path):
self.show_warning(_('Plugin already installed.'))
return
shutil.copyfile(filename, path) shutil.copyfile(filename, path)
try: try:
success = self.add_external_plugin(path) success = self.add_external_plugin(path)
@@ -283,7 +292,10 @@ class PluginsDialog(WindowModalDialog, MessageBoxMixin):
self.show_error(f"{e}") self.show_error(f"{e}")
success = False success = False
if not success: if not success:
os.unlink(path) try:
os.unlink(path)
except FileNotFoundError:
self._logger.debug("", exc_info=True)
def add_external_plugin(self, path): def add_external_plugin(self, path):
manifest = self.plugins.read_manifest(path) manifest = self.plugins.read_manifest(path)
@@ -353,8 +365,7 @@ class PluginsDialog(WindowModalDialog, MessageBoxMixin):
status_button.update() status_button.update()
if self.gui_object: if self.gui_object:
self.gui_object.reload_windows() self.gui_object.reload_windows()
self.setFocus() self.bring_to_front()
self.activateWindow()
def uninstall_plugin(self, name): def uninstall_plugin(self, name):
if not self.question(_('Remove plugin \'{}\'?').format(name)): if not self.question(_('Remove plugin \'{}\'?').format(name)):
@@ -363,3 +374,10 @@ class PluginsDialog(WindowModalDialog, MessageBoxMixin):
if self.gui_object: if self.gui_object:
self.gui_object.reload_windows() self.gui_object.reload_windows()
self.show_list() self.show_list()
self.bring_to_front()
def bring_to_front(self):
def _bring_self_to_front():
self.activateWindow()
self.setFocus()
QTimer.singleShot(100, _bring_self_to_front)

View File

@@ -246,10 +246,12 @@ class Plugins(DaemonThread):
os.mkdir(pkg_path) os.mkdir(pkg_path)
return pkg_path return pkg_path
async def download_external_plugin(self, url): async def download_external_plugin(self, url: str) -> str:
filename = os.path.basename(urlparse(url).path) filename = os.path.basename(urlparse(url).path)
pkg_path = self.get_external_plugin_dir() pkg_path = self.get_external_plugin_dir()
path = os.path.join(pkg_path, filename) path = os.path.join(pkg_path, filename)
if os.path.exists(path):
raise FileExistsError(f"Plugin {filename} already exists at {path}")
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
async with session.get(url) as resp: async with session.get(url) as resp:
if resp.status == 200: if resp.status == 200: