diff --git a/dynamic_stack_decider/dynamic_stack_decider/abstract_stack_element.py b/dynamic_stack_decider/dynamic_stack_decider/abstract_stack_element.py index 8814c1b..866b3c0 100644 --- a/dynamic_stack_decider/dynamic_stack_decider/abstract_stack_element.py +++ b/dynamic_stack_decider/dynamic_stack_decider/abstract_stack_element.py @@ -52,6 +52,14 @@ def pop(self): """ self._dsd.pop() + def on_pop(self): # noqa + """ + This method is called when the element is popped from the stack. + It can be used to clean up resources, cancel actions or similar tasks. + Overload this method if you need to do something when the element is popped. + """ + pass + @abstractmethod def perform(self, reevaluate=False): """ diff --git a/dynamic_stack_decider/dynamic_stack_decider/dsd.py b/dynamic_stack_decider/dynamic_stack_decider/dsd.py index 25807cf..870194f 100644 --- a/dynamic_stack_decider/dynamic_stack_decider/dsd.py +++ b/dynamic_stack_decider/dynamic_stack_decider/dsd.py @@ -259,7 +259,8 @@ def update(self, reevaluate: bool = True): elif result != self.stack[self.stack_exec_index + 1][0].activation_reason: # In this case, however, the activation reason actually did change. Therefore, we have to # discard everything in the stack above the current decision and push the new result. - self.stack = self.stack[0 : self.stack_exec_index + 1] + for _ in range(self.stack_exec_index + 1, len(self.stack)): + self.stack.pop()[1].on_pop() self.stack_reevaluate = False self.push(tree_element.get_child(result)) @@ -309,8 +310,9 @@ def pop(self): if self.stack_reevaluate: # we are currently reevaluating. we shorten the stack here if self.stack_exec_index > 0: - # only shorten stack if it still has one element - self.stack = self.stack[0 : self.stack_exec_index] + ## only shorten stack if it still has one element + for _ in range(self.stack_exec_index, len(self.stack)): + self.stack.pop()[1].on_pop() # stop reevaluating self.stack_reevaluate = False else: @@ -321,10 +323,10 @@ def pop(self): # only a single element of the sequence # We also do not want to reset do_not_reevaluate because an action in the sequence # may control the stack beyond its own lifetime but in the sequence element's lifetime - self.stack[-1][1].pop_one() + self.stack[-1][1].pop_one().on_pop() return # Remove the last element of the stack - self.stack.pop() + self.stack.pop()[1].on_pop() # We will reevaluate even when the popped element set do_not_reevaluate # because no module should control the stack beyond its lifetime diff --git a/dynamic_stack_decider/dynamic_stack_decider/sequence_element.py b/dynamic_stack_decider/dynamic_stack_decider/sequence_element.py index 045518a..d653b6c 100644 --- a/dynamic_stack_decider/dynamic_stack_decider/sequence_element.py +++ b/dynamic_stack_decider/dynamic_stack_decider/sequence_element.py @@ -63,11 +63,22 @@ def pop_one(self): assert not self.in_last_element(), ( "It is not possible to pop a single element when" "the last element of the sequence is active" ) + # Save the current action to return it + popped_action = self.current_action # Increment the index to the next action and initialize it self.current_action_index += 1 - # We initilize the current action here to avoid the problem described in + # We initialize the current action here to avoid the problem described in # https://github.com/bit-bots/dynamic_stack_decider/issues/107 self.current_action = self._init_function(self.actions[self.current_action_index]) + # Return the popped action + return popped_action + + def on_pop(self): + """ + This method is called when the sequence is popped from the stack. + This means that the last element of the sequence was also popped, so + """ + self.current_action.on_pop() def in_last_element(self): """Returns if the current element is the last element of the sequence"""