Skip to content

Commit 555f023

Browse files
committed
Suppress KeyboardInterrupt in portions of py/pyscript where the interactive console isn't running
1 parent dc6590f commit 555f023

File tree

1 file changed

+157
-141
lines changed

1 file changed

+157
-141
lines changed

cmd2/cmd2.py

Lines changed: 157 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -2957,161 +2957,170 @@ def do_py(self, args: argparse.Namespace) -> bool:
29572957
self.perror(err, traceback_war=False)
29582958
self._last_result = CommandResult('', err)
29592959
return False
2960-
self._in_py = True
29612960

2962-
# Support the run command even if called prior to invoking an interactive interpreter
2963-
def py_run(filename: str):
2964-
"""Run a Python script file in the interactive console.
2965-
:param filename: filename of *.py script file to run
2966-
"""
2967-
expanded_filename = os.path.expanduser(filename)
2961+
try:
2962+
self._in_py = True
29682963

2969-
# cmd_echo defaults to False for scripts. The user can always toggle this value in their script.
2970-
bridge.cmd_echo = False
2964+
# Support the run command even if called prior to invoking an interactive interpreter
2965+
def py_run(filename: str):
2966+
"""Run a Python script file in the interactive console.
2967+
:param filename: filename of *.py script file to run
2968+
"""
2969+
expanded_filename = os.path.expanduser(filename)
29712970

2972-
try:
2973-
with open(expanded_filename) as f:
2974-
interp.runcode(f.read())
2975-
except OSError as ex:
2976-
error_msg = "Error opening script file '{}': {}".format(expanded_filename, ex)
2977-
self.perror(error_msg, traceback_war=False)
2978-
2979-
def py_quit():
2980-
"""Function callable from the interactive Python console to exit that environment"""
2981-
raise EmbeddedConsoleExit
2982-
2983-
# Set up Python environment
2984-
bridge = PyscriptBridge(self)
2985-
self.pystate[self.pyscript_name] = bridge
2986-
self.pystate['run'] = py_run
2987-
self.pystate['quit'] = py_quit
2988-
self.pystate['exit'] = py_quit
2989-
2990-
if self.locals_in_py:
2991-
self.pystate['self'] = self
2992-
elif 'self' in self.pystate:
2993-
del self.pystate['self']
2994-
2995-
localvars = self.pystate
2996-
from code import InteractiveConsole
2997-
interp = InteractiveConsole(locals=localvars)
2998-
interp.runcode('import sys, os;sys.path.insert(0, os.getcwd())')
2999-
3000-
# Check if the user is running a Python statement on the command line
3001-
if args.command:
3002-
full_command = args.command
3003-
if args.remainder:
3004-
full_command += ' ' + ' '.join(args.remainder)
3005-
3006-
# Set cmd_echo to True so PyscriptBridge statements like: py app('help')
3007-
# run at the command line will print their output.
3008-
bridge.cmd_echo = True
3009-
3010-
# noinspection PyBroadException
3011-
try:
3012-
interp.runcode(full_command)
3013-
except BaseException:
3014-
pass
2971+
# cmd_echo defaults to False for scripts. The user can always toggle this value in their script.
2972+
bridge.cmd_echo = False
30152973

3016-
# If there are no args, then we will open an interactive Python console
3017-
else:
3018-
# Set up readline for Python console
3019-
if rl_type != RlType.NONE:
3020-
# Save cmd2 history
3021-
saved_cmd2_history = []
3022-
for i in range(1, readline.get_current_history_length() + 1):
3023-
saved_cmd2_history.append(readline.get_history_item(i))
2974+
try:
2975+
with open(expanded_filename) as f:
2976+
interp.runcode(f.read())
2977+
except OSError as ex:
2978+
error_msg = "Error opening script file '{}': {}".format(expanded_filename, ex)
2979+
self.perror(error_msg, traceback_war=False)
30242980

3025-
readline.clear_history()
2981+
def py_quit():
2982+
"""Function callable from the interactive Python console to exit that environment"""
2983+
raise EmbeddedConsoleExit
30262984

