summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd2/cmd2.py51
-rw-r--r--cmd2/constants.py5
-rw-r--r--cmd2/transcript.py3
-rw-r--r--docs/settingchanges.rst4
-rw-r--r--docs/unfreefeatures.rst40
-rwxr-xr-xexamples/colors.py143
-rw-r--r--tests/conftest.py18
-rw-r--r--tests/scripts/postcmds.txt2
-rw-r--r--tests/scripts/precmds.txt2
-rw-r--r--tests/test_cmd2.py161
-rw-r--r--tests/transcripts/regex_set.txt4
11 files changed, 368 insertions, 65 deletions
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index fb929078..136328b1 100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -40,7 +40,7 @@ import platform
import re
import shlex
import sys
-from typing import Any, Callable, Dict, List, Mapping, Optional, Tuple, Type, Union
+from typing import Any, Callable, Dict, List, Mapping, Optional, Tuple, Type, Union, IO
from . import constants
from . import utils
@@ -317,7 +317,7 @@ class Cmd(cmd.Cmd):
reserved_words = []
# Attributes which ARE dynamically settable at runtime
- colors = (platform.system() != 'Windows')
+ colors = constants.COLORS_TERMINAL
continuation_prompt = '> '
debug = False
echo = False
@@ -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': 'Colorized output (*nix only)',
+ settable = {'colors': 'Allow colorized output',
'continuation_prompt': 'On 2nd+ line of input',
'debug': 'Show full error stack on error',
'echo': 'Echo command issued into output',
@@ -547,11 +547,25 @@ 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 poutput(self, msg: str, end: str='\n') -> None:
- """Convenient shortcut for self.stdout.write(); by default adds newline to end if not already present.
+ def decolorized_write(self, fileobj: IO, msg: str):
+ """Write a string to a fileobject, stripping ANSI escape sequences if necessary
- Also handles BrokenPipeError exceptions for when a commands's output has been piped to another process and
- that process terminates before the cmd2 command is finished executing.
+ Honor the current colors setting, which requires us to check whether the
+ fileobject is a tty.
+ """
+ if self.colors == constants.COLORS_NEVER:
+ 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:
+ """Smarter self.stdout.write(); color aware and adds newline of not present.
+
+ Also handles BrokenPipeError exceptions for when a commands's output has
+ been piped to another process and that process terminates before the
+ cmd2 command is finished executing.
:param msg: message to print to current stdout - anything convertible to a str with '{}'.format() is OK
:param end: string appended after the end of the message if not already present, default a newline
@@ -559,13 +573,15 @@ class Cmd(cmd.Cmd):
if msg is not None and msg != '':
try:
msg_str = '{}'.format(msg)
- self.stdout.write(msg_str)
+ self.decolorized_write(self.stdout, msg_str)
if not msg_str.endswith(end):
self.stdout.write(end)
except BrokenPipeError:
- # This occurs if a command's output is being piped to another process and that process closes before the
- # command is finished. If you would like your application to print a warning message, then set the
- # broken_pipe_warning attribute to the message you want printed.
+ # This occurs if a command's output is being piped to another
+ # process and that process closes before the command is
+ # finished. If you would like your application to print a
+ # warning message, then set the broken_pipe_warning attribute
+ # to the message you want printed.
if self.broken_pipe_warning:
sys.stderr.write(self.broken_pipe_warning)
@@ -582,14 +598,15 @@ class Cmd(cmd.Cmd):
if isinstance(err, Exception):
err_msg = "EXCEPTION of type '{}' occurred with message: '{}'\n".format(type(err).__name__, err)
- sys.stderr.write(self.colorize(err_msg, 'red'))
else:
- err_msg = self.colorize("ERROR: {}\n".format(err), 'red')
- sys.stderr.write(err_msg)
+ err_msg = "ERROR: {}\n".format(err)
+ err_msg = Fore.RED + err_msg + Fore.RESET
+ self.decolorized_write(sys.stderr, err_msg)
if traceback_war:
war = "To enable full traceback, run the following command: 'set debug true'\n"
- sys.stderr.write(self.colorize(war, 'yellow'))
+ war = Fore.YELLOW + war + Fore.RESET
+ self.decolorized_write(sys.stderr, war)
def pfeedback(self, msg: str) -> None:
"""For printing nonessential feedback. Can be silenced with `quiet`.
@@ -2081,10 +2098,6 @@ class Cmd(cmd.Cmd):
:param end: end code to tell GNU Readline about end of invisible characters
:return: prompt safe to pass to GNU Readline
"""
- # Windows terminals don't use ANSI escape codes and Windows readline isn't based on GNU Readline
- if sys.platform == "win32":
- return prompt
-
escaped = False
result = ""
diff --git a/cmd2/constants.py b/cmd2/constants.py
index d3e8a125..3c133b70 100644
--- a/cmd2/constants.py
+++ b/cmd2/constants.py
@@ -17,3 +17,8 @@ REDIRECTION_TOKENS = [REDIRECTION_PIPE, REDIRECTION_OUTPUT, REDIRECTION_APPEND]
ANSI_ESCAPE_RE = re.compile(r'\x1b[^m]*m')
LINE_FEED = '\n'
+
+# values for colors setting
+COLORS_NEVER = 'Never'
+COLORS_TERMINAL = 'Terminal'
+COLORS_ALWAYS = 'Always'
diff --git a/cmd2/transcript.py b/cmd2/transcript.py
index 5ba8d20d..a6b4cb1a 100644
--- a/cmd2/transcript.py
+++ b/cmd2/transcript.py
@@ -224,3 +224,6 @@ class OutputTrap(object):
result = self.contents
self.contents = ''
return result
+
+ def isatty(self) -> bool:
+ return True
diff --git a/docs/settingchanges.rst b/docs/settingchanges.rst
index 02955273..e08b6026 100644
--- a/docs/settingchanges.rst
+++ b/docs/settingchanges.rst
@@ -137,7 +137,7 @@ comments, is viewable from within a running application
with::
(Cmd) set --long
- colors: True # Colorized output (*nix only)
+ colors: Terminal # Allow colorized output
continuation_prompt: > # On 2nd+ line of input
debug: False # Show full error stack on error
echo: False # Echo command issued into output
@@ -150,5 +150,5 @@ with::
Any of these user-settable parameters can be set while running your app with the ``set`` command like so::
- set colors False
+ set colors Never
diff --git a/docs/unfreefeatures.rst b/docs/unfreefeatures.rst
index cd27745d..0124132b 100644
--- a/docs/unfreefeatures.rst
+++ b/docs/unfreefeatures.rst
@@ -139,23 +139,43 @@ instead. These methods have these advantages:
.. automethod:: cmd2.cmd2.Cmd.ppaged
-color
-=====
+Colored Output
+==============
-Text output can be colored by wrapping it in the ``colorize`` method.
+The output methods in the previous section all honor the ``colors`` setting,
+which has three possible values:
+
+Never
+ poutput() and pfeedback() strip all ANSI escape sequences
+ which instruct the terminal to colorize output
+
+Terminal
+ (the default value) poutput() and pfeedback() do not strip any ANSI escape
+ sequences when the output is a terminal, but if the output is a pipe or a
+ file the escape sequences are stripped. If you want colorized output you
+ must add ANSI escape sequences, preferably using some python color library
+ like `plumbum.colors`, `colorama`, `blessings`, or `termcolor`.
+
+Always
+ poutput() and pfeedback() never strip ANSI escape sequences, regardless of
+ the output destination
+
+
+The previously recommended ``colorize`` method is now deprecated.
-.. automethod:: cmd2.cmd2.Cmd.colorize
.. _quiet:
+Suppressing non-essential output
+================================
-quiet
-=====
+The ``quiet`` setting controls whether ``self.pfeedback()`` actually produces
+any output. If ``quiet`` is ``False``, then the output will be produced. If
+``quiet`` is ``True``, no output will be produced.
-Controls whether ``self.pfeedback('message')`` output is suppressed;
-useful for non-essential feedback that the user may not always want
-to read. ``quiet`` is only relevant if
-``app.pfeedback`` is sometimes used.
+This makes ``self.pfeedback()`` useful for non-essential output like status
+messages. Users can control whether they would like to see these messages by changing
+the value of the ``quiet`` setting.
select
diff --git a/examples/colors.py b/examples/colors.py
new file mode 100755
index 00000000..59e108b6
--- /dev/null
+++ b/examples/colors.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python
+# coding=utf-8
+"""
+A sample application for cmd2. Demonstrating colorized output.
+
+Experiment with the command line options on the `speak` command to see how
+different output colors ca
+
+The colors setting has three possible values:
+
+Never
+ poutput() and pfeedback() strip all ANSI escape sequences
+ which instruct the terminal to colorize output
+
+Terminal
+ (the default value) poutput() and pfeedback() do not strip any ANSI escape
+ sequences when the output is a terminal, but if the output is a pipe or a
+ file the escape sequences are stripped. If you want colorized output you
+ must add ANSI escape sequences, preferably using some python color library
+ like `plumbum.colors`, `colorama`, `blessings`, or `termcolor`.
+
+Always
+ poutput() and pfeedback() never strip ANSI escape sequences, regardless of
+ the output destination
+
+
+"""
+
+import random
+import argparse
+
+import cmd2
+from colorama import Fore, Back
+
+FG_COLORS = {
+ 'black': Fore.BLACK,
+ 'red': Fore.RED,
+ 'green': Fore.GREEN,
+ 'yellow': Fore.YELLOW,
+ 'blue': Fore.BLUE,
+ 'magenta': Fore.MAGENTA,
+ 'cyan': Fore.CYAN,
+ 'white': Fore.WHITE,
+}
+BG_COLORS ={
+ 'black': Back.BLACK,
+ 'red': Back.RED,
+ 'green': Back.GREEN,
+ 'yellow': Back.YELLOW,
+ 'blue': Back.BLUE,
+ 'magenta': Back.MAGENTA,
+ 'cyan': Back.CYAN,
+ 'white':Back.WHITE,
+}
+
+class CmdLineApp(cmd2.Cmd):
+ """Example cmd2 application demonstrating colorized output."""
+
+ # Setting this true makes it run a shell command if a cmd2/cmd command doesn't exist
+ # default_to_shell = True
+ MUMBLES = ['like', '...', 'um', 'er', 'hmmm', 'ahh']
+ MUMBLE_FIRST = ['so', 'like', 'well']
+ MUMBLE_LAST = ['right?']
+
+ def __init__(self):
+ self.multiline_commands = ['orate']
+ self.maxrepeats = 3
+
+ # Add stuff to settable and shortcuts before calling base class initializer
+ self.settable['maxrepeats'] = 'max repetitions for speak command'
+ 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)
+
+ 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('words', nargs='+', help='words to say')
+
+ @cmd2.with_argparser(speak_parser)
+ def do_speak(self, args):
+ """Repeats what you tell me to."""
+ words = []
+ for word in args.words:
+ if args.piglatin:
+ word = '%s%say' % (word[1:], word[0])
+ if args.shout:
+ word = word.upper()
+ words.append(word)
+
+ repetitions = args.repeat or 1
+
+ color_on = ''
+ if args.fg and args.fg in FG_COLORS:
+ color_on += FG_COLORS[args.fg]
+ if args.bg and args.bg in BG_COLORS:
+ color_on += BG_COLORS[args.bg]
+ color_off = Fore.RESET + Back.RESET
+
+ for i in range(min(repetitions, self.maxrepeats)):
+ # .poutput handles newlines, and accommodates output redirection too
+ self.poutput(color_on + ' '.join(words) + color_off)
+
+ do_say = do_speak # now "say" is a synonym for "speak"
+ do_orate = do_speak # another synonym, but this one takes multi-line input
+
+ mumble_parser = argparse.ArgumentParser()
+ mumble_parser.add_argument('-r', '--repeat', type=int, help='how many times to repeat')
+ mumble_parser.add_argument('-f', '--fg', help='foreground color to apply to output')
+ mumble_parser.add_argument('-b', '--bg', help='background color to apply to output')
+ mumble_parser.add_argument('words', nargs='+', help='words to say')
+
+ @cmd2.with_argparser(mumble_parser)
+ def do_mumble(self, args):
+ """Mumbles what you tell me to."""
+ color_on = ''
+ if args.fg and args.fg in FG_COLORS:
+ color_on += FG_COLORS[args.fg]
+ if args.bg and args.bg in BG_COLORS:
+ color_on += BG_COLORS[args.bg]
+ color_off = Fore.RESET + Back.RESET
+
+ repetitions = args.repeat or 1
+ for i in range(min(repetitions, self.maxrepeats)):
+ output = []
+ if random.random() < .33:
+ output.append(random.choice(self.MUMBLE_FIRST))
+ for word in args.words:
+ if random.random() < .40:
+ output.append(random.choice(self.MUMBLES))
+ output.append(word)
+ if random.random() < .25:
+ output.append(random.choice(self.MUMBLE_LAST))
+ self.poutput(color_on + ' '.join(output) + color_off)
+
+
+if __name__ == '__main__':
+ c = CmdLineApp()
+ c.cmdloop()
diff --git a/tests/conftest.py b/tests/conftest.py
index a23c44d0..c86748e8 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -80,11 +80,8 @@ SHORTCUTS_TXT = """Shortcuts for other commands:
@@: _relative_load
"""
-expect_colors = True
-if sys.platform.startswith('win'):
- expect_colors = False
# Output from the show command with default settings
-SHOW_TXT = """colors: {}
+SHOW_TXT = """colors: Terminal
continuation_prompt: >
debug: False
echo: False
@@ -94,14 +91,10 @@ locals_in_py: False
prompt: (Cmd)
quiet: False
timing: False
-""".format(expect_colors)
+"""
-if expect_colors:
- color_str = 'True '
-else:
- color_str = 'False'
SHOW_LONG = """
-colors: {} # Colorized output (*nix only)
+colors: Terminal # Allow colorized output
continuation_prompt: > # On 2nd+ line of input
debug: False # Show full error stack on error
echo: False # Echo command issued into output
@@ -111,7 +104,7 @@ locals_in_py: False # Allow access to your application in py via self
prompt: (Cmd) # The prompt issued to solicit input
quiet: False # Don't print nonessential feedback
timing: False # Report execution times
-""".format(color_str)
+"""
class StdOut(object):
@@ -128,6 +121,9 @@ class StdOut(object):
def clear(self):
self.buffer = ''
+ def isatty(self):
+ return True
+
def normalize(block):
""" Normalize a block of text to perform comparison.
diff --git a/tests/scripts/postcmds.txt b/tests/scripts/postcmds.txt
index 2b478b57..dea8f265 100644
--- a/tests/scripts/postcmds.txt
+++ b/tests/scripts/postcmds.txt
@@ -1 +1 @@
-set colors off
+set colors Never
diff --git a/tests/scripts/precmds.txt b/tests/scripts/precmds.txt
index d0b27fb6..0ae7eae8 100644
--- a/tests/scripts/precmds.txt
+++ b/tests/scripts/precmds.txt
@@ -1 +1 @@
-set colors on
+set colors Always
diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py
index fdf0f661..e2a3d854 100644
--- a/tests/test_cmd2.py
+++ b/tests/test_cmd2.py
@@ -13,6 +13,7 @@ import os
import sys
import tempfile
+import colorama
import pytest
# Python 3.5 had some regressions in the unitest.mock module, so use 3rd party mock if available
@@ -559,11 +560,11 @@ def test_load_nested_loads(base_app, request):
expected = """
%s
_relative_load precmds.txt
-set colors on
+set colors Always
help
shortcuts
_relative_load postcmds.txt
-set colors off""" % initial_load
+set colors Never""" % initial_load
assert run_cmd(base_app, 'history -s') == normalize(expected)
@@ -581,11 +582,11 @@ def test_base_runcmds_plus_hooks(base_app, request):
'load ' + postfilepath])
expected = """
load %s
-set colors on
+set colors Always
help
shortcuts
load %s
-set colors off""" % (prefilepath, postfilepath)
+set colors Never""" % (prefilepath, postfilepath)
assert run_cmd(base_app, 'history -s') == normalize(expected)
@@ -812,12 +813,7 @@ def test_base_colorize(base_app):
# But if we create a fresh Cmd() instance, it will
fresh_app = cmd2.Cmd()
color_test = fresh_app.colorize('Test', 'red')
- # Actually, colorization only ANSI escape codes is only applied on non-Windows systems
- if sys.platform == 'win32':
- assert color_test == 'Test'
- else:
- assert color_test == '\x1b[31mTest\x1b[39m'
-
+ assert color_test == '\x1b[31mTest\x1b[39m'
def _expected_no_editor_error():
expected_exception = 'OSError'
@@ -1107,14 +1103,9 @@ def test_ansi_prompt_escaped():
readline_hack_end = "\x02"
readline_safe_prompt = app._surround_ansi_escapes(color_prompt)
- if sys.platform.startswith('win'):
- # colorize() does nothing on Windows due to lack of ANSI color support
- assert prompt == color_prompt
- assert readline_safe_prompt == prompt
- else:
- assert prompt != color_prompt
- assert readline_safe_prompt.startswith(readline_hack_start + app._colorcodes[color][True] + readline_hack_end)
- assert readline_safe_prompt.endswith(readline_hack_start + app._colorcodes[color][False] + readline_hack_end)
+ assert prompt != color_prompt
+ assert readline_safe_prompt.startswith(readline_hack_start + app._colorcodes[color][True] + readline_hack_end)
+ assert readline_safe_prompt.endswith(readline_hack_start + app._colorcodes[color][False] + readline_hack_end)
class HelpApp(cmd2.Cmd):
@@ -1925,7 +1916,6 @@ def test_bad_history_file_path(capsys, request):
assert 'readline cannot read' in err
-
def test_get_all_commands(base_app):
# Verify that the base app has the expected commands
commands = base_app.get_all_commands()
@@ -2012,3 +2002,136 @@ def test_exit_code_nonzero(exit_code_repl):
app.cmdloop()
out = app.stdout.buffer
assert out == expected
+
+
+class ColorsApp(cmd2.Cmd):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ def do_echo(self, args):
+ self.poutput(args)
+ self.perror(args, False)
+
+ def do_echo_error(self, args):
+ color_on = colorama.Fore.RED + colorama.Back.BLACK
+ color_off = colorama.Style.RESET_ALL
+ self.poutput(color_on + args + color_off)
+ # perror uses colors by default
+ self.perror(args, False)
+
+def test_colors_default():
+ app = ColorsApp()
+ assert app.colors == cmd2.constants.COLORS_TERMINAL
+
+def test_colors_pouterr_always_tty(mocker, capsys):
+ app = ColorsApp()
+ app.colors = cmd2.constants.COLORS_ALWAYS
+ mocker.patch.object(app.stdout, 'isatty', return_value=True)
+ mocker.patch.object(sys.stderr, 'isatty', return_value=True)
+
+ app.onecmd_plus_hooks('echo_error oopsie')
+ out, err = capsys.readouterr()
+ # if colors are on, the output should have some escape sequences in it
+ assert len(out) > len('oopsie\n')
+ assert 'oopsie' in out
+ assert len(err) > len('Error: oopsie\n')
+ assert 'ERROR: oopsie' in err
+
+ # but this one shouldn't
+ app.onecmd_plus_hooks('echo oopsie')
+ out, err = capsys.readouterr()
+ assert out == 'oopsie\n'
+ # errors always have colors
+ assert len(err) > len('Error: oopsie\n')
+ assert 'ERROR: oopsie' in err
+
+def test_colors_pouterr_always_notty(mocker, capsys):
+ app = ColorsApp()
+ app.colors = cmd2.constants.COLORS_ALWAYS
+ mocker.patch.object(app.stdout, 'isatty', return_value=False)
+ mocker.patch.object(sys.stderr, 'isatty', return_value=False)
+
+ app.onecmd_plus_hooks('echo_error oopsie')
+ out, err = capsys.readouterr()
+ # if colors are on, the output should have some escape sequences in it
+ assert len(out) > len('oopsie\n')
+ assert 'oopsie' in out
+ assert len(err) > len('Error: oopsie\n')
+ assert 'ERROR: oopsie' in err
+
+ # but this one shouldn't
+ app.onecmd_plus_hooks('echo oopsie')
+ out, err = capsys.readouterr()
+ assert out == 'oopsie\n'
+ # errors always have colors
+ assert len(err) > len('Error: oopsie\n')
+ assert 'ERROR: oopsie' in err
+
+def test_colors_terminal_tty(mocker, capsys):
+ app = ColorsApp()
+ app.colors = cmd2.constants.COLORS_TERMINAL
+ mocker.patch.object(app.stdout, 'isatty', return_value=True)
+ mocker.patch.object(sys.stderr, 'isatty', return_value=True)
+
+ app.onecmd_plus_hooks('echo_error oopsie')
+ # if colors are on, the output should have some escape sequences in it
+ out, err = capsys.readouterr()
+ assert len(out) > len('oopsie\n')
+ assert 'oopsie' in out
+ assert len(err) > len('Error: oopsie\n')
+ assert 'ERROR: oopsie' in err
+
+ # but this one shouldn't
+ app.onecmd_plus_hooks('echo oopsie')
+ out, err = capsys.readouterr()
+ assert out == 'oopsie\n'
+ assert len(err) > len('Error: oopsie\n')
+ assert 'ERROR: oopsie' in err
+
+def test_colors_terminal_notty(mocker, capsys):
+ app = ColorsApp()
+ app.colors = cmd2.constants.COLORS_TERMINAL
+ mocker.patch.object(app.stdout, 'isatty', return_value=False)
+ mocker.patch.object(sys.stderr, 'isatty', return_value=False)
+
+ app.onecmd_plus_hooks('echo_error oopsie')
+ out, err = capsys.readouterr()
+ assert out == 'oopsie\n'
+ assert err == 'ERROR: oopsie\n'
+
+ app.onecmd_plus_hooks('echo oopsie')
+ out, err = capsys.readouterr()
+ assert out == 'oopsie\n'
+ assert err == 'ERROR: oopsie\n'
+
+def test_colors_never_tty(mocker, capsys):
+ app = ColorsApp()
+ app.colors = cmd2.constants.COLORS_NEVER
+ mocker.patch.object(app.stdout, 'isatty', return_value=True)
+ mocker.patch.object(sys.stderr, 'isatty', return_value=True)
+
+ app.onecmd_plus_hooks('echo_error oopsie')
+ out, err = capsys.readouterr()
+ assert out == 'oopsie\n'
+ assert err == 'ERROR: oopsie\n'
+
+ app.onecmd_plus_hooks('echo oopsie')
+ out, err = capsys.readouterr()
+ assert out == 'oopsie\n'
+ assert err == 'ERROR: oopsie\n'
+
+def test_colors_never_notty(mocker, capsys):
+ app = ColorsApp()
+ app.colors = cmd2.constants.COLORS_NEVER
+ mocker.patch.object(app.stdout, 'isatty', return_value=False)
+ mocker.patch.object(sys.stderr, 'isatty', return_value=False)
+
+ app.onecmd_plus_hooks('echo_error oopsie')
+ out, err = capsys.readouterr()
+ assert out == 'oopsie\n'
+ assert err == 'ERROR: oopsie\n'
+
+ app.onecmd_plus_hooks('echo oopsie')
+ out, err = capsys.readouterr()
+ assert out == 'oopsie\n'
+ assert err == 'ERROR: oopsie\n'
diff --git a/tests/transcripts/regex_set.txt b/tests/transcripts/regex_set.txt
index b818c464..d45672a7 100644
--- a/tests/transcripts/regex_set.txt
+++ b/tests/transcripts/regex_set.txt
@@ -1,10 +1,10 @@
# Run this transcript with "python example.py -t transcript_regex.txt"
-# The regex for colors is because no color on Windows.
+# The regex for colors shows all possible settings for colors
# The regex for editor will match whatever program you use.
# Regexes on prompts just make the trailing space obvious
(Cmd) set
-colors: /(True|False)/
+colors: /(Terminal|Always|Never)/
continuation_prompt: >/ /
debug: False
echo: False