From ec18c7ea552c096e2bcbaa82ceccf0cbc99f88c0 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 13 May 2024 22:24:12 +1000 Subject: [PATCH] Extensions: refactor CommandBatch.exec_non_blocking return value Use a named tuple for readability. - Messages (as before). - Boolean (completed status). - Boolean (true when the status-data changed). Used to detect when a redraw is needed. --- bl_pkg/bl_extension_notify.py | 19 +++++--------- bl_pkg/bl_extension_ops.py | 4 +-- bl_pkg/bl_extension_utils.py | 59 +++++++++++++++++++------------------------ 3 files changed, 34 insertions(+), 48 deletions(-) diff --git a/bl_pkg/bl_extension_notify.py b/bl_pkg/bl_extension_notify.py index 1621d1e..4174169 100644 --- a/bl_pkg/bl_extension_notify.py +++ b/bl_pkg/bl_extension_notify.py @@ -120,7 +120,7 @@ def sync_status_generator(repos_notify): if USE_GRACEFUL_EXIT: import time # Force all commands to close. - while not cmd_batch.exec_non_blocking(request_exit=True)[1]: + while not cmd_batch.exec_non_blocking(request_exit=True).all_complete: # Avoid high CPU usage on exit. time.sleep(0.01) @@ -139,18 +139,14 @@ def sync_status_generator(repos_notify): # Run The Update # # ############## # - status_data = status_data_prev = bl_extension_utils.CommandBatch_StatusFlag(0, 0, 0) - # The count is unknown. update_total = -1 while True: - command_info, command_complete = cmd_batch.exec_non_blocking( + command_result = cmd_batch.exec_non_blocking( # TODO: if Blender requested an exit... this should request exit here. request_exit=False, ) - # Defer breaking when `command_info is None` so the final output text can be displayed. - # Forward new messages to reports. msg_list_per_command = cmd_batch.calc_status_log_since_last_request_or_none() if bpy.app.debug: @@ -167,19 +163,16 @@ def sync_status_generator(repos_notify): else: print(ty, msg) - status_data = cmd_batch.calc_status_data() # TODO: more elegant way to detect changes. # Re-calculating the same information each time then checking if it's different isn't great. - if status_data != status_data_prev: - if command_complete: + if command_result.status_data_changed: + if command_result.all_complete: update_total = sync_status_count_outdated_extensions(repos_notify) - status_data_prev = status_data - yield (status_data, update_total) + yield (cmd_batch.calc_status_data(), update_total) else: yield None - if command_complete: - # Finished. + if command_result.all_complete: break atexit.unregister(cmd_force_quit) diff --git a/bl_pkg/bl_extension_ops.py b/bl_pkg/bl_extension_ops.py index fa6a32d..601e5a0 100644 --- a/bl_pkg/bl_extension_ops.py +++ b/bl_pkg/bl_extension_ops.py @@ -801,7 +801,7 @@ class CommandHandle: return {'RUNNING_MODAL'} def op_modal_step(self, op, context): - command_info, command_complete = self.cmd_batch.exec_non_blocking( + command_result = self.cmd_batch.exec_non_blocking( request_exit=self.request_exit, ) @@ -835,7 +835,7 @@ class CommandHandle: repo_status_text.running = True _preferences_ui_redraw() - if command_complete: + if command_result.all_complete: self.wm.event_timer_remove(self.modal_timer) del op._runtime_handle context.workspace.status_text_set(None) diff --git a/bl_pkg/bl_extension_utils.py b/bl_pkg/bl_extension_utils.py index 7eb4354..ef0dd6b 100644 --- a/bl_pkg/bl_extension_utils.py +++ b/bl_pkg/bl_extension_utils.py @@ -558,6 +558,15 @@ class CommandBatchItem: return self.fn_with_args() +class CommandBatch_ExecNonBlockingResult(NamedTuple): + # A message list for each command, aligned to `CommandBatchItem._batch`. + messages: Tuple[List[Tuple[str, str]], ...] + # When true, the status of all commands is `CommandBatchItem.STATUS_COMPLETE`. + all_complete: bool + # When true, `calc_status_data` will return a different result. + status_data_changed: bool + + class CommandBatch_StatusFlag(NamedTuple): flag: int failure_count: int @@ -571,7 +580,6 @@ class CommandBatch: "_batch", "_request_exit", "_log_added_since_accessed", - "_status_data_cache", ) def __init__( @@ -584,7 +592,6 @@ class CommandBatch: self._batch = [CommandBatchItem(fn_with_args) for fn_with_args in batch] self._request_exit = False self._log_added_since_accessed = True - self._status_data_cache: Optional[CommandBatch_StatusFlag] = None def _exec_blocking_single( self, @@ -641,22 +648,16 @@ class CommandBatch: self, *, request_exit: bool, - ) -> Tuple[ - Tuple[List[Tuple[str, str]], ...], - bool, - ]: + ) -> CommandBatch_ExecNonBlockingResult: """ - For each command. - Return a tuple: - - list of commands for each command. - - all_complete: (true when every command has been completed). + Return the result of running multiple commands. """ command_output: Tuple[List[Tuple[str, str]], ...] = tuple([] for _ in range(len(self._batch))) if request_exit: self._request_exit = True - reset_status_data_cache = False + status_data_changed = False complete_count = 0 for cmd_index in reversed(range(len(self._batch))): @@ -671,7 +672,7 @@ class CommandBatch: if cmd.fn_iter is None: cmd.fn_iter = cmd.invoke() cmd.status = CommandBatchItem.STATUS_RUNNING - reset_status_data_cache = True + status_data_changed = True send_arg = None try: @@ -680,7 +681,7 @@ class CommandBatch: # FIXME: This should not happen, we should get a "DONE" instead. cmd.status = CommandBatchItem.STATUS_COMPLETE complete_count += 1 - reset_status_data_cache = True + status_data_changed = True continue if json_messages: @@ -693,7 +694,7 @@ class CommandBatch: assert msg == "" cmd.status = CommandBatchItem.STATUS_COMPLETE complete_count += 1 - reset_status_data_cache = True + status_data_changed = True break command_output[cmd_index].append((ty, msg)) @@ -701,20 +702,21 @@ class CommandBatch: if ty == 'ERROR': if not cmd.has_error: cmd.has_error = True - reset_status_data_cache = True + status_data_changed = True elif ty == 'WARNING': if not cmd.has_warning: cmd.has_warning = True - reset_status_data_cache = True + status_data_changed = True cmd.msg_log.append((ty, msg)) - if reset_status_data_cache: - self._status_data_cache = None - # Check if all are complete. assert complete_count == len([cmd for cmd in self._batch if cmd.status == CommandBatchItem.STATUS_COMPLETE]) all_complete = (complete_count == len(self._batch)) - return command_output, all_complete + return CommandBatch_ExecNonBlockingResult( + messages=command_output, + all_complete=all_complete, + status_data_changed=status_data_changed, + ) def calc_status_string(self) -> List[str]: return [ @@ -722,7 +724,10 @@ class CommandBatch: for cmd in self._batch if (cmd.msg_type or cmd.msg_info) ] - def _calc_status_data_no_cache(self) -> CommandBatch_StatusFlag: + def calc_status_data(self) -> CommandBatch_StatusFlag: + """ + A single string for all commands + """ status_flag = 0 failure_count = 0 for cmd in self._batch: @@ -735,18 +740,6 @@ class CommandBatch: count=len(self._batch), ) - def calc_status_data(self) -> CommandBatch_StatusFlag: - """ - A single string for all commands - """ - result = self._status_data_cache - if result is None: - result = self._status_data_cache = self._calc_status_data_no_cache() - else: - # Ensure the cache is properly reset. - assert result == self._calc_status_data_no_cache() - return result - @staticmethod def calc_status_text_icon_from_data(status_data: CommandBatch_StatusFlag, update_count: int) -> Tuple[str, str]: # Generate a nice UI string for a status-bar & splash screen (must be short). -- 2.11.4.GIT