diff options
-rw-r--r-- | cmd2/cmd2.py | 38 | ||||
-rw-r--r-- | cmd2/parsing.py | 4 | ||||
-rwxr-xr-x | examples/colors.py | 11 | ||||
-rw-r--r-- | tests/conftest.py | 2 |
4 files changed, 30 insertions, 25 deletions
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index db96228b..da5b528a 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -32,11 +32,11 @@ Git repository on GitHub at https://github.com/python-cmd2/cmd2 import argparse import cmd import collections +import colorama from colorama import Fore import glob import inspect import os -import platform import re import shlex import sys @@ -337,7 +337,7 @@ class Cmd(cmd.Cmd): # To make an attribute settable with the "do_set" command, add it to this ... # This starts out as a dictionary but gets converted to an OrderedDict sorted alphabetically by key - settable = {'colors': 'Allow colorized output', + settable = {'colors': 'Allow colorized output (valid values: Terminal, Always, Never)', 'continuation_prompt': 'On 2nd+ line of input', 'debug': 'Show full error stack on error', 'echo': 'Echo command issued into output', @@ -369,6 +369,9 @@ class Cmd(cmd.Cmd): except AttributeError: pass + # Override whether ansi codes should be stripped from the output since cmd2 has its own logic for doing this + colorama.init(strip=False) + # initialize plugin system # needs to be done before we call __init__(0) self._initialize_plugin_system() @@ -417,13 +420,13 @@ class Cmd(cmd.Cmd): self._STOP_AND_EXIT = True # cmd convention self._colorcodes = {'bold': {True: '\x1b[1m', False: '\x1b[22m'}, - 'cyan': {True: '\x1b[36m', False: '\x1b[39m'}, - 'blue': {True: '\x1b[34m', False: '\x1b[39m'}, - 'red': {True: '\x1b[31m', False: '\x1b[39m'}, - 'magenta': {True: '\x1b[35m', False: '\x1b[39m'}, - 'green': {True: '\x1b[32m', False: '\x1b[39m'}, - 'underline': {True: '\x1b[4m', False: '\x1b[24m'}, - 'yellow': {True: '\x1b[33m', False: '\x1b[39m'}} + 'cyan': {True: Fore.CYAN, False: Fore.RESET}, + 'blue': {True: Fore.BLUE, False: Fore.RESET}, + 'red': {True: Fore.RED, False: Fore.RESET}, + 'magenta': {True: Fore.MAGENTA, False: Fore.RESET}, + 'green': {True: Fore.GREEN, False: Fore.RESET}, + 'underline': {True: '\x1b[4m', False: Fore.RESET}, + 'yellow': {True: Fore.YELLOW, False: Fore.RESET}} # Used load command to store the current script dir as a LIFO queue to support _relative_load command self._script_dir = [] @@ -547,17 +550,15 @@ class Cmd(cmd.Cmd): # Make sure settable parameters are sorted alphabetically by key self.settable = collections.OrderedDict(sorted(self.settable.items(), key=lambda t: t[0])) - def decolorized_write(self, fileobj: IO, msg: str): + def decolorized_write(self, fileobj: IO, msg: str) -> None: """Write a string to a fileobject, stripping ANSI escape sequences if necessary Honor the current colors setting, which requires us to check whether the fileobject is a tty. """ - if self.colors == constants.COLORS_NEVER: + if self.colors.lower() == constants.COLORS_NEVER.lower() or \ + (self.colors.lower() == constants.COLORS_TERMINAL.lower() and not fileobj.isatty()): msg = utils.strip_ansi(msg) - if self.colors == constants.COLORS_TERMINAL: - if not fileobj.isatty(): - msg = utils.strip_ansi(msg) fileobj.write(msg) def poutput(self, msg: Any, end: str='\n') -> None: @@ -575,7 +576,7 @@ class Cmd(cmd.Cmd): msg_str = '{}'.format(msg) self.decolorized_write(self.stdout, msg_str) if not msg_str.endswith(end): - self.stdout.write(end) + self.decolorized_write(self.stdout, end) except BrokenPipeError: # This occurs if a command's output is being piped to another # process and that process closes before the command is @@ -615,7 +616,7 @@ class Cmd(cmd.Cmd): if self.feedback_to_output: self.poutput(msg) else: - sys.stderr.write("{}\n".format(msg)) + self.decolorized_write(sys.stderr, "{}\n".format(msg)) def ppaged(self, msg: str, end: str='\n', chop: bool=False) -> None: """Print output using a pager if it would go off screen and stdout isn't currently being redirected. @@ -651,6 +652,9 @@ class Cmd(cmd.Cmd): # Don't attempt to use a pager that can block if redirecting or running a script (either text or Python) # Also only attempt to use a pager if actually running in a real fully functional terminal if functional_terminal and not self.redirecting and not self._in_py and not self._script_dir: + if self.colors.lower() == constants.COLORS_NEVER.lower(): + msg_str = utils.strip_ansi(msg_str) + pager = self.pager if chop: pager = self.pager_chop @@ -686,7 +690,7 @@ class Cmd(cmd.Cmd): is running on Windows, will return ``val`` unchanged. ``color`` should be one of the supported strings (or styles): red/blue/green/cyan/magenta, bold, underline""" - if self.colors and (self.stdout == self.initial_stdout): + if self.colors.lower() != constants.COLORS_NEVER.lower() and (self.stdout == self.initial_stdout): return self._colorcodes[color][True] + val + self._colorcodes[color][False] return val diff --git a/cmd2/parsing.py b/cmd2/parsing.py index 8edfacb9..92dc1b40 100644 --- a/cmd2/parsing.py +++ b/cmd2/parsing.py @@ -90,7 +90,7 @@ class Statement(str): command = attr.ib(default='', validator=attr.validators.instance_of(str), type=str) # list of arguments to the command, not including any output redirection or terminators; quoted args remain quoted - arg_list = attr.ib(factory=list, validator=attr.validators.instance_of(list), type=List[str]) + arg_list = attr.ib(default=attr.Factory(list), validator=attr.validators.instance_of(list), type=List[str]) # if the command is a multiline command, the name of the command, otherwise empty multiline_command = attr.ib(default='', validator=attr.validators.instance_of(str), type=str) @@ -102,7 +102,7 @@ class Statement(str): suffix = attr.ib(default='', validator=attr.validators.instance_of(str), type=str) # if output was piped to a shell command, the shell command as a list of tokens - pipe_to = attr.ib(factory=list, validator=attr.validators.instance_of(list), type=List[str]) + pipe_to = attr.ib(default=attr.Factory(list), validator=attr.validators.instance_of(list), type=List[str]) # if output was redirected, the redirection token, i.e. '>>' output = attr.ib(default='', validator=attr.validators.instance_of(str), type=str) diff --git a/examples/colors.py b/examples/colors.py index 59e108b6..fb2de045 100755 --- a/examples/colors.py +++ b/examples/colors.py @@ -53,6 +53,7 @@ BG_COLORS ={ 'white':Back.WHITE, } + class CmdLineApp(cmd2.Cmd): """Example cmd2 application demonstrating colorized output.""" @@ -71,14 +72,14 @@ class CmdLineApp(cmd2.Cmd): self.shortcuts.update({'&': 'speak'}) # Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell - super().__init__(use_ipython=False) + super().__init__(use_ipython=True) speak_parser = argparse.ArgumentParser() speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay') speak_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE') speak_parser.add_argument('-r', '--repeat', type=int, help='output [n] times') - speak_parser.add_argument('-f', '--fg', help='foreground color to apply to output') - speak_parser.add_argument('-b', '--bg', help='background color to apply to output') + speak_parser.add_argument('-f', '--fg', choices=FG_COLORS, help='foreground color to apply to output') + speak_parser.add_argument('-b', '--bg', choices=BG_COLORS, help='background color to apply to output') speak_parser.add_argument('words', nargs='+', help='words to say') @cmd2.with_argparser(speak_parser) @@ -95,9 +96,9 @@ class CmdLineApp(cmd2.Cmd): repetitions = args.repeat or 1 color_on = '' - if args.fg and args.fg in FG_COLORS: + if args.fg: color_on += FG_COLORS[args.fg] - if args.bg and args.bg in BG_COLORS: + if args.bg: color_on += BG_COLORS[args.bg] color_off = Fore.RESET + Back.RESET diff --git a/tests/conftest.py b/tests/conftest.py index c86748e8..e177496b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -94,7 +94,7 @@ timing: False """ SHOW_LONG = """ -colors: Terminal # Allow colorized output +colors: Terminal # Allow colorized output (valid values: Terminal, Always, Never) continuation_prompt: > # On 2nd+ line of input debug: False # Show full error stack on error echo: False # Echo command issued into output |