Skip to content

Commit 911d965

Browse files
authored
Merge pull request #623 from python-cmd2/pyscript_quit
Fixed issue where calling exit() or quit() from a pyscript would close the whole console
2 parents 7d93134 + 555f023 commit 911d965

File tree

3 files changed

+46
-26
lines changed

3 files changed

+46
-26
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
## 0.9.8 (TBD, 2019)
1+
## 0.9.8 (February 06, 2019)
22
* Bug Fixes
33
* Fixed issue with echoing strings in StdSim. Because they were being sent to a binary buffer, line buffering
44
was being ignored.
5+
* Enhancements
6+
* Made quit() and exit() functions available to scripts run with pyscript. This allows those scripts to exit
7+
back to the console's prompt instead of exiting the whole application.
58

69
## 0.9.7 (January 08, 2019)
710
* Bug Fixes

cmd2/cmd2.py

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2957,14 +2957,13 @@ 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-
# noinspection PyBroadException
29632961
try:
2962+
self._in_py = True
2963+
29642964
# Support the run command even if called prior to invoking an interactive interpreter
2965-
def run(filename: str):
2965+
def py_run(filename: str):
29662966
"""Run a Python script file in the interactive console.
2967-
29682967
:param filename: filename of *.py script file to run
29692968
"""
29702969
expanded_filename = os.path.expanduser(filename)
@@ -2979,9 +2978,16 @@ def run(filename: str):
29792978
error_msg = "Error opening script file '{}': {}".format(expanded_filename, ex)
29802979
self.perror(error_msg, traceback_war=False)
29812980

2981+
def py_quit():
2982+
"""Function callable from the interactive Python console to exit that environment"""
2983+
raise EmbeddedConsoleExit
2984+
2985+
# Set up Python environment
29822986
bridge = PyscriptBridge(self)
2983-
self.pystate['run'] = run
29842987
self.pystate[self.pyscript_name] = bridge
2988+
self.pystate['run'] = py_run
2989+
self.pystate['quit'] = py_quit
2990+
self.pystate['exit'] = py_quit
29852991

29862992
if self.locals_in_py:
29872993
self.pystate['self'] = self
@@ -3002,18 +3008,16 @@ def run(filename: str):
30023008
# Set cmd_echo to True so PyscriptBridge statements like: py app('help')
30033009
# run at the command line will print their output.
30043010
bridge.cmd_echo = True
3005-
interp.runcode(full_command)
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
30063018

30073019
# If there are no args, then we will open an interactive Python console
30083020
else:
3009-
# noinspection PyShadowingBuiltins
3010-
def quit():
3011-
"""Function callable from the interactive Python console to exit that environment"""
3012-
raise EmbeddedConsoleExit
3013-
3014-
self.pystate['quit'] = quit
3015-
self.pystate['exit'] = quit
3016-
30173021
# Set up readline for Python console
30183022
if rl_type != RlType.NONE:
30193023
# Save cmd2 history
@@ -3072,10 +3076,12 @@ def quit():
30723076
'Run Python code from external script files with: run("script.py")'
30733077
.format(self.pyscript_name))
30743078

3079+
# noinspection PyBroadException
30753080
try:
30763081
interp.interact(banner="Python {} on {}\n{}\n\n{}\n".
30773082
format(sys.version, sys.platform, cprt, instructions))
3078-
except EmbeddedConsoleExit:
3083+
except BaseException:
3084+
# We don't care about any exception that happened in the interactive console
30793085
pass
30803086

30813087
finally:
@@ -3109,10 +3115,12 @@ def quit():
31093115
else:
31103116
sys.modules['readline'] = saved_readline
31113117

3112-
except Exception:
3118+
except KeyboardInterrupt:
31133119
pass
3120+
31143121
finally:
31153122
self._in_py = False
3123+
31163124
return self._should_quit
31173125

31183126
pyscript_parser = ACArgumentParser()
@@ -3123,21 +3131,30 @@ def quit():
31233131
ACTION_ARG_CHOICES, ('path_complete',))
31243132

31253133
@with_argparser(pyscript_parser)
3126-
def do_pyscript(self, args: argparse.Namespace) -> None:
3134+
def do_pyscript(self, args: argparse.Namespace) -> bool:
31273135
"""Run a Python script file inside the console"""
31283136
script_path = os.path.expanduser(args.script_path)
3137+
py_return = False
31293138

31303139
# Save current command line arguments
31313140
orig_args = sys.argv
31323141

3133-
# Overwrite sys.argv to allow the script to take command line arguments
3134-
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
3145+
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))
31353149

3136-
# Run the script - use repr formatting to escape things which need to be escaped to prevent issues on Windows
3137-
self.do_py("run({!r})".format(script_path))
3150+
except KeyboardInterrupt:
3151+
pass
3152+
3153+
finally:
3154+
# Restore command line arguments to original state
3155+
sys.argv = orig_args
31383156

3139-
# Restore command line arguments to original state
3140-
sys.argv = orig_args
3157+
return py_return
31413158

31423159
# Only include the do_ipy() method if IPython is available on the system
31433160
if ipython_available: # pragma: no cover

examples/scripts/conditional.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
./python_scripting.py
88
pyscript scripts/conditional.py directory_path
99
10-
Note: The "cmd" function is defined within the cmd2 embedded Python environment and in there "self" is your cmd2
11-
application instance.
10+
Note: The "app" function is defined within the cmd2 embedded Python environment and in there "self" is your cmd2
11+
application instance. Note: self only exists in this environment if locals_in_py is True.
1212
"""
1313
import os
1414
import sys

0 commit comments

Comments
 (0)