From 7679ec5b1fff3ce62785e4a1d2622247c64f6079 Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Sat, 29 Oct 2022 15:07:00 +0100 Subject: [PATCH] Improved Metadata deepcopy recursion fix. It was possible for the recursion to occur if mi.deepcopy() was called after (un)safe_format(). Change the state save/restore mechanism to ensure all instance variables are restored to base settings. --- src/calibre/utils/formatter.py | 40 ++++++++++++++++------------------ 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/calibre/utils/formatter.py b/src/calibre/utils/formatter.py index 68ed75d153..e02b8f9b3f 100644 --- a/src/calibre/utils/formatter.py +++ b/src/calibre/utils/formatter.py @@ -1827,29 +1827,12 @@ class TemplateFormatter(string.Formatter): # reference can use different parameters when calling safe_format(). Because # the parameters are saved as instance variables they can possibly affect # the 'calling' template. To avoid this problem, save the current formatter - # state when recursion is detected. There is no point in saving the level - # 0 state. + # state when recursion is detected. Save state at level zero to be sure that + # all class instance variables are restored to their base settings. def save_state(self): self.recursion_level += 1 - if self.recursion_level > 0: - return ( - (self.strip_results, - self.column_name, - self.template_cache, - self.kwargs, - self.book, - self.global_vars, - self.funcs, - self.locals, - self._caller, - self.python_context_object)) - else: - return None - - def restore_state(self, state): - self.recursion_level -= 1 - if state is not None: + return ( (self.strip_results, self.column_name, self.template_cache, @@ -1859,7 +1842,22 @@ class TemplateFormatter(string.Formatter): self.funcs, self.locals, self._caller, - self.python_context_object) = state + self.python_context_object)) + + def restore_state(self, state): + self.recursion_level -= 1 + if state is None: + raise ValueError(_('Formatter state restored before saved')) + (self.strip_results, + self.column_name, + self.template_cache, + self.kwargs, + self.book, + self.global_vars, + self.funcs, + self.locals, + self._caller, + self.python_context_object) = state # Allocate an interpreter if the formatter encounters a GPM or TPM template. # We need to allocate additional interpreters if there is composite recursion