3027-
# Restore py's history
3028-
for item in self.py_history:
3029-
readline.add_history(item)
3030-
3031-
if self.use_rawinput and self.completekey:
3032-
# Set up tab completion for the Python console
3033-
# rlcompleter relies on the default settings of the Python readline module
3034-
if rl_type == RlType.GNU:
3035-
old_basic_quotes = ctypes.cast(rl_basic_quote_characters, ctypes.c_void_p).value
3036-
rl_basic_quote_characters.value = orig_rl_basic_quotes
3037-
3038-
if 'gnureadline' in sys.modules:
3039-
# rlcompleter imports readline by name, so it won't use gnureadline
3040-
# Force rlcompleter to use gnureadline instead so it has our settings and history
3041-
saved_readline = None
3042-
if 'readline' in sys.modules:
3043-
saved_readline = sys.modules['readline']
3044-
3045-
sys.modules['readline'] = sys.modules['gnureadline']
3046-
3047-
old_delims = readline.get_completer_delims()
3048-
readline.set_completer_delims(orig_rl_delims)
3049-
3050-
# rlcompleter will not need cmd2's custom display function
3051-
# This will be restored by cmd2 the next time complete() is called
3052-
if rl_type == RlType.GNU:
3053-
readline.set_completion_display_matches_hook(None)
3054-
elif rl_type == RlType.PYREADLINE:
3055-
readline.rl.mode._display_completions = self._display_matches_pyreadline
3056-
3057-
# Save off the current completer and set a new one in the Python console
3058-
# Make sure it tab completes from its locals() dictionary
3059-
old_completer = readline.get_completer()
3060-
interp.runcode("from rlcompleter import Completer")
3061-
interp.runcode("import readline")
3062-
interp.runcode("readline.set_completer(Completer(locals()).complete)")
3063-
3064-
# Set up sys module for the Python console
3065-
self._reset_py_display()
3066-
keepstate = Statekeeper(sys, ('stdin', 'stdout'))
3067-
sys.stdout = self.stdout
3068-
sys.stdin = self.stdin
3069-
3070-
cprt = 'Type "help", "copyright", "credits" or "license" for more information.'
3071-
instructions = ('End with `Ctrl-D` (Unix) / `Ctrl-Z` (Windows), `quit()`, `exit()`.\n'
3072-
'Non-Python commands can be issued with: {}("your command")\n'
3073-
'Run Python code from external script files with: run("script.py")'
3074-
.format(self.pyscript_name))
3075-
3076-
# noinspection PyBroadException
3077-
try:
3078-
interp.interact(banner="Python {} on {}\n{}\n\n{}\n".
3079-
format(sys.version, sys.platform, cprt, instructions))
3080-
except BaseException:
3081-
pass
2985+
# Set up Python environment
2986+
bridge = PyscriptBridge(self)
2987+
self.pystate[self.pyscript_name] = bridge
2988+
self.pystate['run'] = py_run
2989+
self.pystate['quit'] = py_quit
2990+
self.pystate['exit'] = py_quit
30822991

3083-
finally:
3084-
keepstate.restore()
2992+
if self.locals_in_py:
2993+
self.pystate['self'] = self
2994+
elif 'self' in self.pystate:
2995+
del self.pystate['self']
2996+
2997+
localvars = self.pystate
2998+
from code import InteractiveConsole
2999+
interp = InteractiveConsole(locals=localvars)
3000+
interp.runcode('import sys, os;sys.path.insert(0, os.getcwd())')
3001+
3002+
# Check if the user is running a Python statement on the command line
3003+
if args.command:
3004+
full_command = args.command
3005+
if args.remainder:
3006+
full_command += ' ' + ' '.join(args.remainder)
3007+
3008+
# Set cmd_echo to True so PyscriptBridge statements like: py app('help')
3009+
# run at the command line will print their output.
3010+
bridge.cmd_echo = True
3011+
3012+
# noinspection PyBroadException
3013+
try:
3014+
interp.runcode(full_command)
3015+
except BaseException:
3016+
# We don't care about any exception that happened in the interactive console
3017+
pass
30853018

3086-
# Set up readline for cmd2
3019+
# If there are no args, then we will open an interactive Python console
3020+
else:
3021+
# Set up readline for Python console
30873022
if rl_type != RlType.NONE:
3088-
# Save py's history
3089-
self.py_history.clear()
3023+
# Save cmd2 history
3024+
saved_cmd2_history = []
30903025
for i in range(1, readline.get_current_history_length() + 1):
3091-
self.py_history.append(readline.get_history_item(i))
3026+
saved_cmd2_history.append(readline.get_history_item(i))
30923027

30933028
readline.clear_history()
30943029

3095-
# Restore cmd2's history
3096-
for item in saved_cmd2_history:
3030+
# Restore py's history
3031+
for item in self.py_history:
30973032
readline.add_history(item)
30983033

30993034
if self.use_rawinput and self.completekey:
3100-
# Restore cmd2's tab completion settings
3101-
readline.set_completer(old_completer)
3102-
readline.set_completer_delims(old_delims)
3103-
3035+
# Set up tab completion for the Python console
3036+
# rlcompleter relies on the default settings of the Python readline module
31043037
if rl_type == RlType.GNU:
3105-
rl_basic_quote_characters.value = old_basic_quotes
3038+
old_basic_quotes = ctypes.cast(rl_basic_quote_characters, ctypes.c_void_p).value
3039+
rl_basic_quote_characters.value = orig_rl_basic_quotes
31063040

