summaryrefslogtreecommitdiff
path: root/cmd2/cmd2.py
diff options
context:
space:
mode:
authoranselor <anselor@gmail.com>2018-05-02 15:29:45 -0400
committerGitHub <noreply@github.com>2018-05-02 15:29:45 -0400
commitd37004d05d0be67f48f35d6986ea899a35bcf89f (patch)
treeb4575eda772450b54620d16c09d4d29c52abdec7 /cmd2/cmd2.py
parent01e4e0296adf57c920c1e12c3c92f45f615894b1 (diff)
parenta95c8a065abeac286c196783393ecc49e4356f54 (diff)
downloadcmd2-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-xcmd2/cmd2.py61
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])