diff --git a/CHANGELOG.md b/CHANGELOG.md index d38d38533..1261bc492 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## 0.9.15 (July TBD, 2019) +* **Renamed Commands Notice** + * The following commands were renamed in the last release and have been removed in this release + * `load` - replaced by `run_script` + * `_relative_load` - replaced by `_relative_run_script` + * `pyscript` - replaced by `run_pyscript` + * We apologize for any inconvenience, but the new names are more self-descriptive + * Lots of end users were confused particularly about what exactly `load` should be loading + ## 0.9.14 (June 29, 2019) * Enhancements * Added support for and testing with Python 3.8, starting with 3.8 beta diff --git a/cmd2/clipboard.py b/cmd2/clipboard.py index b2331649e..8e3cddc3d 100644 --- a/cmd2/clipboard.py +++ b/cmd2/clipboard.py @@ -2,30 +2,14 @@ """ This module provides basic ability to copy from and paste to the clipboard/pastebuffer. """ -import sys - import pyperclip - -# Newer versions of pyperclip are released as a single file, but older versions had a more complicated structure -try: - from pyperclip.exceptions import PyperclipException -except ImportError: # pragma: no cover - # noinspection PyUnresolvedReferences,PyProtectedMember - from pyperclip import PyperclipException +# noinspection PyProtectedMember +from pyperclip import PyperclipException # Can we access the clipboard? Should always be true on Windows and Mac, but only sometimes on Linux -# noinspection PyUnresolvedReferences try: - # Get the version of the pyperclip module as a float - pyperclip_ver = float('.'.join(pyperclip.__version__.split('.')[:2])) - - # The extraneous output bug in pyperclip on Linux using xclip was fixed in more recent versions of pyperclip - if sys.platform.startswith('linux') and pyperclip_ver < 1.6: - # Avoid extraneous output to stderr from xclip when clipboard is empty at cost of overwriting clipboard contents - pyperclip.copy('') - else: - # Try getting the contents of the clipboard - _ = pyperclip.paste() + # Try getting the contents of the clipboard + _ = pyperclip.paste() except PyperclipException: can_clip = False else: diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index eb2d4c15e..d41a631d4 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -3261,16 +3261,9 @@ def do_run_pyscript(self, args: argparse.Namespace) -> bool: finally: # Restore command line arguments to original state sys.argv = orig_args - if args.__statement__.command == "pyscript": - warning = ("pyscript has been renamed and will be removed in the next release, " - "please use run_pyscript instead\n") - self.perror(ansi.style_warning(warning)) 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()) @@ -3644,59 +3637,49 @@ def do_run_script(self, args: argparse.Namespace) -> Optional[bool]: """ expanded_path = os.path.abspath(os.path.expanduser(args.script_path)) - # 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)) - return + # 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)) + 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)) - 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)) + return - # Make sure the file is not empty - if os.path.getsize(expanded_path) == 0: - self.perror("'{}' is empty".format(expanded_path)) - return + # Make sure the file is not empty + if os.path.getsize(expanded_path) == 0: + self.perror("'{}' is empty".format(expanded_path)) + 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)) - 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)) + 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.pexcept("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.pexcept("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: - if args.__statement__.command == "load": - warning = ("load has been renamed and will be removed in the next release, " - "please use run_script instead\n") - self.perror(ansi.style_warning(warning)) - - # load has been deprecated - do_load = do_run_script + 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() relative_run_script_description = run_script_description relative_run_script_description += ( @@ -3717,19 +3700,11 @@ 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": - warning = ("_relative_load has been renamed and will be removed in the next release, " - "please use _relative_run_script instead\n") - self.perror(ansi.style_warning(warning)) - 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_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/docs/integrating.rst b/docs/integrating.rst index 352bb2f0e..bf79ee4e0 100644 --- a/docs/integrating.rst +++ b/docs/integrating.rst @@ -45,9 +45,8 @@ loop:: Documented commands (type help ): ======================================== - alias history mumble pyscript run_script shell - edit load orate quit say shortcuts - help macro py run_pyscript set speak + alias help macro orate quit run_script set shortcuts + edit history mumble py run_pyscript say shell speak (Cmd) diff --git a/docs/unfreefeatures.rst b/docs/unfreefeatures.rst index 713e44e65..02e1b9814 100644 --- a/docs/unfreefeatures.rst +++ b/docs/unfreefeatures.rst @@ -248,12 +248,8 @@ By default, the ``help`` command displays:: Documented commands (type help ): ======================================== - alias findleakers pyscript sessions status vminfo - config help quit set stop which - connect history redeploy shell thread_dump - deploy list resources shortcuts unalias - edit load restart sslconnectorciphers undeploy - expire py serverinfo start version + alias help ipy py run_pyscript set shortcuts + edit history macro quit run_script shell If you have a large number of commands, you can optionally group your commands into categories. Here's the output from the example ``help_categories.py``:: @@ -265,6 +261,10 @@ Here's the output from the example ``help_categories.py``:: deploy findleakers redeploy sessions stop expire list restart start undeploy + Command Management + ================== + disable_commands enable_commands + Connecting ========== connect which @@ -275,9 +275,8 @@ Here's the output from the example ``help_categories.py``:: Other ===== - alias edit history py quit shell unalias - config help load pyscript set shortcuts version - + alias edit history py run_pyscript set shortcuts + config help macro quit run_script shell version There are 2 methods of specifying command categories, using the ``@with_category`` decorator or with the ``categorize()`` function. Once a single command category is detected, the help output switches to a categorized diff --git a/examples/transcripts/exampleSession.txt b/examples/transcripts/exampleSession.txt index 8fa7c9bba..3e579c28f 100644 --- a/examples/transcripts/exampleSession.txt +++ b/examples/transcripts/exampleSession.txt @@ -3,7 +3,7 @@ # The regex for editor will match whatever program you use. # regexes on prompts just make the trailing space obvious (Cmd) set -colors: /(Terminal|Always|Never)/ +allow_ansi: /(Terminal|Always|Never)/ continuation_prompt: >/ / debug: False echo: False diff --git a/examples/transcripts/transcript_regex.txt b/examples/transcripts/transcript_regex.txt index 6980fac60..d94c442b6 100644 --- a/examples/transcripts/transcript_regex.txt +++ b/examples/transcripts/transcript_regex.txt @@ -3,7 +3,7 @@ # The regex for editor will match whatever program you use. # regexes on prompts just make the trailing space obvious (Cmd) set -colors: /(Terminal|Always|Never)/ +allow_ansi: /(Terminal|Always|Never)/ continuation_prompt: >/ / debug: False echo: False diff --git a/setup.py b/setup.py index f6b8f5127..3319c82d1 100755 --- a/setup.py +++ b/setup.py @@ -31,7 +31,7 @@ SETUP_REQUIRES = ['setuptools_scm'] -INSTALL_REQUIRES = ['pyperclip >= 1.5.27', 'colorama', 'attrs >= 16.3.0', 'wcwidth >= 0.1.7'] +INSTALL_REQUIRES = ['pyperclip >= 1.6', 'colorama >= 0.3.7', 'attrs >= 16.3.0', 'wcwidth >= 0.1.7'] EXTRAS_REQUIRE = { # Windows also requires pyreadline to ensure tab completion works diff --git a/tests/.cmd2rc b/tests/.cmd2rc new file mode 100644 index 000000000..cedcbe205 --- /dev/null +++ b/tests/.cmd2rc @@ -0,0 +1,2 @@ +alias create ls !ls -hal +alias create pwd !pwd diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index d3e51130f..c9a41033c 100644 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -306,11 +306,6 @@ def test_run_script(base_app, request): assert script_out == manual_out assert script_err == manual_err -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] - 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] @@ -436,11 +431,6 @@ 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') os.close(fd) @@ -1490,7 +1480,6 @@ def test_poutput_ansi_always(outsim_app): assert colored_msg != msg assert out == expected - def test_poutput_ansi_never(outsim_app): msg = 'Hello World' ansi.allow_ansi = ansi.ANSI_NEVER @@ -1848,8 +1837,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', '_relative_run_script', 'alias', 'edit', 'eof', 'help', 'history', 'load', - 'macro', 'py', 'pyscript', 'quit', 'run_pyscript', 'run_script', 'set', 'shell', 'shortcuts'] + expected_commands = ['_relative_run_script', 'alias', 'edit', 'eof', 'help', 'history', 'macro', + 'py', 'quit', 'run_pyscript', 'run_script', 'set', 'shell', 'shortcuts'] assert commands == expected_commands def test_get_help_topics(base_app): @@ -2154,3 +2143,22 @@ def test_disabled_message_command_name(disable_commands_app): out, err = run_cmd(disable_commands_app, 'has_help_func') assert err[0].startswith('has_help_func is currently disabled') + + +def test_startup_script(request): + test_dir = os.path.dirname(request.module.__file__) + startup_script = os.path.join(test_dir, '.cmd2rc') + app = cmd2.Cmd(allow_cli_args=False, startup_script=startup_script) + assert len(app._startup_commands) == 1 + assert app._startup_commands[0] == "run_script '{}'".format(startup_script) + app._startup_commands.append('quit') + app.cmdloop() + out, err = run_cmd(app, 'alias list') + assert len(out) > 1 + assert 'alias create ls' in out[0] + + +def test_transcripts_at_init(): + transcript_files = ['foo', 'bar'] + app = cmd2.Cmd(allow_cli_args=False, transcript_files=transcript_files) + assert app._transcript_files == transcript_files diff --git a/tests/test_run_pyscript.py b/tests/test_run_pyscript.py index 9eb33b314..50b1e3010 100644 --- a/tests/test_run_pyscript.py +++ b/tests/test_run_pyscript.py @@ -84,8 +84,3 @@ def test_run_pyscript_stop(base_app, request): 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]