def _print_error(err: AnyErr): if is_int(err): if err != 0: # 0 is success print(errno_str(err)) elif is_str(err): print(err) else: log.w(f"err expected of type int or str, found {type(err)}") log.w(stacktrace(color=ansi.FG_YELLOW))
def set_char_p(lib: CDLL, name: str, value: Union[bytes, str]): try: if is_str(value): value = c_stob(value) elif not is_bytes(value): raise TypeError(f"Exception bytes or str, found {type(value)}") c_char_p.in_dll(lib, name).value = value except Exception as e: log.w(f"set_char_p failed: {e}") raise CException(e)
def parse(self, args: Union[str, List[str]]) -> Optional[Args]: if is_str(args): args = self.split_args(args) if not is_list(args): raise TypeError(f"args must be a list, found {type(args)}") return Args.parse( args=args, positionals_spec=self.positionals_spec(), options_spec=self.options_spec(), continue_parsing_hook=self.continue_parsing_hook(), )
def create_error_of_response(err: Union[int, str], *subjects) -> Optional[ResponseError]: if is_int(err): # Consider err as an error number if not subjects: return {"errno": err} return {"errno": err, "subjects": [str(s) for s in subjects]} if is_str(err): # Consider err as a reason of a SPECIFIED_ERROR return {"errno": ServerErrors.GENERAL_ERROR, "subjects": err} return None
def quote_string(s: str) -> str: return "\"" + s + "\"" if is_str(s) else str(s)
def conv(o: Any): val = key(o) if key else o if is_str(val): val = val.lower() return val
def is_request(req: Request) -> bool: """ Returns whether 'req' is a valid request """ return \ req and \ is_dict(req) and \ is_str(req.get("api", None))
def _next_suggestion(self, token: str, count: int): """ Called by GNU readline when new suggestions have to be provided. Provide the next suggestion, or None if there is nothing more to suggest. """ def escape(s: str): return s.replace(" ", "\\ ") def unescape(s: str): return s.replace("\\ ", " ") try: log.d(f"next_suggestion, token='{token}' | count={count}") # Never insert trailing quote, we will do it manually # (this is needed because for directory completion we should not # insert the trailing quote) rl_set_completion_suppress_quote(1) is_quoting = rl_get_completion_quote_character() == ord('"') if count == 0: self._current_line = readline.get_line_buffer() # Take out the trailing white spaces, and in case a ; # is found, ignore everything before it (was another command inline) line = rightof(self._current_line, ";", from_end=True).lstrip() # Unescape since the token might contain \ we inserted in next_suggestion # for allow spaces in the line token = unescape(token) line = unescape(line) # Detect the command (first token of the line) by resolving aliases # and figure out if the command is unique for the given prefix log.d(f"line: '{line}'") resolved_line = self._resolve_alias(line, as_string=True) resolved_command = self._command_for(resolved_line, resolve_alias=False) log.d(f"resolved_line: '{resolved_line}'") log.d(f"resolved_command: '{resolved_command}'") no_suggestions = True # keep track, in order to propose local # files if shell passthrough is True self._suggestions_intent = SuggestionsIntent([]) for comm_name, comm_info in self._available_commands.items(): comm_resolved_name = comm_info.name( ) if comm_info else None log.d(f" > iterating, comm_name='{comm_name}'") if resolved_command == comm_name and re.match( Shell.COMM_SPACE_RE, line): # Typing a COMPLETE command # e.g. 'ls \t' log.d( f"Fetching suggestions for COMMAND INTENT '{comm_resolved_name}'" ) if comm_info: comms_sugg = comm_info.suggestions( line, token, self._client) if comms_sugg: # don't let it to be None self._suggestions_intent = comms_sugg log.d( f"Fetched ({len(self._suggestions_intent.suggestions)}) " f"suggestions INTENT for command '{comm_name}'" ) else: log.w( "Null comm info, maybe refers to a multi-command?" "Not providing suggestions for it") no_suggestions = False break # nothing more to complete, the command has been found if comm_name.startswith(line): # Typing an INCOMPLETE command # e.g. 'clos\t' # Case 1: complete command log.d( f"Adding suggestion for COMMAND COMPLETION of '{comm_resolved_name}'" ) self._suggestions_intent.suggestions.append( StyledString(comm_name)) no_suggestions = False # Translate the finding into the real name if the token # is exactly a finding if len(self._suggestions_intent.suggestions) == 1: log.d( "Just a suggestion, checking whether it is a finding pattern" ) the_suggestion = self._suggestions_intent.suggestions[0] findings = None if re.match(Shell.LOCAL_FINDINGS_RE, the_suggestion.string): findings = self._client.get_local_findings(token) elif re.match(Shell.REMOTE_FINDINGS_RE, the_suggestion.string): findings = self._client.get_remote_findings(token) if findings and len(findings) == 1: finding_info = findings[0] log.d( f"Found single finding for token: {finding_info}") self._suggestions_intent.suggestions.clear() self._suggestions_intent.suggestions.append( StyledString( str( Path(findings.path) / finding_info.get("name")))) no_suggestions = False # If there are no suggestions and we are doing shell passthrough # show the local files (probably the user command acts on those) if no_suggestions and get_setting(Settings.SHELL_PASSTHROUGH): log.d("Showing local files as suggestions as fallback, " "since shell passthrough is enabled") self._suggestions_intent = Ls.suggestions(line, token, self._client) \ or self._suggestions_intent if not self._suggestions_intent.completion: # TODO: find a way for not show the the suggestion inline # probably see https://tiswww.case.edu/php/chet/readline/readline.html#SEC45 # for now we add a dummy suggestion that we won't print in our # custom renderer self._suggestions_intent.suggestions.append( StyledString("")) self._suggestions_intent.suggestions = sorted( self._suggestions_intent.suggestions, key=lambda s: s.string.lower()) if count < len(self._suggestions_intent.suggestions): sug = self._suggestions_intent.suggestions[count].string # Eventually escape it if not is_quoting: sug = escape(sug) log.d(f"Returning suggestion {count}: {sug}") log.d( f"Completion is enabled = {self._suggestions_intent.completion}" ) # If there is only a suggestion that begins with # this name, complete the suggestion (and eventually insert a space) if self._suggestions_intent.completion and \ self._suggestions_intent.insert_after_completion and \ len(self._suggestions_intent.suggestions) == 1: if is_str( self._suggestions_intent.insert_after_completion): insert_after = self._suggestions_intent.insert_after_completion else: # is a hook insert_after = self._suggestions_intent.insert_after_completion( sug) if insert_after: log.d( "Last command with autocomplete -> adding required string" ) if insert_after == " " and is_quoting: # Insert the quote before the space sug += '"' sug += insert_after return sug log.d("END OF suggestions") return None except: log.w( f"Exception occurred while retrieving suggestions\n{traceback.format_exc()}" ) return None
def _execute_single_real(self, cmd: str) -> AnyErrs: if not is_str(cmd): log.e("Invalid command") return ClientErrors.INVALID_COMMAND_SYNTAX cmd = cmd.strip() if len(cmd) == 0: log.w("Empty command, nothing to do here") return ClientErrors.SUCCESS # no problem... log.d(f"Will try to execute '{cmd}'") if cmd.startswith("#"): log.d("Ignoring, it's a comment") return ClientErrors.SUCCESS log.d(f"Before alias resolution: {cmd}") # resolved_cmd_prefix, resolved_cmd_suffix = self._resolve_alias(cmd, as_string=False) resolved_cmd = self._resolve_alias(cmd, as_string=True) # log.d(f"resolved_cmd: {resolved_cmd}") # Resolved cmd can contain multiple command after alias resolution resolved_cmd_prefix, resolved_cmd_suffix = \ Shell._split_command_prefix_suffix(resolved_cmd, keep_space=True) log.d(f"resolved_cmd_prefix: {resolved_cmd_prefix}") log.d(f"resolved_cmd_suffix: {resolved_cmd_suffix}") # 'command_prefix' might be partial (unique prefix of a valid command) commands = self._commands_for(resolved_cmd_prefix, resolve_alias=False) log.d(f"Commands found: {commands}") # No command if len(commands) == 0: if get_setting(Settings.SHELL_PASSTHROUGH): log.d( "Passing unknown command to underlying shell due to passthrough" ) return self._client.execute_command(Commands.LOCAL_SHELL, cmd) return ClientErrors.COMMAND_NOT_RECOGNIZED # More than a command for this prefix if len(commands) > 1 and resolved_cmd_prefix not in commands: print("Available commands: ") for comm in isorted(commands): print( red(resolved_cmd_prefix) + comm[len(resolved_cmd_prefix):]) return ClientErrors.SUCCESS if len(commands) == 1: # Just 1 command found command = commands[0] else: # More than a command, but one matches exactly command = resolved_cmd_prefix # Exactly a known command, execute it try: outcome = ClientErrors.COMMAND_NOT_RECOGNIZED if self.has_command(command): outcome = self._execute_shell_command(command, resolved_cmd_suffix) elif self._client.has_command(command): outcome = self._client.execute_command(command, resolved_cmd_suffix) log.d(f"Command outcome: {outcome}") return outcome except ConnectionError: log.eexception("Connection error occurred") print_errors(ClientErrors.CONNECTION_ERROR) self._client.destroy_connection() except EOFError: log.i("\nCTRL+D: exiting") self._client.destroy_connection() # for consistency with CTRL+D typed while reading command, exit exit(0) except KeyboardInterrupt: log.d("\nCTRL+C")