Skip to content

Commit f91ccf2

Browse files
committed
Simplified ansi color dictionaries and lookup methods
Also: - Updated examples that use color to use cmd2.ansi instead of colorama - Updated tests that use color to use cmd2.ansi instead of colorama - plumbum_colorspy example shows how to override color lookup functions to use a different color library
1 parent 0038893 commit f91ccf2

File tree

8 files changed

+92
-221
lines changed

8 files changed

+92
-221
lines changed

cmd2/ansi.py

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@
2121

2222
# Foreground color presets
2323
FG_COLORS = {
24+
'black': Fore.BLACK,
25+
'red': Fore.RED,
26+
'green': Fore.GREEN,
27+
'yellow': Fore.YELLOW,
28+
'blue': Fore.BLUE,
29+
'magenta': Fore.MAGENTA,
30+
'cyan': Fore.CYAN,
31+
'white': Fore.WHITE,
2432
'reset': Fore.RESET,
25-
'gray': Fore.LIGHTBLACK_EX,
26-
'lightred': Fore.LIGHTRED_EX,
27-
'lightblue': Fore.LIGHTBLUE_EX,
28-
'lightgreen': Fore.LIGHTGREEN_EX,
29-
'lightyellow': Fore.LIGHTYELLOW_EX,
30-
'lightmagenta': Fore.LIGHTMAGENTA_EX,
31-
'lightcyan': Fore.LIGHTCYAN_EX,
32-
'lightwhite': Fore.LIGHTWHITE_EX,
3333
'bright_black': Fore.LIGHTBLACK_EX,
3434
'bright_red': Fore.LIGHTRED_EX,
3535
'bright_green': Fore.LIGHTGREEN_EX,
@@ -39,19 +39,18 @@
3939
'bright_cyan': Fore.LIGHTCYAN_EX,
4040
'bright_white': Fore.LIGHTWHITE_EX,
4141
}
42-
FG_RESET = FG_COLORS['reset']
4342

4443
# Background color presets
4544
BG_COLORS = {
45+
'black': Back.BLACK,
46+
'red': Back.RED,
47+
'green': Back.GREEN,
48+
'yellow': Back.YELLOW,
49+
'blue': Back.BLUE,
50+
'magenta': Back.MAGENTA,
51+
'cyan': Back.CYAN,
52+
'white': Back.WHITE,
4653
'reset': Back.RESET,
47-
'gray': Back.LIGHTBLACK_EX,
48-
'lightred': Back.LIGHTRED_EX,
49-
'lightblue': Back.LIGHTBLUE_EX,
50-
'lightgreen': Back.LIGHTGREEN_EX,
51-
'lightyellow': Back.LIGHTYELLOW_EX,
52-
'lightmagenta': Back.LIGHTMAGENTA_EX,
53-
'lightcyan': Back.LIGHTCYAN_EX,
54-
'lightwhite': Back.LIGHTWHITE_EX,
5554
'bright_black': Back.LIGHTBLACK_EX,
5655
'bright_red': Back.LIGHTRED_EX,
5756
'bright_green': Back.LIGHTGREEN_EX,
@@ -61,7 +60,10 @@
6160
'bright_cyan': Back.LIGHTCYAN_EX,
6261
'bright_white': Back.LIGHTWHITE_EX,
6362
}
63+
64+
FG_RESET = FG_COLORS['reset']
6465
BG_RESET = BG_COLORS['reset']
66+
RESET_ALL = Style.RESET_ALL
6567

6668
# ANSI escape sequences not provided by colorama
6769
UNDERLINE_ENABLE = colorama.ansi.code_to_chars(4)
@@ -104,38 +106,28 @@ def ansi_aware_write(fileobj: IO, msg: str) -> None:
104106
def fg_lookup(fg_name: str) -> str:
105107
"""Look up ANSI escape codes based on foreground color name.
106108
107-
This function first searches the FG_COLORS dictionary and then falls back to searching colorama.Fore.
108-
109109
:param fg_name: foreground color name to look up ANSI escape code(s) for
110110
:return: ANSI escape code(s) associated with this color
111111
:raises ValueError if the color cannot be found
112112
"""
113113
try:
114114
ansi_escape = FG_COLORS[fg_name.lower()]
115115
except KeyError:
116-
try:
117-
ansi_escape = getattr(Fore, fg_name.upper())
118-
except AttributeError:
119-
raise ValueError('Foreground color {!r} does not exist.'.format(fg_name))
116+
raise ValueError('Foreground color {!r} does not exist.'.format(fg_name))
120117
return ansi_escape
121118

122119

