tqt wizard: make "GoBack" unroll the call stack to avoid stack overflow - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit f13f46c555b979b265c7da9b6e340b6342f9e4b0 DIR parent aa32e31a3dbd9a6517ae40187b1160940f764958 HTML 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: M electrum/base_wizard.py | 17 ++++++++++------- M electrum/gui/qt/installwizard.py | 32 +++++++++++++++++++------------ 2 files changed, 30 insertions(+), 19 deletions(-) --- DIR diff --git a/electrum/base_wizard.py b/electrum/base_wizard.py t@@ -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 = [] DIR diff --git a/electrum/gui/qt/installwizard.py b/electrum/gui/qt/installwizard.py t@@ -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