commit f13f46c555b979b265c7da9b6e340b6342f9e4b0
parent aa32e31a3dbd9a6517ae40187b1160940f764958
Author: SomberNight <somber.night@protonmail.com>
Date: Fri, 3 Apr 2020 18:58:51 +0200
qt wizard: make "GoBack" unroll the call stack to avoid stack overflow
fixes #6069
Diffstat:
2 files changed, 30 insertions(+), 19 deletions(-)
diff --git a/electrum/base_wizard.py b/electrum/base_wizard.py
@@ -113,18 +113,21 @@ class BaseWizard(Logger):
def can_go_back(self):
return len(self._stack) > 1
- def go_back(self):
+ def go_back(self, *, rerun_previous: bool = True) -> None:
if not self.can_go_back():
return
# pop 'current' frame
self._stack.pop()
- # pop 'previous' frame
- stack_item = self._stack.pop()
+ prev_frame = self._stack[-1]
# try to undo side effects since we last entered 'previous' frame
- # FIXME only self.storage is properly restored
- self.data = copy.deepcopy(stack_item.db_data)
- # rerun 'previous' frame
- self.run(stack_item.action, *stack_item.args, **stack_item.kwargs)
+ # FIXME only self.data is properly restored
+ self.data = copy.deepcopy(prev_frame.db_data)
+
+ if rerun_previous:
+ # pop 'previous' frame
+ self._stack.pop()
+ # rerun 'previous' frame
+ self.run(prev_frame.action, *prev_frame.args, **prev_frame.kwargs)
def reset_stack(self):
self._stack = []
diff --git a/electrum/gui/qt/installwizard.py b/electrum/gui/qt/installwizard.py
@@ -96,19 +96,27 @@ def wizard_dialog(func):
def func_wrapper(*args, **kwargs):
run_next = kwargs['run_next']
wizard = args[0] # type: InstallWizard
- wizard.back_button.setText(_('Back') if wizard.can_go_back() else _('Cancel'))
- try:
- out = func(*args, **kwargs)
- if type(out) is not tuple:
- out = (out,)
- run_next(*out)
- except GoBack:
- if wizard.can_go_back():
- wizard.go_back()
- return
- else:
- wizard.close()
+ while True:
+ wizard.back_button.setText(_('Back') if wizard.can_go_back() else _('Cancel'))
+ # current dialog
+ try:
+ out = func(*args, **kwargs)
+ if type(out) is not tuple:
+ out = (out,)
+ except GoBack:
+ if not wizard.can_go_back():
+ wizard.close()
+ # to go back from the current dialog, we just let the caller unroll the stack:
raise
+ # next dialog
+ try:
+ run_next(*out)
+ except GoBack:
+ # to go back from the next dialog, we ask the wizard to restore state
+ wizard.go_back(rerun_previous=False)
+ # and we re-run the current dialog (by continuing)
+ else:
+ break
return func_wrapper