diff options
-rw-r--r-- | .coveragerc | 3 | ||||
-rw-r--r-- | cmd2/__init__.py | 3 | ||||
-rw-r--r-- | cmd2/cmd2.py | 53 | ||||
-rw-r--r-- | noxfile.py | 22 | ||||
-rw-r--r-- | plugins/ext_test/cmd2_ext_test/__init__.py | 8 | ||||
-rw-r--r-- | plugins/ext_test/cmd2_ext_test/cmd2_ext_test.py | 2 | ||||
-rw-r--r-- | plugins/tasks.py | 5 | ||||
-rw-r--r-- | plugins/template/cmd2_myplugin/__init__.py | 13 | ||||
-rw-r--r-- | plugins/template/cmd2_myplugin/myplugin.py | 2 |
9 files changed, 66 insertions, 45 deletions
diff --git a/.coveragerc b/.coveragerc index 96bf5d72..ad8b6730 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,7 +1,8 @@ # .coveragerc to control coverage.py [run] # Source -source = cmd2/ +source = plugins/*/cmd2_*/ + cmd2/ # (boolean, default False): whether to measure branch coverage in addition to statement coverage. branch = False diff --git a/cmd2/__init__.py b/cmd2/__init__.py index 8c07fb80..c3c1f87e 100644 --- a/cmd2/__init__.py +++ b/cmd2/__init__.py @@ -11,7 +11,7 @@ except ImportError: import importlib_metadata try: __version__ = importlib_metadata.version(__name__) -except importlib_metadata.PackageNotFoundError: +except importlib_metadata.PackageNotFoundError: # pragma: no cover # package is not installed pass @@ -31,6 +31,7 @@ from .cmd2 import Cmd from .constants import COMMAND_NAME, DEFAULT_SHORTCUTS from .decorators import with_argument_list, with_argparser, with_argparser_and_unknown_args, with_category from .exceptions import Cmd2ArgparseError, SkipPostcommandHooks +from . import plugin from .parsing import Statement from .py_bridge import CommandResult from .utils import categorize, CompletionError, Settable diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index cdee3523..70ec508c 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -200,7 +200,7 @@ class Cmd(cmd.Cmd): self.max_completion_items = 50 # A dictionary mapping settable names to their Settable instance - self.settables = dict() + self.settables = dict() # type: Dict[str, Settable] self.build_settables() # Use as prompt for multiline commands on the 2nd+ line of input @@ -220,7 +220,7 @@ class Cmd(cmd.Cmd): self.exclude_from_history = ['eof', 'history'] # Dictionary of macro names and their values - self.macros = dict() + self.macros = dict() # type: Dict[str, Macro] # Keeps track of typed command history in the Python shell self._py_history = [] @@ -249,14 +249,14 @@ class Cmd(cmd.Cmd): self.last_result = None # Used by run_script command to store current script dir as a LIFO queue to support _relative_run_script command - self._script_dir = [] + self._script_dir = [] # type: List[str] # Context manager used to protect critical sections in the main thread from stopping due to a KeyboardInterrupt self.sigint_protection = utils.ContextFlag() # If the current command created a process to pipe to, then this will be a ProcReader object. # Otherwise it will be None. It's used to know when a pipe process can be killed and/or waited upon. - self._cur_pipe_proc_reader = None + self._cur_pipe_proc_reader = None # type: Optional[utils.ProcReader] # Used to keep track of whether we are redirecting or piping output self._redirecting = False @@ -280,7 +280,7 @@ class Cmd(cmd.Cmd): self.broken_pipe_warning = '' # Commands that will run at the beginning of the command loop - self._startup_commands = [] + self._startup_commands = [] # type: List[str] # If a startup script is provided and exists, then execute it in the startup commands if startup_script: @@ -289,7 +289,7 @@ class Cmd(cmd.Cmd): self._startup_commands.append("run_script {}".format(utils.quote_string(startup_script))) # Transcript files to run instead of interactive command loop - self._transcript_files = None + self._transcript_files = None # type: Optional[List[str]] # Check for command line args if allow_cli_args: @@ -333,7 +333,7 @@ class Cmd(cmd.Cmd): # Commands that have been disabled from use. This is to support commands that are only available # during specific states of the application. This dictionary's keys are the command names and its # values are DisabledCommand objects. - self.disabled_commands = dict() + self.disabled_commands = dict() # type: Dict[str, DisabledCommand] # If any command has been categorized, then all other commands that haven't been categorized # will display under this section in the help output. @@ -1910,7 +1910,7 @@ class Cmd(cmd.Cmd): self._cur_pipe_proc_reader, self._redirecting) # The ProcReader for this command - cmd_pipe_proc_reader = None + cmd_pipe_proc_reader = None # type: Optional[utils.ProcReader] if not self.allow_redirection: # Don't return since we set some state variables at the end of the function @@ -2694,16 +2694,31 @@ class Cmd(cmd.Cmd): def _help_menu(self, verbose: bool = False) -> None: """Show a list of commands which help can be displayed for""" + cmds_cats, cmds_doc, cmds_undoc, help_topics = self._build_command_info() + + if len(cmds_cats) == 0: + # No categories found, fall back to standard behavior + self.poutput("{}".format(str(self.doc_leader))) + self._print_topics(self.doc_header, cmds_doc, verbose) + else: + # Categories found, Organize all commands by category + self.poutput('{}'.format(str(self.doc_leader))) + self.poutput('{}'.format(str(self.doc_header)), end="\n\n") + for category in sorted(cmds_cats.keys(), key=self.default_sort_key): + self._print_topics(category, cmds_cats[category], verbose) + self._print_topics(self.default_category, cmds_doc, verbose) + + self.print_topics(self.misc_header, help_topics, 15, 80) + self.print_topics(self.undoc_header, cmds_undoc, 15, 80) + + def _build_command_info(self): # Get a sorted list of help topics help_topics = sorted(self.get_help_topics(), key=self.default_sort_key) - # Get a sorted list of visible command names visible_commands = sorted(self.get_visible_commands(), key=self.default_sort_key) - cmds_doc = [] cmds_undoc = [] cmds_cats = {} - for command in visible_commands: func = self.cmd_func(command) has_help_func = False @@ -2724,21 +2739,7 @@ class Cmd(cmd.Cmd): cmds_doc.append(command) else: cmds_undoc.append(command) - - if len(cmds_cats) == 0: - # No categories found, fall back to standard behavior - self.poutput("{}".format(str(self.doc_leader))) - self._print_topics(self.doc_header, cmds_doc, verbose) - else: - # Categories found, Organize all commands by category - self.poutput('{}'.format(str(self.doc_leader))) - self.poutput('{}'.format(str(self.doc_header)), end="\n\n") - for category in sorted(cmds_cats.keys(), key=self.default_sort_key): - self._print_topics(category, cmds_cats[category], verbose) - self._print_topics(self.default_category, cmds_doc, verbose) - - self.print_topics(self.misc_header, help_topics, 15, 80) - self.print_topics(self.undoc_header, cmds_undoc, 15, 80) + return cmds_cats, cmds_doc, cmds_undoc, help_topics def _print_topics(self, header: str, cmds: List[str], verbose: bool) -> None: """Customized version of print_topics that can switch between verbose or traditional output""" @@ -3,7 +3,10 @@ import nox @nox.session(python=['3.7']) def docs(session): - session.install('sphinx', 'sphinx-rtd-theme', '.') + session.install('sphinx', + 'sphinx-rtd-theme', + '.', + ) session.chdir('docs') tmpdir = session.create_tmp() @@ -12,16 +15,25 @@ def docs(session): @nox.session(python=['3.5', '3.6', '3.7', '3.8', '3.9']) -@nox.parametrize('plugin', [None, 'ext_test', 'template']) +@nox.parametrize('plugin', [None, + 'ext_test', + 'template', + 'coverage']) def tests(session, plugin): if plugin is None: session.install('invoke', './[test]') session.run('invoke', 'pytest', '--junit', '--no-pty') + elif plugin == 'coverage': + session.install('invoke', 'codecov', 'coverage') + session.run('codecov') else: session.install('invoke', '.') # cd into test directory to run other unit test session.install('plugins/{}[test]'.format(plugin)) - session.run('invoke', 'plugin.{}.pytest'.format(plugin.replace('_', '-')), '--junit', '--no-pty') - - session.run('codecov') + session.run('invoke', + 'plugin.{}.pytest'.format(plugin.replace('_', '-')), + '--junit', + '--no-pty', + '--append-cov', + ) diff --git a/plugins/ext_test/cmd2_ext_test/__init__.py b/plugins/ext_test/cmd2_ext_test/__init__.py index dbbc4250..21fd000b 100644 --- a/plugins/ext_test/cmd2_ext_test/__init__.py +++ b/plugins/ext_test/cmd2_ext_test/__init__.py @@ -1,19 +1,19 @@ # # coding=utf-8 -"""Description of myplugin +"""cmd2 External Python Testing Mixin -An overview of what myplugin does. +Allows developers to exercise their cmd2 application using the PyScript interface """ try: # For python 3.8 and later import importlib.metadata as importlib_metadata -except ImportError: +except ImportError: # pragma: no cover # For everyone else import importlib_metadata try: __version__ = importlib_metadata.version(__name__) -except importlib_metadata.PackageNotFoundError: +except importlib_metadata.PackageNotFoundError: # pragma: no cover # package is not installed __version__ = 'unknown' diff --git a/plugins/ext_test/cmd2_ext_test/cmd2_ext_test.py b/plugins/ext_test/cmd2_ext_test/cmd2_ext_test.py index 731a0f3b..df54e112 100644 --- a/plugins/ext_test/cmd2_ext_test/cmd2_ext_test.py +++ b/plugins/ext_test/cmd2_ext_test/cmd2_ext_test.py @@ -6,7 +6,7 @@ from typing import Optional, TYPE_CHECKING import cmd2 -if TYPE_CHECKING: +if TYPE_CHECKING: # pragma: no cover _Base = cmd2.Cmd else: _Base = object diff --git a/plugins/tasks.py b/plugins/tasks.py index 06104429..26ec1adb 100644 --- a/plugins/tasks.py +++ b/plugins/tasks.py @@ -8,14 +8,15 @@ Make sure you satisfy the following Python module requirements if you are trying - wheel >= 0.31.0 - setuptools >= 39.1.0 """ -import os import invoke from plugins.ext_test import tasks as ext_test_tasks from plugins.template import tasks as template_tasks # create namespaces -namespace = invoke.Collection(ext_test=ext_test_tasks, template=template_tasks) +namespace = invoke.Collection(ext_test=ext_test_tasks, + template=template_tasks, + ) namespace_clean = invoke.Collection('clean') namespace.add_collection(namespace_clean, 'clean') diff --git a/plugins/template/cmd2_myplugin/__init__.py b/plugins/template/cmd2_myplugin/__init__.py index 41f0b9cc..e66b62cd 100644 --- a/plugins/template/cmd2_myplugin/__init__.py +++ b/plugins/template/cmd2_myplugin/__init__.py @@ -5,11 +5,16 @@ An overview of what myplugin does. """ -from pkg_resources import get_distribution, DistributionNotFound - from .myplugin import empty_decorator, MyPluginMixin # noqa: F401 try: - __version__ = get_distribution(__name__).version -except DistributionNotFound: + # For python 3.8 and later + import importlib.metadata as importlib_metadata +except ImportError: # pragma: no cover + # For everyone else + import importlib_metadata +try: + __version__ = importlib_metadata.version(__name__) +except importlib_metadata.PackageNotFoundError: # pragma: no cover + # package is not installed __version__ = 'unknown' diff --git a/plugins/template/cmd2_myplugin/myplugin.py b/plugins/template/cmd2_myplugin/myplugin.py index 5fa12caf..4f1ff0e9 100644 --- a/plugins/template/cmd2_myplugin/myplugin.py +++ b/plugins/template/cmd2_myplugin/myplugin.py @@ -7,7 +7,7 @@ from typing import Callable, TYPE_CHECKING import cmd2 -if TYPE_CHECKING: +if TYPE_CHECKING: # pragma: no cover _Base = cmd2.Cmd else: _Base = object |