diff options
author | Todd Leonhardt <todd.leonhardt@gmail.com> | 2019-06-15 10:53:06 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-06-15 10:53:06 -0400 |
commit | c12ba0ff11b3a8fd083c641cb9149aff6494bbf9 (patch) | |
tree | a858864c091088e633de6ba6c8db55fd618a71e7 | |
parent | b3759991adca62779ef7aefbff9dc7004463e129 (diff) | |
parent | 6a122ae91b6a3fabc3709b1d488843715258e58c (diff) | |
download | cmd2-git-c12ba0ff11b3a8fd083c641cb9149aff6494bbf9.tar.gz |
Merge pull request #701 from python-cmd2/rename
Rename load, _relative_load, and pyscript
30 files changed, 324 insertions, 306 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 5eb3d40b..a66cad15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ * Added support for and testing with Python 3.8, starting with 3.8 beta * Breaking Changes * Python 3.4 reached its [end of life](https://www.python.org/dev/peps/pep-0429/) on March 18, 2019 and is no longer supported by `cmd2` +* **Renamed Commands Notice** + * The following commands have been renamed. The old names will be supported until the next release. + * load --> run_script + * _relative_load --> _relative_run_script + * pyscript --> run_pyscript ## 0.9.13 (June 14, 2019) * Bug Fixes @@ -35,7 +35,7 @@ examples/tab_au*.py @anselor examples/tab_co*.py @kmvanbrunt # Unit Tests -tests/pyscript/* @anselor +tests/pyscript/* @anselor @kmvanbrunt tests/transcripts/* @kotfu tests/__init__.py @kotfu tests/conftest.py @kotfu @tleonhardt @@ -20,8 +20,8 @@ Click on image below to watch a short video demonstrating the capabilities of cm Main Features ------------- - Searchable command history (`history` command and `<Ctrl>+r`) - optionally persistent -- Text file scripting of your application with `load` (`@`) and `_relative_load` (`@@`) -- Python scripting of your application with ``pyscript`` +- Text file scripting of your application with `run_script` (`@`) and `_relative_run_script` (`@@`) +- Python scripting of your application with ``run_pyscript`` - Run shell commands with ``!`` - Pipe command output to shell commands with `|` - Redirect command output to file with `>`, `>>` @@ -32,7 +32,7 @@ Main Features - Special-character command shortcuts (beyond cmd's `?` and `!`) - Command aliasing similar to bash `alias` command - Macros, which are similar to aliases, but they can contain argument placeholders -- Ability to load commands at startup from an initialization script +- Ability to run commands at startup from an initialization script - Settable environment parameters - Parsing commands with arguments using `argparse`, including support for sub-commands - Unicode character support @@ -41,7 +41,7 @@ Main Features - Support for Python 3.5+ on Windows, macOS, and Linux - Trivial to provide built-in help for all commands - Built-in regression testing framework for your applications (transcript-based testing) -- Transcripts for use with built-in regression can be automatically generated from `history -t` or `load -t` +- Transcripts for use with built-in regression can be automatically generated from `history -t` or `run_script -t` - Alerts that seamlessly print while user enters text at prompt Python 2.7 support is EOL @@ -109,12 +109,12 @@ Instructions for implementing each feature follow. - Simple scripting using ASCII text files with one command + arguments per line - See the [Script files](https://cmd2.readthedocs.io/en/latest/freefeatures.html#script-files) section of the `cmd2` docs for more info - See [script.txt](https://github.com/python-cmd2/cmd2/blob/master/examples/scripts/script.txt) for a trivial example script that can be - used in any `cmd2` application with the `load` command (or `@` shortcut) + used in any `cmd2` application with the `run_script` command (or `@` shortcut) -- Powerful and flexible built-in Python scripting of your application using the `pyscript` command +- Powerful and flexible built-in Python scripting of your application using the `run_pyscript` command - Run arbitrary Python scripts within your `cmd2` application with the ability to also call custom `cmd2` commands - No separate API for your end users to learn - - Syntax for calling `cmd2` commands in a `pyscript` is essentially identical to what they would enter on the command line + - Syntax for calling `cmd2` commands in a `run_pyscript` is essentially identical to what they would enter on the command line - See the [Python](https://cmd2.readthedocs.io/en/latest/freefeatures.html#python) section of the `cmd2` docs for more info - Also see the [python_scripting.py](https://github.com/python-cmd2/cmd2/blob/master/examples/python_scripting.py) example in conjunction with the [conditional.py](https://github.com/python-cmd2/cmd2/blob/master/examples/scripts/conditional.py) script diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 7fcb38d6..46b098c5 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -6,9 +6,9 @@ To use, simply import cmd2.Cmd instead of cmd.Cmd; use precisely as though you were using the standard library's cmd, while enjoying the extra features. Searchable command history (commands: "history") -Load commands from file, save to file, edit commands in file +Run commands from file, save to file, edit commands in file Multi-line commands -Special-character shortcut commands (beyond cmd's "@" and "!") +Special-character shortcut commands (beyond cmd's "?" and "!") Settable environment parameters Parsing commands with `argparse` argument parsers (flags) Redirection to file or paste buffer (clipboard) with > or >> @@ -327,7 +327,7 @@ class Cmd(cmd.Cmd): Line-oriented command interpreters are often useful for test harnesses, internal tools, and rapid prototypes. """ - DEFAULT_SHORTCUTS = {'?': 'help', '!': 'shell', '@': 'load', '@@': '_relative_load'} + DEFAULT_SHORTCUTS = {'?': 'help', '!': 'shell', '@': 'run_script', '@@': '_relative_run_script'} DEFAULT_EDITOR = utils.find_editor() def __init__(self, completekey: str = 'tab', stdin=None, stdout=None, *, @@ -343,7 +343,7 @@ class Cmd(cmd.Cmd): :param stdout: (optional) alternate output file object, if not specified, sys.stdout is used :param persistent_history_file: (optional) file path to load a persistent cmd2 command history from :param persistent_history_length: (optional) max number of history items to write to the persistent history file - :param startup_script: (optional) file path to a a script to load and execute at startup + :param startup_script: (optional) file path to a script to execute at startup :param use_ipython: (optional) should the "ipy" command be included for an embedded IPython shell :param allow_cli_args: (optional) if True, then cmd2 will process command line arguments as either commands to be run or, if -t is specified, transcript files to run. @@ -398,7 +398,7 @@ class Cmd(cmd.Cmd): 'timing': 'Report execution times'} # Commands to exclude from the help menu and tab completion - self.hidden_commands = ['eof', '_relative_load'] + self.hidden_commands = ['eof', '_relative_load', '_relative_run_script'] # Commands to exclude from the history command # initialize history @@ -429,7 +429,7 @@ class Cmd(cmd.Cmd): # Built-in commands don't make use of this. It is purely there for user-defined commands and convenience. self._last_result = None - # Used load command to store the current script dir as a LIFO queue to support _relative_load command + # Used by run_script command to store current script dir as a LIFO queue to support _relative_run_script command self._script_dir = [] # Context manager used to protect critical sections in the main thread from stopping due to a KeyboardInterrupt @@ -460,11 +460,11 @@ class Cmd(cmd.Cmd): # Commands that will run at the beginning of the command loop self._startup_commands = [] - # If a startup script is provided, then add it in the queue to load + # If a startup script is provided, then execute it in the startup commands if startup_script is not None: startup_script = os.path.abspath(os.path.expanduser(startup_script)) if os.path.exists(startup_script) and os.path.getsize(startup_script) > 0: - self._startup_commands.append("load '{}'".format(startup_script)) + self._startup_commands.append("run_script '{}'".format(startup_script)) # Transcript files to run instead of interactive command loop self._transcript_files = None @@ -3240,15 +3240,15 @@ class Cmd(cmd.Cmd): return bridge.stop - pyscript_parser = ACArgumentParser() - setattr(pyscript_parser.add_argument('script_path', help='path to the script file'), + run_pyscript_parser = ACArgumentParser() + setattr(run_pyscript_parser.add_argument('script_path', help='path to the script file'), ACTION_ARG_CHOICES, ('path_complete',)) - setattr(pyscript_parser.add_argument('script_arguments', nargs=argparse.REMAINDER, - help='arguments to pass to script'), + setattr(run_pyscript_parser.add_argument('script_arguments', nargs=argparse.REMAINDER, + help='arguments to pass to script'), ACTION_ARG_CHOICES, ('path_complete',)) - @with_argparser(pyscript_parser) - def do_pyscript(self, args: argparse.Namespace) -> bool: + @with_argparser(run_pyscript_parser) + def do_run_pyscript(self, args: argparse.Namespace) -> bool: """Run a Python script file inside the console""" script_path = os.path.expanduser(args.script_path) py_return = False @@ -3270,9 +3270,16 @@ class Cmd(cmd.Cmd): finally: # Restore command line arguments to original state sys.argv = orig_args + if args.__statement__.command == "pyscript": + self.perror("pyscript has been renamed and will be removed in the next release, " + "please use run_pyscript instead\n", + traceback_war=False, err_color=Fore.LIGHTYELLOW_EX) return py_return + # pyscript is deprecated + do_pyscript = do_run_pyscript + # Only include the do_ipy() method if IPython is available on the system if ipython_available: # pragma: no cover @with_argparser(ACArgumentParser()) @@ -3412,7 +3419,7 @@ class Cmd(cmd.Cmd): fobj.write('{}\n'.format(command.raw)) try: self.do_edit(fname) - return self.do_load(fname) + return self.do_run_script(fname) finally: os.remove(fname) elif args.output_file: @@ -3623,94 +3630,115 @@ class Cmd(cmd.Cmd): else: return None - load_description = ("Run commands in script file that is encoded as either ASCII or UTF-8 text\n" - "\n" - "Script should contain one command per line, just like the command would be\n" - "typed in the console.\n" - "\n" - "If the -r/--record_transcript flag is used, this command instead records\n" - "the output of the script commands to a transcript for testing purposes.\n" - ) + run_script_description = ("Run commands in script file that is encoded as either ASCII or UTF-8 text\n" + "\n" + "Script should contain one command per line, just like the command would be\n" + "typed in the console.\n" + "\n" + "If the -r/--record_transcript flag is used, this command instead records\n" + "the output of the script commands to a transcript for testing purposes.\n" + ) - load_parser = ACArgumentParser(description=load_description) - setattr(load_parser.add_argument('-t', '--transcript', help='record the output of the script as a transcript file'), + run_script_parser = ACArgumentParser(description=run_script_description) + setattr(run_script_parser.add_argument('-t', '--transcript', + help='record the output of the script as a transcript file'), ACTION_ARG_CHOICES, ('path_complete',)) - setattr(load_parser.add_argument('script_path', help="path to the script file"), + setattr(run_script_parser.add_argument('script_path', help="path to the script file"), ACTION_ARG_CHOICES, ('path_complete',)) - @with_argparser(load_parser) - def do_load(self, args: argparse.Namespace) -> Optional[bool]: + @with_argparser(run_script_parser) + def do_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 """ expanded_path = os.path.abspath(os.path.expanduser(args.script_path)) - # Make sure the path exists and we can access it - if not os.path.exists(expanded_path): - self.perror("'{}' does not exist or cannot be accessed".format(expanded_path), traceback_war=False) - return + # Wrap everything in a try/finally just to make sure the warning prints at end if `load` was called + try: + # Make sure the path exists and we can access it + if not os.path.exists(expanded_path): + self.perror("'{}' does not exist or cannot be accessed".format(expanded_path), traceback_war=False) + return - # Make sure expanded_path points to a file - if not os.path.isfile(expanded_path): - self.perror("'{}' is not a file".format(expanded_path), traceback_war=False) - return + # Make sure expanded_path points to a file + if not os.path.isfile(expanded_path): + self.perror("'{}' is not a file".format(expanded_path), traceback_war=False) + return - # Make sure the file is not empty - if os.path.getsize(expanded_path) == 0: - self.perror("'{}' is empty".format(expanded_path), traceback_war=False) - return + # Make sure the file is not empty + if os.path.getsize(expanded_path) == 0: + self.perror("'{}' is empty".format(expanded_path), traceback_war=False) + return - # Make sure the file is ASCII or UTF-8 encoded text - if not utils.is_text_file(expanded_path): - self.perror("'{}' is not an ASCII or UTF-8 encoded text file".format(expanded_path), traceback_war=False) - return + # Make sure the file is ASCII or UTF-8 encoded text + if not utils.is_text_file(expanded_path): + self.perror("'{}' is not an ASCII or UTF-8 encoded text file".format(expanded_path), traceback_war=False) + return - try: - # Read all lines of the script - with open(expanded_path, encoding='utf-8') as target: - script_commands = target.read().splitlines() - except OSError as ex: # pragma: no cover - self.perror("Problem accessing script from '{}': {}".format(expanded_path, ex)) - return + try: + # Read all lines of the script + with open(expanded_path, encoding='utf-8') as target: + script_commands = target.read().splitlines() + except OSError as ex: # pragma: no cover + self.perror("Problem accessing script from '{}': {}".format(expanded_path, ex)) + return - orig_script_dir_count = len(self._script_dir) + orig_script_dir_count = len(self._script_dir) - try: - self._script_dir.append(os.path.dirname(expanded_path)) + try: + self._script_dir.append(os.path.dirname(expanded_path)) - if args.transcript: - self._generate_transcript(script_commands, os.path.expanduser(args.transcript)) - else: - return self.runcmds_plus_hooks(script_commands) + if args.transcript: + self._generate_transcript(script_commands, os.path.expanduser(args.transcript)) + else: + return self.runcmds_plus_hooks(script_commands) + finally: + with self.sigint_protection: + # Check if a script dir was added before an exception occurred + if orig_script_dir_count != len(self._script_dir): + self._script_dir.pop() finally: - with self.sigint_protection: - # Check if a script dir was added before an exception occurred - if orig_script_dir_count != len(self._script_dir): - self._script_dir.pop() + if args.__statement__.command == "load": + self.perror("load has been renamed and will be removed in the next release, " + "please use run_script instead\n", + traceback_war=False, err_color=Fore.LIGHTYELLOW_EX) - relative_load_description = load_description - relative_load_description += ("\n\n" - "If this is called from within an already-running script, the filename will be\n" - "interpreted relative to the already-running script's directory.") + # load has been deprecated + do_load = do_run_script - relative_load_epilog = ("Notes:\n" - " This command is intended to only be used within text file scripts.") + relative_run_script_description = run_script_description + relative_run_script_description += ( + "\n\n" + "If this is called from within an already-running script, the filename will be\n" + "interpreted relative to the already-running script's directory.") - relative_load_parser = ACArgumentParser(description=relative_load_description, epilog=relative_load_epilog) - relative_load_parser.add_argument('file_path', help='a file path pointing to a script') + relative_run_script_epilog = ("Notes:\n" + " This command is intended to only be used within text file scripts.") - @with_argparser(relative_load_parser) - def do__relative_load(self, args: argparse.Namespace) -> Optional[bool]: + relative_run_script_parser = ACArgumentParser(description=relative_run_script_description, + epilog=relative_run_script_epilog) + relative_run_script_parser.add_argument('file_path', help='a file path pointing to a script') + + @with_argparser(relative_run_script_parser) + 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 """ + if args.__statement__.command == "_relative_load": + self.perror("_relative_load has been renamed and will be removed in the next release, " + "please use _relative_run_script instead\n", + traceback_war=False, err_color=Fore.LIGHTYELLOW_EX) + file_path = args.file_path # NOTE: Relative path is an absolute path, it is just relative to the current script directory relative_path = os.path.join(self._current_script_dir or '', file_path) - return self.do_load(relative_path) + return self.do_run_script(relative_path) + + # _relative_load has been deprecated + do__relative_load = do__relative_run_script def run_transcript_tests(self, transcript_paths: List[str]) -> None: """Runs transcript tests for provided file(s). diff --git a/cmd2/pyscript_bridge.py b/cmd2/pyscript_bridge.py index 31c1ab9c..01d283ea 100644 --- a/cmd2/pyscript_bridge.py +++ b/cmd2/pyscript_bridge.py @@ -1,6 +1,6 @@ # coding=utf-8 """ -Bridges calls made inside of pyscript with the Cmd2 host app while maintaining a reasonable +Bridges calls made inside of a pyscript with the Cmd2 host app while maintaining a reasonable degree of isolation between the two """ diff --git a/docs/freefeatures.rst b/docs/freefeatures.rst index 12421601..a06bab90 100644 --- a/docs/freefeatures.rst +++ b/docs/freefeatures.rst @@ -13,24 +13,15 @@ Script files ============ Text files can serve as scripts for your ``cmd2``-based -application, with the ``load``, ``_relative_load``, and ``edit`` commands. +application, with the ``run_script``, ``_relative_run_script``, and ``edit`` commands. Both ASCII and UTF-8 encoded unicode text files are supported. Simply include one command per line, typed exactly as you would inside a ``cmd2`` application. -The ``load`` command loads commands from a script file into a queue and then the normal cmd2 REPL -resumes control and executes the commands in the queue in FIFO order. A side effect of this -is that if you redirect/pipe the output of a load command, it will redirect the output of the ``load`` -command itself, but will NOT redirect the output of the command loaded from the script file. Of course, -you can add redirection to the commands being run in the script file, e.g.:: +.. automethod:: cmd2.cmd2.Cmd.do_run_script - # This is your script file - command arg1 arg2 > file.txt - -.. automethod:: cmd2.cmd2.Cmd.do_load - -.. automethod:: cmd2.cmd2.Cmd.do__relative_load +.. automethod:: cmd2.cmd2.Cmd.do__relative_run_script .. automethod:: cmd2.cmd2.Cmd.do_edit @@ -51,7 +42,7 @@ Comments can be useful in :ref:`scripts`, but would be pointless within an inter Startup Initialization Script ============================= -You can load and execute commands from a startup initialization script by passing a file path to the ``startup_script`` +You can execute commands from a startup initialization script by passing a file path to the ``startup_script`` argument to the ``cmd2.Cmd.__init__()`` method like so:: class StartupApp(cmd2.Cmd): @@ -188,16 +179,16 @@ conditional control flow logic. See the **python_scripting.py** ``cmd2`` applic the **script_conditional.py** script in the ``examples`` source code directory for an example of how to achieve this in your own applications. -Using ``py`` to run scripts directly is considered deprecated. The newer ``pyscript`` command +Using ``py`` to run scripts directly is considered deprecated. The newer ``run_pyscript`` command is superior for doing this in two primary ways: - it supports tab-completion of file system paths - it has the ability to pass command-line arguments to the scripts invoked -There are no disadvantages to using ``pyscript`` as opposed to ``py run()``. A simple example -of using ``pyscript`` is shown below along with the arg_printer_ script:: +There are no disadvantages to using ``run_pyscript`` as opposed to ``py run()``. A simple example +of using ``run_pyscript`` is shown below along with the arg_printer_ script:: - (Cmd) pyscript examples/scripts/arg_printer.py foo bar baz + (Cmd) run_pyscript examples/scripts/arg_printer.py foo bar baz Running Python script 'arg_printer.py' which was called with 3 arguments arg 1: 'foo' arg 2: 'bar' @@ -390,7 +381,7 @@ would:: If you want to save the commands to a text file, but not edit and re-run them, use the ``-o`` or ``--output-file`` option. This is a great way to create -:ref:`scripts`, which can be loaded and executed using the ``load`` command. To +:ref:`scripts`, which can be executed using the ``run_script`` command. To save the first 5 commands entered in this session to a text file:: (Cmd) history :5 -o history.txt @@ -512,8 +503,8 @@ Tab-Completion ``cmd2`` adds tab-completion of file system paths for all built-in commands where it makes sense, including: - ``edit`` -- ``load`` -- ``pyscript`` +- ``run_pyscript`` +- ``run_script`` - ``shell`` ``cmd2`` also adds tab-completion of shell commands to the ``shell`` command. diff --git a/docs/integrating.rst b/docs/integrating.rst index a8377fdb..352bb2f0 100644 --- a/docs/integrating.rst +++ b/docs/integrating.rst @@ -45,8 +45,9 @@ loop:: Documented commands (type help <topic>): ======================================== - alias help load orate pyscript say shell speak - edit history mumble py quit set shortcuts unalias + alias history mumble pyscript run_script shell + edit load orate quit say shortcuts + help macro py run_pyscript set speak (Cmd) @@ -128,9 +129,7 @@ the main loop for the program by using code like the following:: app.postloop() The **runcmds_plus_hooks()** method is a convenience method to run multiple -commands via **onecmd_plus_hooks()**. It properly deals with ``load`` commands -which under the hood put commands in a FIFO queue as it reads them in from a -script file. +commands via **onecmd_plus_hooks()**. The **onecmd_plus_hooks()** method will do the following to execute a single ``cmd2`` command in a normal fashion: diff --git a/docs/settingchanges.rst b/docs/settingchanges.rst index b9ad4a22..aa6d9084 100644 --- a/docs/settingchanges.rst +++ b/docs/settingchanges.rst @@ -23,10 +23,10 @@ following shortcuts are defined: shell: run as OS-level command ``@`` - load script file + run script file ``@@`` - load script file; filename is relative to current script location + run script file; filename is relative to current script location To define more shortcuts, update the dict ``App.shortcuts`` with the {'shortcut': 'command_name'} (omit ``do_``):: diff --git a/docs/transcript.rst b/docs/transcript.rst index c7c31fd6..089ab704 100644 --- a/docs/transcript.rst +++ b/docs/transcript.rst @@ -34,9 +34,9 @@ This is by far the easiest way to generate a transcript. Automatically from a script file -------------------------------- -A transcript can also be automatically generated from a script file using ``load -t``:: +A transcript can also be automatically generated from a script file using ``run_script -t``:: - (Cmd) load scripts/script.txt -t transcript.txt + (Cmd) run_script scripts/script.txt -t transcript.txt 2 commands and their outputs saved to transcript file 'transcript.txt' (Cmd) diff --git a/docs/unfreefeatures.rst b/docs/unfreefeatures.rst index 81be76d8..ada3a2f6 100644 --- a/docs/unfreefeatures.rst +++ b/docs/unfreefeatures.rst @@ -359,10 +359,10 @@ the help categories with per-command Help Messages:: edit Edit a file in a text editor help List available commands with "help" or detailed help with "help cmd" history usage: history [-h] [-r | -e | -s | -o FILE | -t TRANSCRIPT] [arg] - load Runs commands in script file that is encoded as either ASCII or UTF-8 text py Invoke python command, shell, or script - pyscript Runs a python script file inside the console quit Exits this application + run_pyscript Runs a python script file inside the console + run_script Runs commands in script file that is encoded as either ASCII or UTF-8 text set usage: set [-h] [-a] [-l] [settable [settable ...]] shell Execute a command as if at the OS prompt shortcuts Lists shortcuts available diff --git a/examples/alias_startup.py b/examples/alias_startup.py index b765e34c..052d1367 100755 --- a/examples/alias_startup.py +++ b/examples/alias_startup.py @@ -2,7 +2,7 @@ # coding=utf-8 """A simple example demonstrating the following: 1) How to add custom command aliases using the alias command - 2) How to load an initialization script at startup + 2) How to run an initialization script at startup """ import os import cmd2 diff --git a/examples/hooks.py b/examples/hooks.py index c533c696..42224403 100755 --- a/examples/hooks.py +++ b/examples/hooks.py @@ -38,10 +38,10 @@ class CmdLineApp(cmd2.Cmd): # Setting this true makes it run a shell command if a cmd2/cmd command doesn't exist # default_to_shell = True def __init__(self, *args, **kwargs): - # sneakily remove the cmd2.Cmd command called load + # sneakily remove the cmd2.Cmd command called run_script # this lets a user enter a command like "l5" and allows it to # be unambiguous - delattr(cmd2.Cmd, "do_load") + delattr(cmd2.Cmd, "do_run_script") super().__init__(*args, **kwargs) diff --git a/examples/persistent_history.py b/examples/persistent_history.py index e88fd5d9..bc62cb14 100755 --- a/examples/persistent_history.py +++ b/examples/persistent_history.py @@ -11,9 +11,9 @@ import cmd2 class Cmd2PersistentHistory(cmd2.Cmd): """Basic example of how to enable persistent readline history within your cmd2 app.""" def __init__(self, hist_file): - """Configure the app to load persistent readline history from a file. + """Configure the app to load persistent history from a file (both readline and cmd2 history command affected). - :param hist_file: file to load readline history from at start and write it to at end + :param hist_file: file to load history from at start and write it to at end """ super().__init__(persistent_history_file=hist_file, persistent_history_length=500, allow_cli_args=False) self.prompt = 'ph> ' diff --git a/examples/python_scripting.py b/examples/python_scripting.py index da7d0f6a..3e8f64ef 100755 --- a/examples/python_scripting.py +++ b/examples/python_scripting.py @@ -2,15 +2,15 @@ # coding=utf-8 """A sample application for how Python scripting can provide conditional control flow of a cmd2 application. -cmd2's built-in scripting capability which can be invoked via the "@" shortcut or "load" command and uses basic ASCII -text scripts is very easy to use. Moreover, the trivial syntax of the script files where there is one command per line -and the line is exactly what the user would type inside the application makes it so non-technical end users can quickly -learn to create scripts. +cmd2's built-in scripting capability, which can be invoked via the "@" shortcut or "run_script" command, uses basic +ASCII/UTF-8 text scripts and is very easy to use. Moreover, the trivial syntax of the script files, where there is one +command per line and the line is exactly what the user would type inside the application, makes it so non-technical +that end users can quickly learn to create scripts. However, there comes a time when technical end users want more capability and power. In particular it is common that users will want to create a script with conditional control flow - where the next command run will depend on the results -from the previous command. This is where the ability to run Python scripts inside a cmd2 application via the pyscript -command and the "pyscript <script> [arguments]" syntax comes into play. +from the previous command. This is where the ability to run Python scripts inside a cmd2 application via the +run_pyscript command and the "run_pyscript <script> [arguments]" syntax comes into play. This application and the "scripts/conditional.py" script serve as an example for one way in which this can be done. """ diff --git a/examples/scripts/conditional.py b/examples/scripts/conditional.py index 8a4f14de..724bb3ee 100644 --- a/examples/scripts/conditional.py +++ b/examples/scripts/conditional.py @@ -5,7 +5,7 @@ This is a Python script intended to be used with the "python_scripting.py" cmd2 To run it you should do the following: ./python_scripting.py - pyscript scripts/conditional.py directory_path + run_pyscript scripts/conditional.py directory_path Note: The "app" function is defined within the cmd2 embedded Python environment and in there "self" is your cmd2 application instance. Note: self only exists in this environment if locals_in_py is True. diff --git a/examples/scripts/nested.txt b/examples/scripts/nested.txt index 9ec26160..1b7e2035 100644 --- a/examples/scripts/nested.txt +++ b/examples/scripts/nested.txt @@ -1,3 +1,3 @@ -!echo "Doing a relative load" -_relative_load script.txt - +!echo "Doing a relative run script" +_relative_run_script script.txt + diff --git a/examples/scripts/save_help_text.py b/examples/scripts/save_help_text.py index 73434a31..a68b0ad9 100644 --- a/examples/scripts/save_help_text.py +++ b/examples/scripts/save_help_text.py @@ -2,7 +2,7 @@ # flake8: noqa F821 """ A cmd2 script that saves the help text for every command, sub-command, and topic to a file. -This is meant to be run within a cmd2 session using pyscript. +This is meant to be run within a cmd2 session using run_pyscript. """ import argparse diff --git a/tests/conftest.py b/tests/conftest.py index 1e124219..d20d060a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -28,8 +28,8 @@ except ImportError: # Help text for base cmd2.Cmd application BASE_HELP = """Documented commands (type help <topic>): ======================================== -alias help load py quit shell -edit history macro pyscript set shortcuts +alias help load py quit run_script shell +edit history macro pyscript run_pyscript set shortcuts """ # noqa: W291 BASE_HELP_VERBOSE = """ @@ -44,6 +44,8 @@ macro Manage macros py Invoke Python command or shell pyscript Run a Python script file inside the console quit Exit this application +run_pyscript Run a Python script file inside the console +run_script Run commands in script file that is encoded as either ASCII or UTF-8 text set Set a settable parameter or show current settings of parameters shell Execute a command as if at the OS prompt shortcuts List available shortcuts @@ -89,8 +91,8 @@ formatting: SHORTCUTS_TXT = """Shortcuts for other commands: !: shell ?: help -@: load -@@: _relative_load +@: run_script +@@: _relative_run_script """ # Output from the show command with default settings diff --git a/tests/relative_multiple.txt b/tests/relative_multiple.txt index bbd11739..a2d564ef 100644 --- a/tests/relative_multiple.txt +++ b/tests/relative_multiple.txt @@ -1 +1 @@ -_relative_load scripts/one_down.txt +_relative_run_script scripts/one_down.txt diff --git a/tests/scripts/nested.txt b/tests/scripts/nested.txt index ac3b4748..88b0a80c 100644 --- a/tests/scripts/nested.txt +++ b/tests/scripts/nested.txt @@ -1,4 +1,4 @@ -_relative_load precmds.txt +_relative_run_script precmds.txt help shortcuts -_relative_load postcmds.txt +_relative_run_script postcmds.txt diff --git a/tests/scripts/one_down.txt b/tests/scripts/one_down.txt index b87ff844..f92a061d 100644 --- a/tests/scripts/one_down.txt +++ b/tests/scripts/one_down.txt @@ -1 +1 @@ -_relative_load ../script.txt +_relative_run_script ../script.txt diff --git a/tests/scripts/recursive.py b/tests/scripts/recursive.py index 3359d31e..7d37e540 100644 --- a/tests/scripts/recursive.py +++ b/tests/scripts/recursive.py @@ -5,4 +5,4 @@ Example demonstrating that running a Python script recursively inside another Python script isn't allowed """ app.cmd_echo = True -app('pyscript ../script.py') +app('run_pyscript ../script.py') diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index 72643308..77542d76 100644 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -244,7 +244,7 @@ def test_base_py(base_app): @pytest.mark.skipif(sys.platform == 'win32', reason="Unit test doesn't work on win32, but feature does") -def test_base_run_python_script(base_app, request): +def test_py_run_script(base_app, request): test_dir = os.path.dirname(request.module.__file__) python_script = os.path.join(test_dir, 'script.py') expected = 'This is a python script running ...' @@ -253,45 +253,12 @@ def test_base_run_python_script(base_app, request): assert expected in out -def test_base_run_pyscript(base_app, capsys, request): - test_dir = os.path.dirname(request.module.__file__) - python_script = os.path.join(test_dir, 'script.py') - expected = 'This is a python script running ...' - - out, err = run_cmd(base_app, "pyscript {}".format(python_script)) - assert expected in out - -def test_recursive_pyscript_not_allowed(base_app, request): - test_dir = os.path.dirname(request.module.__file__) - python_script = os.path.join(test_dir, 'scripts', 'recursive.py') - expected = 'Recursively entering interactive Python consoles is not allowed.' - - out, err = run_cmd(base_app, "pyscript {}".format(python_script)) - assert err[0] == expected - -def test_pyscript_with_nonexist_file(base_app): - python_script = 'does_not_exist.py' - out, err = run_cmd(base_app, "pyscript {}".format(python_script)) - assert "Error opening script file" in err[0] - -def test_pyscript_with_exception(base_app, request): - test_dir = os.path.dirname(request.module.__file__) - python_script = os.path.join(test_dir, 'scripts', 'raises_exception.py') - out, err = run_cmd(base_app, "pyscript {}".format(python_script)) - assert err[0].startswith('Traceback') - assert "TypeError: unsupported operand type(s) for +: 'int' and 'str'" in err[-1] - -def test_pyscript_requires_an_argument(base_app): - out, err = run_cmd(base_app, "pyscript") - assert "the following arguments are required: script_path" in err[1] - - def test_base_error(base_app): out, err = run_cmd(base_app, 'meow') assert "is not a recognized command" in err[0] -def test_base_load(base_app, request): +def test_run_script(base_app, request): test_dir = os.path.dirname(request.module.__file__) filename = os.path.join(test_dir, 'script.txt') @@ -299,7 +266,7 @@ def test_base_load(base_app, request): assert base_app._current_script_dir is None # Get output out the script - script_out, script_err = run_cmd(base_app, 'load {}'.format(filename)) + script_out, script_err = run_cmd(base_app, 'run_script {}'.format(filename)) assert base_app._script_dir == [] assert base_app._current_script_dir is None @@ -318,52 +285,37 @@ def test_base_load(base_app, request): assert script_out == manual_out assert script_err == manual_err -def test_load_with_empty_args(base_app): - # The way the load command works, we can't directly capture its stdout or stderr - out, err = run_cmd(base_app, 'load') +def test_load_deprecated(base_app): + """Delete this when load alias is removed""" + _, err = run_cmd(base_app, "load fake") + assert "load has been renamed and will be removed" in err[-1] - # The load command requires a file path argument, so we should get an error message +def test_run_script_with_empty_args(base_app): + out, err = run_cmd(base_app, 'run_script') assert "the following arguments are required" in err[1] - -def test_load_with_nonexistent_file(base_app, capsys): - # The way the load command works, we can't directly capture its stdout or stderr - out, err = run_cmd(base_app, 'load does_not_exist.txt') - - # The load command requires a path to an existing file +def test_run_script_with_nonexistent_file(base_app, capsys): + out, err = run_cmd(base_app, 'run_script does_not_exist.txt') assert "does not exist" in err[0] -def test_load_with_directory(base_app, request): +def test_run_script_with_directory(base_app, request): test_dir = os.path.dirname(request.module.__file__) - - # The way the load command works, we can't directly capture its stdout or stderr - out, err = run_cmd(base_app, 'load {}'.format(test_dir)) - + out, err = run_cmd(base_app, 'run_script {}'.format(test_dir)) assert "is not a file" in err[0] -def test_load_with_empty_file(base_app, request): +def test_run_script_with_empty_file(base_app, request): test_dir = os.path.dirname(request.module.__file__) filename = os.path.join(test_dir, 'scripts', 'empty.txt') - - # The way the load command works, we can't directly capture its stdout or stderr - out, err = run_cmd(base_app, 'load {}'.format(filename)) - - # The load command requires non-empty script files + out, err = run_cmd(base_app, 'run_script {}'.format(filename)) assert "is empty" in err[0] - -def test_load_with_binary_file(base_app, request): +def test_run_script_with_binary_file(base_app, request): test_dir = os.path.dirname(request.module.__file__) filename = os.path.join(test_dir, 'scripts', 'binary.bin') - - # The way the load command works, we can't directly capture its stdout or stderr - out, err = run_cmd(base_app, 'load {}'.format(filename)) - - # The load command requires non-empty scripts files + out, err = run_cmd(base_app, 'run_script {}'.format(filename)) assert "is not an ASCII or UTF-8 encoded text file" in err[0] - -def test_load_with_utf8_file(base_app, request): +def test_run_script_with_utf8_file(base_app, request): test_dir = os.path.dirname(request.module.__file__) filename = os.path.join(test_dir, 'scripts', 'utf8.txt') @@ -371,7 +323,7 @@ def test_load_with_utf8_file(base_app, request): assert base_app._current_script_dir is None # Get output out the script - script_out, script_err = run_cmd(base_app, 'load {}'.format(filename)) + script_out, script_err = run_cmd(base_app, 'run_script {}'.format(filename)) assert base_app._script_dir == [] assert base_app._current_script_dir is None @@ -390,51 +342,49 @@ def test_load_with_utf8_file(base_app, request): assert script_out == manual_out assert script_err == manual_err - -def test_load_nested_loads(base_app, request): - # Verify that loading a script with nested load commands works correctly, - # and loads the nested script commands in the correct order. +def test_run_script_nested_run_scripts(base_app, request): + # Verify that running a script with nested run_script commands works correctly, + # and runs the nested script commands in the correct order. test_dir = os.path.dirname(request.module.__file__) filename = os.path.join(test_dir, 'scripts', 'nested.txt') - # Load the top level script - initial_load = 'load ' + filename - run_cmd(base_app, initial_load) + # Run the top level script + initial_run = 'run_script ' + filename + run_cmd(base_app, initial_run) # Check that the right commands were executed. expected = """ %s -_relative_load precmds.txt +_relative_run_script precmds.txt set colors Always help shortcuts -_relative_load postcmds.txt -set colors Never""" % initial_load +_relative_run_script postcmds.txt +set colors Never""" % initial_run out, err = run_cmd(base_app, 'history -s') assert out == normalize(expected) - -def test_base_runcmds_plus_hooks(base_app, request): +def test_runcmds_plus_hooks(base_app, request): test_dir = os.path.dirname(request.module.__file__) prefilepath = os.path.join(test_dir, 'scripts', 'precmds.txt') postfilepath = os.path.join(test_dir, 'scripts', 'postcmds.txt') - base_app.runcmds_plus_hooks(['load ' + prefilepath, + base_app.runcmds_plus_hooks(['run_script ' + prefilepath, 'help', 'shortcuts', - 'load ' + postfilepath]) + 'run_script ' + postfilepath]) expected = """ -load %s +run_script %s set colors Always help shortcuts -load %s +run_script %s set colors Never""" % (prefilepath, postfilepath) out, err = run_cmd(base_app, 'history -s') assert out == normalize(expected) -def test_base_relative_load(base_app, request): +def test_relative_run_script(base_app, request): test_dir = os.path.dirname(request.module.__file__) filename = os.path.join(test_dir, 'script.txt') @@ -442,7 +392,7 @@ def test_base_relative_load(base_app, request): assert base_app._current_script_dir is None # Get output out the script - script_out, script_err = run_cmd(base_app, 'load {}'.format(filename)) + script_out, script_err = run_cmd(base_app, 'run_script {}'.format(filename)) assert base_app._script_dir == [] assert base_app._current_script_dir is None @@ -461,10 +411,14 @@ def test_base_relative_load(base_app, request): assert script_out == manual_out assert script_err == manual_err -def test_relative_load_requires_an_argument(base_app): - out, err = run_cmd(base_app, '_relative_load') +def test_relative_run_script_requires_an_argument(base_app): + out, err = run_cmd(base_app, '_relative_run_script') assert 'Error: the following arguments' in err[1] +def test_relative_load_deprecated(base_app): + """Delete this when _relative_load alias is removed""" + _, err = run_cmd(base_app, "_relative_load fake") + assert "_relative_load has been renamed and will be removed" in err[0] def test_output_redirection(base_app): fd, filename = tempfile.mkstemp(prefix='cmd2_test', suffix='.txt') @@ -950,8 +904,8 @@ def test_custom_help_menu(help_app): expected = normalize(""" Documented commands (type help <topic>): ======================================== -alias help load py quit shell squat -edit history macro pyscript set shortcuts +alias help load py quit run_script shell squat +edit history macro pyscript run_pyscript set shortcuts Undocumented commands: ====================== @@ -1020,7 +974,8 @@ cat_nodoc diddly Other ===== -alias help history load macro py pyscript quit set shell shortcuts +alias history macro pyscript run_pyscript set shortcuts +help load py quit run_script shell Undocumented commands: ====================== @@ -1052,6 +1007,8 @@ macro Manage macros py Invoke Python command or shell pyscript Run a Python script file inside the console quit Exit this application +run_pyscript Run a Python script file inside the console +run_script Run commands in script file that is encoded as either ASCII or UTF-8 text set Set a settable parameter or show current settings of parameters shell Execute a command as if at the OS prompt shortcuts List available shortcuts @@ -1600,7 +1557,7 @@ invalid_command_name = [ def test_get_alias_names(base_app): assert len(base_app.aliases) == 0 - run_cmd(base_app, 'alias create fake pyscript') + run_cmd(base_app, 'alias create fake run_pyscript') run_cmd(base_app, 'alias create ls !ls -hal') assert len(base_app.aliases) == 2 assert sorted(base_app.get_alias_names()) == ['fake', 'ls'] @@ -1621,7 +1578,7 @@ def test_alias_no_subcommand(base_app): def test_alias_create(base_app): # Create the alias - out, err = run_cmd(base_app, 'alias create fake pyscript') + out, err = run_cmd(base_app, 'alias create fake run_pyscript') assert out == normalize("Alias 'fake' created") # Use the alias @@ -1630,11 +1587,11 @@ def test_alias_create(base_app): # See a list of aliases out, err = run_cmd(base_app, 'alias list') - assert out == normalize('alias create fake pyscript') + assert out == normalize('alias create fake run_pyscript') # Look up the new alias out, err = run_cmd(base_app, 'alias list fake') - assert out == normalize('alias create fake pyscript') + assert out == normalize('alias create fake run_pyscript') def test_alias_create_with_quoted_value(base_app): """Demonstrate that quotes in alias value will be preserved (except for redirectors and terminators)""" @@ -1675,7 +1632,7 @@ def test_alias_list_invalid_alias(base_app): def test_alias_delete(base_app): # Create an alias - run_cmd(base_app, 'alias create fake pyscript') + run_cmd(base_app, 'alias create fake run_pyscript') # Delete the alias out, err = run_cmd(base_app, 'alias delete fake') @@ -1712,7 +1669,7 @@ def test_macro_no_subcommand(base_app): def test_macro_create(base_app): # Create the macro - out, err = run_cmd(base_app, 'macro create fake pyscript') + out, err = run_cmd(base_app, 'macro create fake run_pyscript') assert out == normalize("Macro 'fake' created") # Use the macro @@ -1721,11 +1678,11 @@ def test_macro_create(base_app): # See a list of macros out, err = run_cmd(base_app, 'macro list') - assert out == normalize('macro create fake pyscript') + assert out == normalize('macro create fake run_pyscript') # Look up the new macro out, err = run_cmd(base_app, 'macro list fake') - assert out == normalize('macro create fake pyscript') + assert out == normalize('macro create fake run_pyscript') def test_macro_create_with_quoted_value(base_app): """Demonstrate that quotes in macro value will be preserved (except for redirectors and terminators)""" @@ -1829,7 +1786,7 @@ def test_macro_list_invalid_macro(base_app): def test_macro_delete(base_app): # Create an macro - run_cmd(base_app, 'macro create fake pyscript') + run_cmd(base_app, 'macro create fake run_pyscript') # Delete the macro out, err = run_cmd(base_app, 'macro delete fake') @@ -1934,8 +1891,8 @@ def test_onecmd_raw_str_quit(outsim_app): def test_get_all_commands(base_app): # Verify that the base app has the expected commands commands = base_app.get_all_commands() - expected_commands = ['_relative_load', 'alias', 'edit', 'eof', 'help', 'history', 'load', 'macro', - 'py', 'pyscript', 'quit', 'set', 'shell', 'shortcuts'] + expected_commands = ['_relative_load', '_relative_run_script', 'alias', 'edit', 'eof', 'help', 'history', 'load', + 'macro', 'py', 'pyscript', 'quit', 'run_pyscript', 'run_script', 'set', 'shell', 'shortcuts'] assert commands == expected_commands def test_get_help_topics(base_app): diff --git a/tests/test_completion.py b/tests/test_completion.py index 0a16bc28..91519b0a 100644 --- a/tests/test_completion.py +++ b/tests/test_completion.py @@ -149,7 +149,7 @@ def test_complete_exception(cmd2_app, capsys): def test_complete_macro(base_app, request): # Create the macro - out, err = run_cmd(base_app, 'macro create fake pyscript {1}') + out, err = run_cmd(base_app, 'macro create fake run_pyscript {1}') assert out == normalize("Macro 'fake' created") # Macros do path completion @@ -537,7 +537,7 @@ def test_basic_completion_nomatch(cmd2_app): def test_delimiter_completion(cmd2_app): text = '/home/' - line = 'load {}'.format(text) + line = 'run_script {}'.format(text) endidx = len(line) begidx = endidx - len(text) diff --git a/tests/test_history.py b/tests/test_history.py index a06bce87..973d8cff 100644 --- a/tests/test_history.py +++ b/tests/test_history.py @@ -275,7 +275,7 @@ def parser(): '42': 'theanswer', 'l': '!ls -al', 'anothermultiline': 'multiline', - 'fake': 'pyscript'}, + 'fake': 'run_pyscript'}, shortcuts=[('?', 'help'), ('!', 'shell')] ) return parser diff --git a/tests/test_parsing.py b/tests/test_parsing.py index 3bd635a1..13a535c0 100644 --- a/tests/test_parsing.py +++ b/tests/test_parsing.py @@ -20,7 +20,7 @@ def parser(): '42': 'theanswer', 'l': '!ls -al', 'anothermultiline': 'multiline', - 'fake': 'pyscript'}, + 'fake': 'run_pyscript'}, shortcuts=[('?', 'help'), ('!', 'shell')] ) return parser @@ -705,8 +705,8 @@ def test_parse_command_only_expands_alias(parser): assert statement == 'foobar.py "somebody.py' assert statement.args == statement assert statement.arg_list == [] - assert statement.command == 'pyscript' - assert statement.command_and_args == 'pyscript foobar.py "somebody.py' + assert statement.command == 'run_pyscript' + assert statement.command_and_args == 'run_pyscript foobar.py "somebody.py' assert statement.multiline_command == '' assert statement.raw == line assert statement.terminator == '' diff --git a/tests/test_pyscript.py b/tests/test_pyscript.py deleted file mode 100644 index 8da4b35a..00000000 --- a/tests/test_pyscript.py +++ /dev/null @@ -1,56 +0,0 @@ -# coding=utf-8 -# flake8: noqa E302 -""" -Unit/functional testing for pytest in cmd2 -""" -import os -from cmd2 import plugin - -from .conftest import run_cmd - -HOOK_OUTPUT = "TEST_OUTPUT" - -def cmdfinalization_hook(data: plugin.CommandFinalizationData) -> plugin.CommandFinalizationData: - """A cmdfinalization_hook hook which requests application exit""" - print(HOOK_OUTPUT) - return data - -def test_pyscript_help(base_app, request): - test_dir = os.path.dirname(request.module.__file__) - python_script = os.path.join(test_dir, 'pyscript', 'help.py') - out1, err1 = run_cmd(base_app, 'help') - out2, err2 = run_cmd(base_app, 'pyscript {}'.format(python_script)) - assert out1 and out1 == out2 - - -def test_pyscript_dir(base_app, request): - test_dir = os.path.dirname(request.module.__file__) - python_script = os.path.join(test_dir, 'pyscript', 'pyscript_dir.py') - - out, err = run_cmd(base_app, 'pyscript {}'.format(python_script)) - assert out - assert out[0] == "['cmd_echo']" - - -def test_pyscript_stdout_capture(base_app, request): - base_app.register_cmdfinalization_hook(cmdfinalization_hook) - test_dir = os.path.dirname(request.module.__file__) - python_script = os.path.join(test_dir, 'pyscript', 'stdout_capture.py') - out, err = run_cmd(base_app, 'pyscript {} {}'.format(python_script, HOOK_OUTPUT)) - - assert out[0] == "PASSED" - assert out[1] == "PASSED" - -def test_pyscript_stop(base_app, request): - # Verify onecmd_plus_hooks() returns True if any commands in a pyscript return True for stop - test_dir = os.path.dirname(request.module.__file__) - - # help.py doesn't run any commands that returns True for stop - python_script = os.path.join(test_dir, 'pyscript', 'help.py') - stop = base_app.onecmd_plus_hooks('pyscript {}'.format(python_script)) - assert not stop - - # stop.py runs the quit command which does return True for stop - python_script = os.path.join(test_dir, 'pyscript', 'stop.py') - stop = base_app.onecmd_plus_hooks('pyscript {}'.format(python_script)) - assert stop diff --git a/tests/test_run_pyscript.py b/tests/test_run_pyscript.py new file mode 100644 index 00000000..9eb33b31 --- /dev/null +++ b/tests/test_run_pyscript.py @@ -0,0 +1,91 @@ +# coding=utf-8 +# flake8: noqa E302 +""" +Unit/functional testing for run_pytest in cmd2 +""" +import os +from cmd2 import plugin + +from .conftest import run_cmd + +HOOK_OUTPUT = "TEST_OUTPUT" + +def cmdfinalization_hook(data: plugin.CommandFinalizationData) -> plugin.CommandFinalizationData: + """A cmdfinalization_hook hook which requests application exit""" + print(HOOK_OUTPUT) + return data + +def test_run_pyscript(base_app, request): + test_dir = os.path.dirname(request.module.__file__) + python_script = os.path.join(test_dir, 'script.py') + expected = 'This is a python script running ...' + + out, err = run_cmd(base_app, "run_pyscript {}".format(python_script)) + assert expected in out + +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, 'scripts', 'recursive.py') + expected = 'Recursively entering interactive Python consoles is not allowed.' + + out, err = run_cmd(base_app, "run_pyscript {}".format(python_script)) + assert err[0] == expected + +def test_run_pyscript_with_nonexist_file(base_app): + python_script = 'does_not_exist.py' + out, err = run_cmd(base_app, "run_pyscript {}".format(python_script)) + assert "Error opening script file" in err[0] + +def test_run_pyscript_with_exception(base_app, request): + test_dir = os.path.dirname(request.module.__file__) + python_script = os.path.join(test_dir, 'scripts', 'raises_exception.py') + out, err = run_cmd(base_app, "run_pyscript {}".format(python_script)) + assert err[0].startswith('Traceback') + assert "TypeError: unsupported operand type(s) for +: 'int' and 'str'" in err[-1] + +def test_run_pyscript_requires_an_argument(base_app): + out, err = run_cmd(base_app, "run_pyscript") + assert "the following arguments are required: script_path" in err[1] + +def test_run_pyscript_help(base_app, request): + test_dir = os.path.dirname(request.module.__file__) + python_script = os.path.join(test_dir, 'pyscript', 'help.py') + out1, err1 = run_cmd(base_app, 'help') + out2, err2 = run_cmd(base_app, 'run_pyscript {}'.format(python_script)) + assert out1 and out1 == out2 + +def test_run_pyscript_dir(base_app, request): + test_dir = os.path.dirname(request.module.__file__) + python_script = os.path.join(test_dir, 'pyscript', 'pyscript_dir.py') + + out, err = run_cmd(base_app, 'run_pyscript {}'.format(python_script)) + assert out + assert out[0] == "['cmd_echo']" + +def test_run_pyscript_stdout_capture(base_app, request): + base_app.register_cmdfinalization_hook(cmdfinalization_hook) + test_dir = os.path.dirname(request.module.__file__) + python_script = os.path.join(test_dir, 'pyscript', 'stdout_capture.py') + out, err = run_cmd(base_app, 'run_pyscript {} {}'.format(python_script, HOOK_OUTPUT)) + + assert out[0] == "PASSED" + assert out[1] == "PASSED" + +def test_run_pyscript_stop(base_app, request): + # Verify onecmd_plus_hooks() returns True if any commands in a pyscript return True for stop + test_dir = os.path.dirname(request.module.__file__) + + # help.py doesn't run any commands that returns True for stop + python_script = os.path.join(test_dir, 'pyscript', 'help.py') + stop = base_app.onecmd_plus_hooks('run_pyscript {}'.format(python_script)) + assert not stop + + # stop.py runs the quit command which does return True for stop + python_script = os.path.join(test_dir, 'pyscript', 'stop.py') + stop = base_app.onecmd_plus_hooks('run_pyscript {}'.format(python_script)) + assert stop + +def test_pyscript_deprecated(base_app): + """Delete this when pyscript alias is removed""" + _, err = run_cmd(base_app, "pyscript fake") + assert "pyscript has been renamed and will be removed" in err[-1] diff --git a/tests/test_transcript.py b/tests/test_transcript.py index 5dd39e1b..909a6a5c 100644 --- a/tests/test_transcript.py +++ b/tests/test_transcript.py @@ -190,7 +190,7 @@ this is a \/multiline\/ command assert transcript == expected -def test_load_record_transcript(base_app, request): +def test_run_script_record_transcript(base_app, request): test_dir = os.path.dirname(request.module.__file__) filename = os.path.join(test_dir, 'scripts', 'help.txt') @@ -201,8 +201,8 @@ def test_load_record_transcript(base_app, request): fd, transcript_fname = tempfile.mkstemp(prefix='', suffix='.trn') os.close(fd) - # Run the load command with the -r option to generate a transcript - run_cmd(base_app, 'load {} -t {}'.format(filename, transcript_fname)) + # Execute the run_script command with the -t option to generate a transcript + run_cmd(base_app, 'run_script {} -t {}'.format(filename, transcript_fname)) assert base_app._script_dir == [] assert base_app._current_script_dir is None diff --git a/tests/transcripts/from_cmdloop.txt b/tests/transcripts/from_cmdloop.txt index 84d7f8fc..76056fc6 100644 --- a/tests/transcripts/from_cmdloop.txt +++ b/tests/transcripts/from_cmdloop.txt @@ -5,8 +5,9 @@ Documented commands (type help <topic>): ======================================== -alias help load mumble orate pyscript say shell speak/ */ -edit history macro nothing py quit set shortcuts/ */ +alias history mumble py run_pyscript set speak/ */ +edit load nothing pyscript run_script shell/ */ +help macro orate quit say shortcuts/ */ (Cmd) help say usage: speak [-h] [-p] [-s] [-r REPEAT]/ */ |