diff options
author | kotfu <kotfu@kotfu.net> | 2019-11-29 16:11:34 -0700 |
---|---|---|
committer | kotfu <kotfu@kotfu.net> | 2019-11-29 16:11:34 -0700 |
commit | 51851b8aa0d685b039a066a3b8efe88aa05b6113 (patch) | |
tree | 7e59a0f716456deeb672ba90e0a239f1e906209b /cmd2 | |
parent | 6102c0aa1b463b4653bf9d0e84fd0a6558fba5f1 (diff) | |
parent | ee735ed2a5a9e8d6d6fa6827a698c9d5630f0029 (diff) | |
download | cmd2-git-51851b8aa0d685b039a066a3b8efe88aa05b6113.tar.gz |
Merge branch 'master' into generating_output_docs
Diffstat (limited to 'cmd2')
-rw-r--r-- | cmd2/argparse_completer.py | 18 | ||||
-rw-r--r-- | cmd2/argparse_custom.py | 9 | ||||
-rw-r--r-- | cmd2/cmd2.py | 29 | ||||
-rw-r--r-- | cmd2/rl_utils.py | 32 |
4 files changed, 50 insertions, 38 deletions
diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py index 7e876258..a2690dd0 100644 --- a/cmd2/argparse_completer.py +++ b/cmd2/argparse_completer.py @@ -14,9 +14,9 @@ import textwrap from collections import deque from typing import Dict, List, Optional, Union +from . import ansi from . import cmd2 from . import utils -from .ansi import ansi_aware_write, ansi_safe_wcswidth, style_error from .argparse_custom import ATTR_CHOICES_CALLABLE, INFINITY, generate_range_error from .argparse_custom import ATTR_SUPPRESS_TAB_HINT, ATTR_DESCRIPTIVE_COMPLETION_HEADER, ATTR_NARGS_RANGE from .argparse_custom import ChoicesCallable, CompletionError, CompletionItem @@ -193,9 +193,9 @@ class AutoCompleter(object): if arg_action == completer_action: return True - error = style_error("\nError: argument {}: not allowed with argument {}\n". - format(argparse._get_action_name(arg_action), - argparse._get_action_name(completer_action))) + error = ansi.style_error("\nError: argument {}: not allowed with argument {}\n". + format(argparse._get_action_name(arg_action), + argparse._get_action_name(completer_action))) self._print_message(error) return False @@ -444,11 +444,11 @@ class AutoCompleter(object): completions.sort(key=self._cmd2_app.default_sort_key) self._cmd2_app.matches_sorted = True - token_width = ansi_safe_wcswidth(action.dest) + token_width = ansi.ansi_safe_wcswidth(action.dest) completions_with_desc = [] for item in completions: - item_width = ansi_safe_wcswidth(item) + item_width = ansi.ansi_safe_wcswidth(item) if item_width > token_width: token_width = item_width @@ -585,7 +585,7 @@ class AutoCompleter(object): def _print_message(msg: str) -> None: """Print a message instead of tab completions and redraw the prompt and input line""" import sys - ansi_aware_write(sys.stdout, msg + '\n') + ansi.ansi_aware_write(sys.stdout, msg + '\n') rl_force_redisplay() def _print_arg_hint(self, arg_action: argparse.Action) -> None: @@ -615,7 +615,7 @@ class AutoCompleter(object): format(argparse._get_action_name(flag_arg_state.action), generate_range_error(flag_arg_state.min, flag_arg_state.max), flag_arg_state.count) - self._print_message(style_error('{}'.format(error))) + self._print_message(ansi.style_error('{}'.format(error))) def _print_completion_error(self, arg_action: argparse.Action, completion_error: CompletionError) -> None: """ @@ -628,4 +628,4 @@ class AutoCompleter(object): error = ("\nError tab completing {}:\n" "{}\n".format(argparse._get_action_name(arg_action), indented_error)) - self._print_message(style_error('{}'.format(error))) + self._print_message(ansi.style_error('{}'.format(error))) diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py index c6aa6550..cfe9ea5e 100644 --- a/cmd2/argparse_custom.py +++ b/cmd2/argparse_custom.py @@ -4,7 +4,8 @@ This module adds capabilities to argparse by patching a few of its functions. It class called Cmd2ArgumentParser which improves error and help output over normal argparse. All cmd2 code uses this parser and it is recommended that developers of cmd2-based apps either use it or write their own parser that inherits from it. This will give a consistent look-and-feel between the help/error output of built-in -cmd2 commands and the app-specific commands. +cmd2 commands and the app-specific commands. If you wish to override the parser used by cmd2's built-in +commands, see override_parser.py example. Since the new capabilities are added by patching at the argparse API level, they are available whether or not Cmd2ArgumentParser is used. However, the help and error output of Cmd2ArgumentParser is customized to notate @@ -184,7 +185,7 @@ import sys from argparse import ZERO_OR_MORE, ONE_OR_MORE, ArgumentError, _ from typing import Callable, Optional, Tuple, Type, Union -from .ansi import ansi_aware_write, style_error +from . import ansi # Used in nargs ranges to signify there is no maximum INFINITY = float('inf') @@ -746,7 +747,7 @@ class Cmd2ArgumentParser(argparse.ArgumentParser): linum += 1 self.print_usage(sys.stderr) - formatted_message = style_error(formatted_message) + formatted_message = ansi.style_error(formatted_message) self.exit(2, '{}\n\n'.format(formatted_message)) # noinspection PyProtectedMember @@ -805,7 +806,7 @@ class Cmd2ArgumentParser(argparse.ArgumentParser): if message: if file is None: file = sys.stderr - ansi_aware_write(file, message) + ansi.ansi_aware_write(file, message) # The default ArgumentParser class for a cmd2 app diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 9f2dd329..2ed87fdc 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -52,13 +52,10 @@ from .clipboard import can_clip, get_paste_buffer, write_to_paste_buffer from .decorators import with_argparser from .history import History, HistoryItem from .parsing import StatementParser, Statement, Macro, MacroArg, shlex_split -from .rl_utils import rl_type, RlType, rl_get_point, rl_set_prompt, vt100_support, rl_make_safe_prompt +from .rl_utils import rl_type, RlType, rl_get_point, rl_set_prompt, vt100_support, rl_make_safe_prompt, rl_warning # Set up readline if rl_type == RlType.NONE: # pragma: no cover - rl_warning = ("Readline features including tab completion have been disabled since no\n" - "supported version of readline was found. To resolve this, install pyreadline\n" - "on Windows or gnureadline on Mac.\n\n") sys.stderr.write(ansi.style_warning(rl_warning)) else: from .rl_utils import rl_force_redisplay, readline @@ -2240,10 +2237,10 @@ class Cmd(cmd.Cmd): if args.all: self.aliases.clear() self.poutput("All aliases deleted") - elif not args.name: + elif not args.names: self.perror("Either --all or alias name(s) must be specified") else: - for cur_name in utils.remove_duplicates(args.name): + for cur_name in utils.remove_duplicates(args.names): if cur_name in self.aliases: del self.aliases[cur_name] self.poutput("Alias '{}' deleted".format(cur_name)) @@ -2252,8 +2249,8 @@ class Cmd(cmd.Cmd): def _alias_list(self, args: argparse.Namespace) -> None: """List some or all aliases""" - if args.name: - for cur_name in utils.remove_duplicates(args.name): + if args.names: + for cur_name in utils.remove_duplicates(args.names): if cur_name in self.aliases: self.poutput("alias create {} {}".format(cur_name, self.aliases[cur_name])) else: @@ -2305,7 +2302,7 @@ class Cmd(cmd.Cmd): alias_delete_description = "Delete specified aliases or all aliases if --all is used" alias_delete_parser = alias_subparsers.add_parser('delete', help=alias_delete_help, description=alias_delete_description) - alias_delete_parser.add_argument('name', nargs=argparse.ZERO_OR_MORE, help='alias to delete', + alias_delete_parser.add_argument('names', nargs=argparse.ZERO_OR_MORE, help='alias(es) to delete', choices_method=_get_alias_completion_items, descriptive_header='Value') alias_delete_parser.add_argument('-a', '--all', action='store_true', help="delete all aliases") alias_delete_parser.set_defaults(func=_alias_delete) @@ -2319,7 +2316,7 @@ class Cmd(cmd.Cmd): alias_list_parser = alias_subparsers.add_parser('list', help=alias_list_help, description=alias_list_description) - alias_list_parser.add_argument('name', nargs=argparse.ZERO_OR_MORE, help='alias to list', + alias_list_parser.add_argument('names', nargs=argparse.ZERO_OR_MORE, help='alias(es) to list', choices_method=_get_alias_completion_items, descriptive_header='Value') alias_list_parser.set_defaults(func=_alias_list) @@ -2417,10 +2414,10 @@ class Cmd(cmd.Cmd): if args.all: self.macros.clear() self.poutput("All macros deleted") - elif not args.name: + elif not args.names: self.perror("Either --all or macro name(s) must be specified") else: - for cur_name in utils.remove_duplicates(args.name): + for cur_name in utils.remove_duplicates(args.names): if cur_name in self.macros: del self.macros[cur_name] self.poutput("Macro '{}' deleted".format(cur_name)) @@ -2429,8 +2426,8 @@ class Cmd(cmd.Cmd): def _macro_list(self, args: argparse.Namespace) -> None: """List some or all macros""" - if args.name: - for cur_name in utils.remove_duplicates(args.name): + if args.names: + for cur_name in utils.remove_duplicates(args.names): if cur_name in self.macros: self.poutput("macro create {} {}".format(cur_name, self.macros[cur_name].value)) else: @@ -2505,7 +2502,7 @@ class Cmd(cmd.Cmd): macro_delete_description = "Delete specified macros or all macros if --all is used" macro_delete_parser = macro_subparsers.add_parser('delete', help=macro_delete_help, description=macro_delete_description) - macro_delete_parser.add_argument('name', nargs=argparse.ZERO_OR_MORE, help='macro to delete', + macro_delete_parser.add_argument('names', nargs=argparse.ZERO_OR_MORE, help='macro(s) to delete', choices_method=_get_macro_completion_items, descriptive_header='Value') macro_delete_parser.add_argument('-a', '--all', action='store_true', help="delete all macros") macro_delete_parser.set_defaults(func=_macro_delete) @@ -2518,7 +2515,7 @@ class Cmd(cmd.Cmd): "Without arguments, all macros will be listed.") macro_list_parser = macro_subparsers.add_parser('list', help=macro_list_help, description=macro_list_description) - macro_list_parser.add_argument('name', nargs=argparse.ZERO_OR_MORE, help='macro to list', + macro_list_parser.add_argument('names', nargs=argparse.ZERO_OR_MORE, help='macro(s) to list', choices_method=_get_macro_completion_items, descriptive_header='Value') macro_list_parser.set_defaults(func=_macro_list) diff --git a/cmd2/rl_utils.py b/cmd2/rl_utils.py index 7f47db79..9a23cbcd 100644 --- a/cmd2/rl_utils.py +++ b/cmd2/rl_utils.py @@ -32,6 +32,9 @@ rl_type = RlType.NONE # Tells if the terminal we are running in supports vt100 control characters vt100_support = False +# Explanation for why readline wasn't loaded +_rl_warn_reason = '' + # The order of this check matters since importing pyreadline will also show readline in the modules list if 'pyreadline' in sys.modules: rl_type = RlType.PYREADLINE @@ -113,15 +116,26 @@ if 'pyreadline' in sys.modules: elif 'gnureadline' in sys.modules or 'readline' in sys.modules: # We don't support libedit if 'libedit' not in readline.__doc__: - rl_type = RlType.GNU - - # Load the readline lib so we can access members of it - import ctypes - readline_lib = ctypes.CDLL(readline.__file__) - - # Check if we are running in a terminal - if sys.stdout.isatty(): - vt100_support = True + try: + # Load the readline lib so we can access members of it + import ctypes + readline_lib = ctypes.CDLL(readline.__file__) + except AttributeError: # pragma: no cover + _rl_warn_reason = ("this application is running in a non-standard Python environment in\n" + "which readline is not loaded dynamically from a shared library file.") + else: + rl_type = RlType.GNU + vt100_support = sys.stdout.isatty() + +# Check if readline was loaded +if rl_type == RlType.NONE: # pragma: no cover + if not _rl_warn_reason: + _rl_warn_reason = ("no supported version of readline was found. To resolve this, install\n" + "pyreadline on Windows or gnureadline on Mac.") + rl_warning = ("Readline features including tab completion have been disabled because\n" + + _rl_warn_reason + '\n\n') +else: + rl_warning = '' # noinspection PyProtectedMember,PyUnresolvedReferences |