From 53bfbf5acff3419b07c8d678a688be5459278ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 5 Jan 2016 17:22:18 +0100 Subject: [PATCH] Refactored things a bit to remove a bit of repetition --- src/octoprint/util/comm.py | 156 +++++++++++++++++++++++++------------ 1 file changed, 106 insertions(+), 50 deletions(-) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 0844b3e8..ed5265d5 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -2109,7 +2109,7 @@ class MachineCom(object): # trigger "sending" phase results = self._process_command_phase("sending", command, command_type, gcode=gcode) - if results == []: + if not results: # No, we are not going to send this, that was a last-minute bail. # However, since we already are in the send queue, our _monitor # loop won't be triggered with the reply from this unsent command @@ -2120,9 +2120,14 @@ class MachineCom(object): # and now let's fetch the next item from the queue continue - # no command expansion allowed in sending, use queuing instead - assert(len(results) == 1) - (command, _, gcode) = results[0] + # we explicitly throw away plugin hook results that try + # to perform command expansion in the sending/sent phase, + # so "results" really should only have more than one entry + # at this point if our core code contains a bug + assert len(results) == 1 + + # we only use the first (and only!) entry here + command, _, gcode = results[0] if command.strip() == "": self._logger.info("Refusing to send an empty line to the printer") @@ -2193,39 +2198,46 @@ class MachineCom(object): # send it through the phase specific handlers provided by plugins for name, hook in self._gcode_hooks[phase].items(): new_results = [] - for (command, command_type, gcode) in results: + for command, command_type, gcode in results: try: hook_results = hook(self, phase, command, command_type, gcode) except: self._logger.exception("Error while processing hook {name} for phase {phase} and command {command}:".format(**locals())) else: - new_results += self._handle_command_handler_result(command, command_type, gcode, hook_results) - if new_results == []: - # hook handler returned None for all commands, so we'll stop here and return a full out None result + normalized = self._handle_command_handler_result(command, command_type, gcode, hook_results) + + # make sure we don't allow multi entry results in sending and sent phase + if phase in ("sending", "sent") and len(normalized) > 1: + self._logger.error("Error while processing hook {name} for phase {phase} and command {command}: Hook returned multi-entry result for phase {phase} and command {command}. That's not supported, if you need to do multi expansion of commands you need to do this in the queuing phase. Ignoring hook result and sending command as-is.".format(**locals())) + new_results.append((command, command_type, gcode)) + else: + new_results += normalized + if not new_results: + # hook handler returned None or empty list for all commands, so we'll stop here and return a full out empty result return [] results = new_results # if it's a gcode command send it through the specific handler if it exists new_results = [] - for (command, command_type, gcode) in results: + for command, command_type, gcode in results: if gcode is not None: - gcodeHandler = "_gcode_" + gcode + "_" + phase - if hasattr(self, gcodeHandler): - handler_results = getattr(self, gcodeHandler)(command, cmd_type=command_type) + gcode_handler = "_gcode_" + gcode + "_" + phase + if hasattr(self, gcode_handler): + handler_results = getattr(self, gcode_handler)(command, cmd_type=command_type) new_results += self._handle_command_handler_result(command, command_type, gcode, handler_results) else: new_results.append((command, command_type, gcode)) - if new_results == []: - # gcode handler returned None for all commands, so we'll stop here and return a full out None result + if not new_results: + # gcode handler returned None or empty list for all commands, so we'll stop here and return a full out empty result return [] results = new_results # send it through the phase specific command handler if it exists - commandPhaseHandler = "_command_phase_" + phase - if hasattr(self, commandPhaseHandler): + command_phase_handler = "_command_phase_" + phase + if hasattr(self, command_phase_handler): new_results = [] - for (command, command_type, gcode) in results: - handler_results = getattr(self, commandPhaseHandler)(command, cmd_type=command_type, gcode=gcode) + for command, command_type, gcode in results: + handler_results = getattr(self, command_phase_handler)(command, cmd_type=command_type, gcode=gcode) new_results += self._handle_command_handler_result(command, command_type, gcode, handler_results) results = new_results @@ -2233,46 +2245,90 @@ class MachineCom(object): return results def _handle_command_handler_result(self, command, command_type, gcode, handler_results): - default = [(command, command_type, gcode)] + """ + Normalizes a command handler results. + + Handler results can be either ``None``, a single result entry or a list of result + entries. + + ``None`` results are ignored, the provided ``command``, ``command_type`` + and ``gcode`` are returned in that case (as single-entry list with one + 3-tuple as entry). + + Single result entries are either: + + * a single string defining a replacement ``command`` + * a 1-tuple defining a replacement ``command`` + * a 2-tuple defining a replacement ``command`` and ``command_type`` + + A ``command`` that is ``None`` will lead to the entry being ignored for + the normalized result. + + The method returns a list of normalized result entries. Normalized result + entries always are a 3-tuple consisting of ``command``, ``command_type`` + and ``gcode``, the latter two being allowed to be ``None``. The list may + be empty in which case the command is to be suppressed. + + Parameters: + command (str or None): The command for which the handler result was + generated + command_type (str or None): The command type for which the handler + result was generated + gcode (str or None): The GCODE for which the handler result was + generated + handler_results: The handler result(s) to normalized. Can be either + a single result entry or a list of result entries. + + Returns: + (list) - A list of normalized handler result entries, which are + 3-tuples consisting of ``command``, ``command_type`` and + ``gcode``, the latter two of which may be ``None``. + """ if handler_results is None: # handler didn't return anything, we'll just continue - return default - - if isinstance(handler_results, basestring): - # handler did return just a string, replace command with it - command = handler_result - gcode = gcode_command_for_cmd(command) return [(command, command_type, gcode)] - elif isinstance(handler_results, tuple): - # handler did return a tuple, extract command and command_type - hook_result_length = len(handler_results) - if hook_result_length == 1: - # handler returned just the command - command, = handler_results - elif hook_result_length == 2: - # handler returned command and command_type - command, command_type = handler_results - else: - # handler returned a tuple of an unexpected length - return default + if not isinstance(handler_results, list): + handler_results = [handler_results,] - gcode = gcode_command_for_cmd(command) - return [(command, command_type, gcode)] + result = [] + for handler_result in handler_results: + # we iterate over all handler result entries and process each one + # individually here - elif isinstance(handler_results, list): - # handler did return a list of commands or commands + command_type tuples - if isinstance(handler_results[0], tuple): - # handler returned command and command_type - return [(command, command_type, gcode_command_for_cmd(command)) for (command, command_type) in handler_results] - else: - # handler returned just the commands - return [(command, command_type, gcode_command_for_cmd(command)) for command in handler_results] + if handler_result is None: + # entry is None, we'll ignore that entry and continue + continue - else: - # handler didn't return an expected result format, we'll just ignore it and continue - return default + if isinstance(handler_result, basestring): + # entry is just a string, replace command with it + command = handler_result + gcode = gcode_command_for_cmd(command) + result.append((command, command_type, gcode)) + + elif isinstance(handler_result, tuple): + # entry is a tuple, extract command and command_type + hook_result_length = len(handler_results) + if hook_result_length == 1: + # handler returned just the command + command, = handler_results + elif hook_result_length == 2: + # handler returned command and command_type + command, command_type = handler_results + else: + # handler returned a tuple of an unexpected length, ignore + # and continue + continue + + if command is None: + # command is None, ignore it and continue + continue + + gcode = gcode_command_for_cmd(command) + result.append((command, command_type, gcode)) + + return result ##~~ actual sending via serial