1
0

Merge pull request #9710 from f321x/dont_delete_config_on_syntax_error

config: Raise instead of overwriting the config file on syntax error
This commit is contained in:
ThomasV
2025-05-15 17:24:35 +02:00
committed by GitHub
4 changed files with 50 additions and 12 deletions

View File

@@ -27,7 +27,7 @@ import os
import signal
import sys
import threading
from typing import Optional, TYPE_CHECKING, List, Sequence
from typing import Optional, TYPE_CHECKING, List, Sequence, Union
try:
import PyQt6
@@ -582,3 +582,25 @@ class ElectrumGui(BaseElectrumGui, Logger):
message = _("Text copied to Clipboard") if title is None else _("{} copied to Clipboard").format(title)
# tooltip cannot be displayed immediately when called from a menu; wait 200ms
self.timer.singleShot(200, lambda: QToolTip.showText(QCursor.pos(), message, None))
def standalone_exception_dialog(exception: Union[str, BaseException]) -> None:
app = QApplication.instance()
if not app:
app = QApplication([])
msg_box = QMessageBox()
msg_box.setWindowTitle(_("Error starting Electrum"))
msg_box.setIcon(QMessageBox.Icon.Critical)
msg_box.setText(_("An error occurred") + ":")
msg_box.setInformativeText(str(exception))
# Add detailed traceback if available
if hasattr(exception, "__traceback__"):
import traceback
detailed_text = ''.join(traceback.format_exception(
type(exception), exception, exception.__traceback__)
)
msg_box.setDetailedText(detailed_text)
msg_box.exec()

View File

@@ -901,9 +901,7 @@ def read_user_config(path: Optional[str]) -> Dict[str, Any]:
with open(config_path, "r", encoding='utf-8') as f:
data = f.read()
result = json.loads(data)
except Exception as exc:
_logger.warning(f"Cannot read config file at {config_path}: {exc}")
return {}
if not type(result) is dict:
return {}
assert isinstance(result, dict), "config file is not a dict"
except Exception as e:
raise ValueError(f"Invalid config file at {config_path}: {str(e)}")
return result

View File

@@ -271,6 +271,25 @@ def sys_exit(i):
loop_thread.join(timeout=1)
sys.exit(i)
def read_config(config_options: dict) -> SimpleConfig:
"""
Reads the config file and returns SimpleConfig, on failure it will potentially
show a GUI error dialog if a gui is available, and then re-raise the exception.
"""
try:
return SimpleConfig(config_options)
except Exception as config_error:
# parse full cmd to find out which UI is being used
full_config_options = parse_command_line(simple_parser=False)
if full_config_options.get("cmd") == 'gui':
gui_name = full_config_options.get(SimpleConfig.GUI_NAME.key(), 'qt')
try:
gui = __import__(f'electrum.gui.{gui_name}', fromlist=['electrum'])
gui.standalone_exception_dialog(config_error) # type: ignore
except Exception as e:
print_stderr(f"Error showing standalone gui dialog: {e}")
raise
def parse_command_line(simple_parser=False) -> Dict:
# parse command line from sys.argv
if simple_parser:
@@ -371,14 +390,14 @@ def main():
sys.argv.remove(x)
# parse first without plugins
config_options = parse_command_line(simple_parser=True)
tmp_config = SimpleConfig(config_options)
tmp_config = read_config(config_options)
# load (only) the commands modules of plugins so their commands are registered
plugin_commands = Plugins(tmp_config, cmd_only=True)
_plugin_commands = Plugins(tmp_config, cmd_only=True)
# re-parse command line
sys.argv = saved_sys_argv[:]
config_options = parse_command_line()
config = SimpleConfig(config_options)
config = read_config(config_options)
cmdname = config.get('cmd')
# set language as early as possible

View File

@@ -6,7 +6,6 @@ import shutil
from io import StringIO
from electrum.simple_config import SimpleConfig, read_user_config
from electrum import constants
from . import ElectrumTestCase
@@ -250,5 +249,5 @@ class TestUserConfig(ElectrumTestCase):
with open(thefile, "w") as f:
f.write(repr(payload))
result = read_user_config(self.user_dir)
self.assertEqual({}, result)
with self.assertRaises(ValueError):
read_user_config(self.user_dir)