diff options
author | anselor <anselor@gmail.com> | 2018-05-02 15:29:45 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-05-02 15:29:45 -0400 |
commit | d37004d05d0be67f48f35d6986ea899a35bcf89f (patch) | |
tree | b4575eda772450b54620d16c09d4d29c52abdec7 /cmd2/cmd2.py | |
parent | 01e4e0296adf57c920c1e12c3c92f45f615894b1 (diff) | |
parent | a95c8a065abeac286c196783393ecc49e4356f54 (diff) | |
download | cmd2-git-d37004d05d0be67f48f35d6986ea899a35bcf89f.tar.gz |
Merge pull request #379 from python-cmd2/pyscript
This merges pyscript and bash_completion branches into master
Diffstat (limited to 'cmd2/cmd2.py')
-rwxr-xr-x | cmd2/cmd2.py | 61 |
1 files changed, 31 insertions, 30 deletions
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 7f5b21d2..80fa5601 100755 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -180,21 +180,6 @@ def _which(editor: str) -> Optional[str]: return editor_path -def strip_quotes(arg: str) -> str: - """ Strip outer quotes from a string. - - Applies to both single and double quotes. - - :param arg: string to strip outer quotes from - :return: same string with potentially outer quotes stripped - """ - quote_chars = '"' + "'" - - if len(arg) > 1 and arg[0] == arg[-1] and arg[0] in quote_chars: - arg = arg[1:-1] - return arg - - def parse_quoted_string(cmdline: str) -> List[str]: """Parse a quoted string into a list of arguments.""" if isinstance(cmdline, list): @@ -207,7 +192,7 @@ def parse_quoted_string(cmdline: str) -> List[str]: if not POSIX_SHLEX and STRIP_QUOTES_FOR_NON_POSIX: temp_arglist = [] for arg in lexed_arglist: - temp_arglist.append(strip_quotes(arg)) + temp_arglist.append(utils.strip_quotes(arg)) lexed_arglist = temp_arglist return lexed_arglist @@ -246,8 +231,12 @@ def with_argparser_and_unknown_args(argparser: argparse.ArgumentParser) -> Calla @functools.wraps(func) def cmd_wrapper(instance, cmdline): lexed_arglist = parse_quoted_string(cmdline) - args, unknown = argparser.parse_known_args(lexed_arglist) - return func(instance, args, unknown) + try: + args, unknown = argparser.parse_known_args(lexed_arglist) + except SystemExit: + return + else: + return func(instance, args, unknown) # argparser defaults the program name to sys.argv[0] # we want it to be the name of our command @@ -283,8 +272,12 @@ def with_argparser(argparser: argparse.ArgumentParser) -> Callable: @functools.wraps(func) def cmd_wrapper(instance, cmdline): lexed_arglist = parse_quoted_string(cmdline) - args = argparser.parse_args(lexed_arglist) - return func(instance, args) + try: + args = argparser.parse_args(lexed_arglist) + except SystemExit: + return + else: + return func(instance, args) # argparser defaults the program name to sys.argv[0] # we want it to be the name of our command @@ -373,7 +366,7 @@ def replace_with_file_contents(fname: str) -> str: """ try: # Any outer quotes are not part of the filename - unquoted_file = strip_quotes(fname[0]) + unquoted_file = utils.strip_quotes(fname[0]) with open(os.path.expanduser(unquoted_file)) as source_file: result = source_file.read() except IOError: @@ -698,7 +691,7 @@ class Cmd(cmd.Cmd): if _which(editor): break feedback_to_output = False # Do not include nonessentials in >, | output by default (things like timing) - locals_in_py = True + locals_in_py = False quiet = False # Do not suppress nonessential output timing = False # Prints elapsed time for each command @@ -760,6 +753,7 @@ class Cmd(cmd.Cmd): self.initial_stdout = sys.stdout self.history = History() self.pystate = {} + self.pyscript_name = 'app' self.keywords = self.reserved_words + [fname[3:] for fname in dir(self) if fname.startswith('do_')] self.parser_manager = ParserManager(redirector=self.redirector, terminators=self.terminators, multilineCommands=self.multilineCommands, @@ -1108,7 +1102,7 @@ class Cmd(cmd.Cmd): raw_tokens = initial_tokens # Save the unquoted tokens - tokens = [strip_quotes(cur_token) for cur_token in raw_tokens] + tokens = [utils.strip_quotes(cur_token) for cur_token in raw_tokens] # If the token being completed had an unclosed quote, we need # to remove the closing quote that was added in order for it @@ -2079,6 +2073,8 @@ class Cmd(cmd.Cmd): if self.allow_redirection: self._redirect_output(statement) timestart = datetime.datetime.now() + if self._in_py: + self._last_result = None statement = self.precmd(statement) stop = self.onecmd(statement) stop = self.postcmd(stop, statement) @@ -2830,7 +2826,7 @@ Usage: Usage: unalias [-a] name [name ...] # an unclosed quote, so we only need to check the first character. first_char = tokens[index][0] if first_char in constants.QUOTES: - tokens[index] = strip_quotes(tokens[index]) + tokens[index] = utils.strip_quotes(tokens[index]) tokens[index] = os.path.expanduser(tokens[index]) @@ -2862,16 +2858,16 @@ Usage: Usage: unalias [-a] name [name ...] py <command>: Executes a Python command. py: Enters interactive Python mode. End with ``Ctrl-D`` (Unix) / ``Ctrl-Z`` (Windows), ``quit()``, '`exit()``. - Non-python commands can be issued with ``cmd("your command")``. + Non-python commands can be issued with ``pyscript_name("your command")``. Run python code from external script files with ``run("script.py")`` """ + from .pyscript_bridge import PyscriptBridge if self._in_py: self.perror("Recursively entering interactive Python consoles is not allowed.", traceback_war=False) return self._in_py = True try: - self.pystate['self'] = self arg = arg.strip() # Support the run command even if called prior to invoking an interactive interpreter @@ -2894,10 +2890,14 @@ Usage: Usage: unalias [-a] name [name ...] """ return self.onecmd_plus_hooks(cmd_plus_args + '\n') + bridge = PyscriptBridge(self) self.pystate['run'] = run - self.pystate['cmd'] = onecmd_plus_hooks + self.pystate[self.pyscript_name] = bridge + + if self.locals_in_py: + self.pystate['self'] = self - localvars = (self.locals_in_py and self.pystate) or {} + localvars = self.pystate interp = InteractiveConsole(locals=localvars) interp.runcode('import sys, os;sys.path.insert(0, os.getcwd())') @@ -2918,9 +2918,10 @@ Usage: Usage: unalias [-a] name [name ...] keepstate = Statekeeper(sys, ('stdin', 'stdout')) sys.stdout = self.stdout sys.stdin = self.stdin + docstr = self.do_py.__doc__.replace('pyscript_name', self.pyscript_name) interp.interact(banner="Python %s on %s\n%s\n(%s)\n%s" % (sys.version, sys.platform, cprt, self.__class__.__name__, - self.do_py.__doc__)) + docstr)) except EmbeddedConsoleExit: pass if keepstate is not None: @@ -3342,7 +3343,7 @@ class ParserManager: ignore=do_not_parse)('pipeTo')) + \ pyparsing.Optional(output_destination_parser + pyparsing.SkipTo(string_end, ignore=do_not_parse). - setParseAction(lambda x: strip_quotes(x[0].strip()))('outputTo')) + setParseAction(lambda x: utils.strip_quotes(x[0].strip()))('outputTo')) multilineCommand.setParseAction(lambda x: x[0]) oneline_command.setParseAction(lambda x: x[0]) |