123120
def bg_lookup(bg_name: str) -> str:
124121
"""Look up ANSI escape codes based on background color name.
125122
126-
This function first searches the BG_COLORS dictionary and then falls back to searching colorama.Back.
127-
128123
:param bg_name: background color name to look up ANSI escape code(s) for
129124
:return: ANSI escape code(s) associated with this color
130125
:raises ValueError if the color cannot be found
131126
"""
132127
try:
133128
ansi_escape = BG_COLORS[bg_name.lower()]
134129
except KeyError:
135-
try:
136-
ansi_escape = getattr(Back, bg_name.upper())
137-
except AttributeError:
138-
raise ValueError('Background color {!r} does not exist.'.format(bg_name))
130+
raise ValueError('Background color {!r} does not exist.'.format(bg_name))
139131
return ansi_escape
140132

141133

@@ -144,7 +136,7 @@ def style(text: Any, *, fg: str = '', bg: str = '', bold: bool = False, underlin
144136
Applies styles to text
145137
146138
:param text: Any object compatible with str.format()
147-
:param fg: foreground color. Expects color names in FG_COLORS (e.g. 'lightred'). Defaults to no color.
139+
:param fg: foreground color. Expects color names in FG_COLORS (e.g. 'red'). Defaults to no color.
148140
:param bg: background color. Expects color names in BG_COLORS (e.g. 'black'). Defaults to no color.
149141
:param bold: apply the bold style if True. Defaults to False.
150142
:param underline: apply the underline style if True. Defaults to False.
@@ -186,5 +178,5 @@ def style(text: Any, *, fg: str = '', bg: str = '', bold: bool = False, underlin
186178
# These can be altered to suit an application's needs and only need to be a
187179
# function with the following structure: func(str) -> str
188180
style_success = functools.partial(style, fg='green', bold=True)
189-
style_warning = functools.partial(style, fg='lightyellow')
190-
style_error = functools.partial(style, fg='lightred')
181+
style_warning = functools.partial(style, fg='bright_yellow')
182+
style_error = functools.partial(style, fg='bright_red')

examples/async_printing.py

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@
44
A simple example demonstrating an application that asynchronously prints alerts, updates the prompt
55
and changes the window title
66
"""
7-
87
import random
98
import threading
109
import time
1110
from typing import List
1211

13-
from colorama import Fore
14-
1512
import cmd2
13+
from cmd2 import ansi
1614

1715
ALERTS = ["Watch as this application prints alerts and updates the prompt",
1816
"This will only happen when the prompt is present",
@@ -141,26 +139,12 @@ def _generate_alert_str(self) -> str:
141139
return alert_str
142140

143141
def _generate_colored_prompt(self) -> str:
144-
"""
145-
Randomly generates a colored prompt
142+
"""Randomly generates a colored prompt
146143
:return: the new prompt
147144
"""
148-
rand_num = random.randint(1, 20)
149-
150-
status_color = Fore.RESET
151-
152-
if rand_num == 1:
153-
status_color = Fore.LIGHTRED_EX
154-
elif rand_num == 2:
155-
status_color = Fore.LIGHTYELLOW_EX
156-
elif rand_num == 3:
157-
status_color = Fore.CYAN
158-
elif rand_num == 4:
159-
status_color = Fore.LIGHTGREEN_EX
160-
elif rand_num == 5:
161-
status_color = Fore.LIGHTBLUE_EX
162-
163-
return status_color + self.visible_prompt + Fore.RESET
145+
fg_color = random.choice(list(ansi.FG_COLORS.keys()))
146+
bg_color = random.choice(list(ansi.BG_COLORS.keys()))
147+
return ansi.style(self.visible_prompt, fg=fg_color, bg=bg_color)
164148

165149
def _alerter_thread_func(self) -> None:
166150
""" Prints alerts and updates the prompt any time the prompt is showing """

examples/colors.py

Lines changed: 12 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
Experiment with the command line options on the `speak` command to see how
77
different output colors ca
88
9-
The colors setting has three possible values:
9+
The allow_ansi setting has three possible values:
1010
1111
Never
1212
poutput(), pfeedback(), and ppaged() strip all ANSI escape sequences
@@ -16,68 +16,38 @@
1616
(the default value) poutput(), pfeedback(), and ppaged() do not strip any
1717
ANSI escape sequences when the output is a terminal, but if the output is
1818
a pipe or a file the escape sequences are stripped. If you want colorized
19-
output you must add ANSI escape sequences, preferably using some python
20-
color library like `plumbum.colors`, `colorama`, `blessings`, or
21-
`termcolor`.
19+
output you must add ANSI escape sequences using either cmd2's internal ansi
20+
module or another color library such as `plumbum.colors` or `colorama`.
2221
2322
Always
2423
poutput(), pfeedback(), and ppaged() never strip ANSI escape sequences,
2524
regardless of the output destination
2625
"""
27-
28-
import random
2926
import argparse
3027

3128
import cmd2
32-
from colorama import Fore, Back
33-
34-
FG_COLORS = {
35-
'black': Fore.BLACK,
36-
'red': Fore.RED,
37-
'green': Fore.GREEN,
38-
'yellow': Fore.YELLOW,
39-
'blue': Fore.BLUE,
40-
'magenta': Fore.MAGENTA,
41-
'cyan': Fore.CYAN,
42-
'white': Fore.WHITE,
43-
}
44-
BG_COLORS = {
45-
'black': Back.BLACK,
46-
'red': Back.RED,
47-
'green': Back.GREEN,
48-
'yellow': Back.YELLOW,
49-
'blue': Back.BLUE,
50-
'magenta': Back.MAGENTA,
51-
'cyan': Back.CYAN,
52-
'white': Back.WHITE,
53-
}
29+
from cmd2 import ansi
5430

5531

5632
class CmdLineApp(cmd2.Cmd):
5733
"""Example cmd2 application demonstrating colorized output."""
58-
59-
# Setting this true makes it run a shell command if a cmd2/cmd command doesn't exist
60-
# default_to_shell = True
61-
MUMBLES = ['like', '...', 'um', 'er', 'hmmm', 'ahh']
62-
MUMBLE_FIRST = ['so', 'like', 'well']
63-
MUMBLE_LAST = ['right?']
64-
6534
def __init__(self):
66-
shortcuts = dict(cmd2.DEFAULT_SHORTCUTS)
67-
shortcuts.update({'&': 'speak'})
6835
# Set use_ipython to True to enable the "ipy" command which embeds and interactive IPython shell
69-
super().__init__(use_ipython=True, multiline_commands=['orate'], shortcuts=shortcuts)
36+
super().__init__(use_ipython=True)
7037

7138
self.maxrepeats = 3
7239
# Make maxrepeats settable at runtime
7340
self.settable['maxrepeats'] = 'max repetitions for speak command'
7441

42+
# Should ANSI color output be allowed
43+
self.allow_ansi = ansi.ANSI_TERMINAL
44+
7545
speak_parser = argparse.ArgumentParser()
7646
speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay')
7747
speak_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE')
7848
speak_parser.add_argument('-r', '--repeat', type=int, help='output [n] times')
79-
speak_parser.add_argument('-f', '--fg', choices=FG_COLORS, help='foreground color to apply to output')
80-
speak_parser.add_argument('-b', '--bg', choices=BG_COLORS, help='background color to apply to output')
49+
speak_parser.add_argument('-f', '--fg', choices=ansi.FG_COLORS, help='foreground color to apply to output')
50+
speak_parser.add_argument('-b', '--bg', choices=ansi.BG_COLORS, help='background color to apply to output')
8151
speak_parser.add_argument('words', nargs='+', help='words to say')
8252

8353
@cmd2.with_argparser(speak_parser)
@@ -92,49 +62,11 @@ def do_speak(self, args):
9262
words.append(word)
9363

9464
repetitions = args.repeat or 1
95-
96-
color_on = ''
97-
if args.fg:
98-
color_on += FG_COLORS[args.fg]
99-
if args.bg:
100-
color_on += BG_COLORS[args.bg]
101-
color_off = Fore.RESET + Back.RESET
65+
output_str = ansi.style(' '.join(words), fg=args.fg, bg=args.bg)
10266

10367
for i in range(min(repetitions, self.maxrepeats)):
10468
# .poutput handles newlines, and accommodates output redirection too
105-
self.poutput(color_on + ' '.join(words) + color_off)
106-
107-
do_say = do_speak # now "say" is a synonym for "speak"
108-
do_orate = do_speak # another synonym, but this one takes multi-line input
109-
110-
mumble_parser = argparse.ArgumentParser()
111-
mumble_parser.add_argument('-r', '--repeat', type=int, help='how many times to repeat')
112-
mumble_parser.add_argument('-f', '--fg', help='foreground color to apply to output')
113-
mumble_parser.add_argument('-b', '--bg', help='background color to apply to output')
114-
mumble_parser.add_argument('words', nargs='+', help='words to say')
115-
116-
@cmd2.with_argparser(mumble_parser)
117-
def do_mumble(self, args):
118-
"""Mumbles what you tell me to."""
119-
color_on = ''
120-
if args.fg and args.fg in FG_COLORS:
121-
color_on += FG_COLORS[args.fg]
122-
if args.bg and args.bg in BG_COLORS:
123-
color_on += BG_COLORS[args.bg]
124-
color_off = Fore.RESET + Back.RESET
125-
126-
repetitions = args.repeat or 1
127-
for i in range(min(repetitions, self.maxrepeats)):
128-
output = []
129-
if random.random() < .33:
130-
output.append(random.choice(self.MUMBLE_FIRST))
131-
for word in args.words:
132-
if random.random() < .40:
133-
output.append(random.choice(self.MUMBLES))
134-
output.append(word)
135-
if random.random() < .25:
136-
output.append(random.choice(self.MUMBLE_LAST))
137-
self.poutput(color_on + ' '.join(output) + color_off)
69+
self.poutput(output_str)
13870

13971

14072
if __name__ == '__main__':

0 commit comments

Comments
 (0)