diff options
-rw-r--r-- | CHANGELOG.md | 7 | ||||
-rw-r--r-- | cmd2/ansi.py | 4 | ||||
-rw-r--r-- | cmd2/argparse_custom.py | 2 | ||||
-rw-r--r-- | cmd2/cmd2.py | 66 | ||||
-rw-r--r-- | tests/test_run_pyscript.py | 2 |
5 files changed, 57 insertions, 24 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index f9ace9c4..01180001 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.0.3 (TBD, 2020) +* Enhancements + * Made `ipy` consistent with `py` in the following ways + * `ipy` returns whether any of the commands run in it returned True to stop command loop + * `Cmd.in_pyscript()` returns True while in `ipy`. + * Starting `ipy` when `Cmd.in_pyscript()` is already True is not allowed. + ## 1.0.2 (April 06, 2020) * Bug Fixes * Ctrl-C now stops a running text script instead of just the current `run_script` command diff --git a/cmd2/ansi.py b/cmd2/ansi.py index ceb135ec..a6c09413 100644 --- a/cmd2/ansi.py +++ b/cmd2/ansi.py @@ -196,7 +196,7 @@ def fg_lookup(fg_name: Union[str, fg]) -> str: :param fg_name: foreground color name or enum to look up ANSI escape code(s) for :return: ANSI escape code(s) associated with this color - :raises ValueError: if the color cannot be found + :raises: ValueError: if the color cannot be found """ if isinstance(fg_name, fg): return fg_name.value @@ -214,7 +214,7 @@ def bg_lookup(bg_name: Union[str, bg]) -> str: :param bg_name: background color name or enum to look up ANSI escape code(s) for :return: ANSI escape code(s) associated with this color - :raises ValueError: if the color cannot be found + :raises: ValueError: if the color cannot be found """ if isinstance(bg_name, bg): return bg_name.value diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py index d6833673..c24d8d9a 100644 --- a/cmd2/argparse_custom.py +++ b/cmd2/argparse_custom.py @@ -387,7 +387,7 @@ def _add_argument_wrapper(self, *args, See the header of this file for more information :return: the created argument action - :raises ValueError on incorrect parameter usage + :raises: ValueError on incorrect parameter usage """ # Verify consistent use of arguments choices_callables = [choices_function, choices_method, completer_function, completer_method] diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 2fcdedc3..6809e5db 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -536,6 +536,7 @@ class Cmd(cmd.Cmd): def pfeedback(self, msg: Any, *, end: str = '\n') -> None: """For printing nonessential feedback. Can be silenced with `quiet`. Inclusion in redirected output is controlled by `feedback_to_output`. + :param msg: message to print (anything convertible to a str with '{}'.format() is OK) :param end: string appended after the end of the message, default a newline """ @@ -1946,7 +1947,7 @@ class Cmd(cmd.Cmd): :param statement: a parsed statement from the user :return: A bool telling if an error occurred and a utils.RedirectionSavedState object - :raises RedirectionError if an error occurs trying to pipe or redirect + :raises: RedirectionError if an error occurs trying to pipe or redirect """ import io import subprocess @@ -2274,11 +2275,12 @@ class Cmd(cmd.Cmd): def read_input(self, prompt: str, *, allow_completion: bool = False) -> str: """ Read input from appropriate stdin value. Also allows you to disable tab completion while input is being read. + :param prompt: prompt to display to user :param allow_completion: if True, then tab completion of commands is enabled. This generally should be - set to False unless reading the command line. Defaults to False. + set to False unless reading the command line. Defaults to False. :return: the line read from stdin with all trailing new lines removed - :raises any exceptions raised by input() and stdin.readline() + :raises: any exceptions raised by input() and stdin.readline() """ completion_disabled = False orig_completer = None @@ -2352,7 +2354,7 @@ class Cmd(cmd.Cmd): :param prompt: prompt to display to user :return: command line text of 'eof' if an EOFError was caught - :raises whatever exceptions are raised by input() except for EOFError + :raises: whatever exceptions are raised by input() except for EOFError """ try: # Wrap in try since terminal_lock may not be locked @@ -2371,6 +2373,7 @@ class Cmd(cmd.Cmd): def _set_up_cmd2_readline(self) -> _SavedReadlineSettings: """ Set up readline with cmd2-specific settings + :return: Class containing saved readline settings """ readline_settings = _SavedReadlineSettings() @@ -2405,6 +2408,7 @@ class Cmd(cmd.Cmd): def _restore_readline(self, readline_settings: _SavedReadlineSettings): """ Restore saved readline settings + :param readline_settings: the readline settings to restore """ if self._completion_supported(): @@ -3297,6 +3301,7 @@ class Cmd(cmd.Cmd): def _restore_cmd2_env(self, cmd2_env: _SavedCmd2Env) -> None: """ Restore cmd2 environment after exiting an interactive Python shell + :param cmd2_env: the environment settings to restore """ sys.stdout = cmd2_env.sys_stdout @@ -3354,6 +3359,7 @@ class Cmd(cmd.Cmd): def do_py(self, args: argparse.Namespace) -> Optional[bool]: """ Enter an interactive Python shell + :return: True if running of commands should stop """ def py_quit(): @@ -3365,8 +3371,7 @@ class Cmd(cmd.Cmd): saved_sys_path = None if self.in_pyscript(): - err = "Recursively entering interactive Python consoles is not allowed." - self.perror(err) + self.perror("Recursively entering interactive Python shells is not allowed") return try: @@ -3473,7 +3478,8 @@ class Cmd(cmd.Cmd): def do_run_pyscript(self, args: argparse.Namespace) -> Optional[bool]: """ Run a Python script file inside the console - :return: True if running of commands should stop + + :return: True if running of commands should stop """ # Expand ~ before placing this path in sys.argv just as a shell would args.script_path = os.path.expanduser(args.script_path) @@ -3507,19 +3513,21 @@ class Cmd(cmd.Cmd): ipython_parser = DEFAULT_ARGUMENT_PARSER(description="Enter an interactive IPython shell") @with_argparser(ipython_parser) - def do_ipy(self, _: argparse.Namespace) -> None: - """Enter an interactive IPython shell""" + def do_ipy(self, _: argparse.Namespace) -> Optional[bool]: + """ + Enter an interactive IPython shell + + :return: True if running of commands should stop + """ from .py_bridge import PyBridge - banner = ('Entering an embedded IPython shell. Type quit or <Ctrl>-d to exit.\n' - 'Run Python code from external files with: run filename.py\n') - exit_msg = 'Leaving IPython, back to {}'.format(sys.argv[0]) # noinspection PyUnusedLocal def load_ipy(cmd2_app: Cmd, py_bridge: PyBridge): """ Embed an IPython shell in an environment that is restricted to only the variables in this function + :param cmd2_app: instance of the cmd2 app - :param py_bridge: a PyscriptBridge + :param py_bridge: a PyBridge """ # Create a variable pointing to py_bridge and name it using the value of py_bridge_name exec("{} = py_bridge".format(cmd2_app.py_bridge_name)) @@ -3532,9 +3540,22 @@ class Cmd(cmd.Cmd): del cmd2_app del py_bridge - embed(banner1=banner, exit_msg=exit_msg) + # Start ipy shell + embed(banner1=('Entering an embedded IPython shell. Type quit or <Ctrl>-d to exit.\n' + 'Run Python code from external files with: run filename.py\n'), + exit_msg='Leaving IPython, back to {}'.format(sys.argv[0])) + + if self.in_pyscript(): + self.perror("Recursively entering interactive Python shells is not allowed") + return - load_ipy(self, PyBridge(self)) + try: + self._in_py = True + new_py_bridge = PyBridge(self) + load_ipy(self, new_py_bridge) + return new_py_bridge.stop + finally: + self._in_py = False history_description = "View, run, edit, save, or clear previously entered commands" @@ -3576,6 +3597,7 @@ class Cmd(cmd.Cmd): def do_history(self, args: argparse.Namespace) -> Optional[bool]: """ View, run, edit, save, or clear previously entered commands + :return: True if running of commands should stop """ @@ -3748,7 +3770,7 @@ class Cmd(cmd.Cmd): atexit.register(self._persist_history) def _persist_history(self): - """write history out to the history file""" + """Write history out to the history file""" if not self.persistent_history_file: return @@ -3761,9 +3783,7 @@ class Cmd(cmd.Cmd): self.pexcept(msg.format(self.persistent_history_file, ex)) def _generate_transcript(self, history: List[Union[HistoryItem, str]], transcript_file: str) -> None: - """ - Generate a transcript file from a given history of commands - """ + """Generate a transcript file from a given history of commands""" # Validate the transcript file path to make sure directory exists and write access is available transcript_path = os.path.abspath(os.path.expanduser(transcript_file)) transcript_dir = os.path.dirname(transcript_path) @@ -3866,8 +3886,9 @@ class Cmd(cmd.Cmd): def _run_editor(self, file_path: Optional[str]) -> None: """ Run a text editor and optionally open a file with it + :param file_path: optional path of the file to edit - :raises EnvironmentError if self.editor is not set + :raises: EnvironmentError if self.editor is not set """ if not self.editor: raise EnvironmentError("Please use 'set editor' to specify your text editing program of choice.") @@ -3977,6 +3998,7 @@ class Cmd(cmd.Cmd): def do__relative_run_script(self, args: argparse.Namespace) -> Optional[bool]: """ Run commands in script file that is encoded as either ASCII or UTF-8 text + :return: True if running of commands should stop """ file_path = args.file_path @@ -4159,6 +4181,7 @@ class Cmd(cmd.Cmd): def enable_command(self, command: str) -> None: """ Enable a command by restoring its functions + :param command: the command being enabled """ # If the commands is already enabled, then return @@ -4190,6 +4213,7 @@ class Cmd(cmd.Cmd): def enable_category(self, category: str) -> None: """ Enable an entire category of commands + :param category: the category to enable """ for cmd_name in list(self.disabled_commands): @@ -4200,6 +4224,7 @@ class Cmd(cmd.Cmd): def disable_command(self, command: str, message_to_print: str) -> None: """ Disable a command and overwrite its functions + :param command: the command being disabled :param message_to_print: what to print when this command is run or help is called on it while disabled @@ -4254,6 +4279,7 @@ class Cmd(cmd.Cmd): def _report_disabled_command_usage(self, *_args, message_to_print: str, **_kwargs) -> None: """ Report when a disabled command has been run or had help called on it + :param args: not used :param message_to_print: the message reporting that the command is disabled :param kwargs: not used diff --git a/tests/test_run_pyscript.py b/tests/test_run_pyscript.py index 12257fc5..6110e3ac 100644 --- a/tests/test_run_pyscript.py +++ b/tests/test_run_pyscript.py @@ -35,7 +35,7 @@ def test_run_pyscript(base_app, request): def test_run_pyscript_recursive_not_allowed(base_app, request): test_dir = os.path.dirname(request.module.__file__) python_script = os.path.join(test_dir, 'pyscript', 'recursive.py') - expected = 'Recursively entering interactive Python consoles is not allowed.' + expected = 'Recursively entering interactive Python shells is not allowed' out, err = run_cmd(base_app, "run_pyscript {}".format(python_script)) assert err[0] == expected |