31073041
if 'gnureadline' in sys.modules:
3108-
# Restore what the readline module pointed to
3109-
if saved_readline is None:
3110-
del(sys.modules['readline'])
3111-
else:
3112-
sys.modules['readline'] = saved_readline
3042+
# rlcompleter imports readline by name, so it won't use gnureadline
3043+
# Force rlcompleter to use gnureadline instead so it has our settings and history
3044+
saved_readline = None
3045+
if 'readline' in sys.modules:
3046+
saved_readline = sys.modules['readline']
3047+
3048+
sys.modules['readline'] = sys.modules['gnureadline']
3049+
3050+
old_delims = readline.get_completer_delims()
3051+
readline.set_completer_delims(orig_rl_delims)
3052+
3053+
# rlcompleter will not need cmd2's custom display function
3054+
# This will be restored by cmd2 the next time complete() is called
3055+
if rl_type == RlType.GNU:
3056+
readline.set_completion_display_matches_hook(None)
3057+
elif rl_type == RlType.PYREADLINE:
3058+
readline.rl.mode._display_completions = self._display_matches_pyreadline
3059+
3060+
# Save off the current completer and set a new one in the Python console
3061+
# Make sure it tab completes from its locals() dictionary
3062+
old_completer = readline.get_completer()
3063+
interp.runcode("from rlcompleter import Completer")
3064+
interp.runcode("import readline")
3065+
interp.runcode("readline.set_completer(Completer(locals()).complete)")
3066+
3067+
# Set up sys module for the Python console
3068+
self._reset_py_display()
3069+
keepstate = Statekeeper(sys, ('stdin', 'stdout'))
3070+
sys.stdout = self.stdout
3071+
sys.stdin = self.stdin
3072+
3073+
cprt = 'Type "help", "copyright", "credits" or "license" for more information.'
3074+
instructions = ('End with `Ctrl-D` (Unix) / `Ctrl-Z` (Windows), `quit()`, `exit()`.\n'
3075+
'Non-Python commands can be issued with: {}("your command")\n'
3076+
'Run Python code from external script files with: run("script.py")'
3077+
.format(self.pyscript_name))
3078+
3079+
# noinspection PyBroadException
3080+
try:
3081+
interp.interact(banner="Python {} on {}\n{}\n\n{}\n".
3082+
format(sys.version, sys.platform, cprt, instructions))
3083+
except BaseException:
3084+
# We don't care about any exception that happened in the interactive console
3085+
pass
3086+
3087+
finally:
3088+
keepstate.restore()
3089+
3090+
# Set up readline for cmd2
3091+
if rl_type != RlType.NONE:
3092+
# Save py's history
3093+
self.py_history.clear()
3094+
for i in range(1, readline.get_current_history_length() + 1):
3095+
self.py_history.append(readline.get_history_item(i))
3096+
3097+
readline.clear_history()
3098+
3099+
# Restore cmd2's history
3100+
for item in saved_cmd2_history:
3101+
readline.add_history(item)
3102+
3103+
if self.use_rawinput and self.completekey:
3104+
# Restore cmd2's tab completion settings
3105+
readline.set_completer(old_completer)
3106+
readline.set_completer_delims(old_delims)
3107+
3108+
if rl_type == RlType.GNU:
3109+
rl_basic_quote_characters.value = old_basic_quotes
3110+
3111+
if 'gnureadline' in sys.modules:
3112+
# Restore what the readline module pointed to
3113+
if saved_readline is None:
3114+
del(sys.modules['readline'])
3115+
else:
3116+
sys.modules['readline'] = saved_readline
3117+
3118+
except KeyboardInterrupt:
3119+
pass
3120+
3121+
finally:
3122+
self._in_py = False
31133123

3114-
self._in_py = False
31153124
return self._should_quit
31163125

31173126
pyscript_parser = ACArgumentParser()
@@ -3125,18 +3134,25 @@ def py_quit():
31253134
def do_pyscript(self, args: argparse.Namespace) -> bool:
31263135
"""Run a Python script file inside the console"""
31273136
script_path = os.path.expanduser(args.script_path)
3137+
py_return = False
31283138

31293139
# Save current command line arguments
31303140
orig_args = sys.argv
31313141

3132-
# Overwrite sys.argv to allow the script to take command line arguments
3133-
sys.argv = [script_path] + args.script_arguments
3142+
try:
3143+
# Overwrite sys.argv to allow the script to take command line arguments
3144+
sys.argv = [script_path] + args.script_arguments
31343145

3135-
# Run the script - use repr formatting to escape things which need to be escaped to prevent issues on Windows
3136-
py_return = self.do_py("run({!r})".format(script_path))
3146+
# Run the script - use repr formatting to escape things which
3147+
# need to be escaped to prevent issues on Windows
3148+
py_return = self.do_py("run({!r})".format(script_path))
31373149

3138-
# Restore command line arguments to original state
3139-
sys.argv = orig_args
3150+
except KeyboardInterrupt:
3151+
pass
3152+
3153+
finally:
3154+
# Restore command line arguments to original state
3155+
sys.argv = orig_args
31403156

31413157
return py_return
31423158

0 commit comments

Comments
 (0)