Skip to content

Commit e66dc6f

Browse files
committed
Having two parallel datastructures each for foreground and background colors felt really clunky - now we just have one
The Enum classes are now smart and deal with it all.
1 parent ede9f3e commit e66dc6f

File tree

6 files changed

+46
-65
lines changed

6 files changed

+46
-65
lines changed

cmd2/ansi.py

Lines changed: 27 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from enum import Enum, unique
77
import functools
88
import re
9-
from typing import Any, IO, Union
9+
from typing import Any, IO, List, Union
1010

1111
import colorama
1212
from colorama import Fore, Back, Style
@@ -26,28 +26,7 @@
2626
# Regular expression to match ANSI style sequences (including 8-bit and 24-bit colors)
2727
ANSI_STYLE_RE = re.compile(r'\x1b\[[^m]*m')
2828

29-
# Foreground color presets
30-
FG_COLORS = {
31-
'black': Fore.BLACK,
32-
'red': Fore.RED,
33-
'green': Fore.GREEN,
34-
'yellow': Fore.YELLOW,
35-
'blue': Fore.BLUE,
36-
'magenta': Fore.MAGENTA,
37-
'cyan': Fore.CYAN,
38-
'white': Fore.WHITE,
39-
'bright_black': Fore.LIGHTBLACK_EX,
40-
'bright_red': Fore.LIGHTRED_EX,
41-
'bright_green': Fore.LIGHTGREEN_EX,
42-
'bright_yellow': Fore.LIGHTYELLOW_EX,
43-
'bright_blue': Fore.LIGHTBLUE_EX,
44-
'bright_magenta': Fore.LIGHTMAGENTA_EX,
45-
'bright_cyan': Fore.LIGHTCYAN_EX,
46-
'bright_white': Fore.LIGHTWHITE_EX,
47-
'reset': Fore.RESET,
48-
}
49-
50-
29+
# Foreground colors
5130
@unique
5231
class fg(Enum):
5332
"""Enum class for foreground colors (to support IDE autocompletion)."""
@@ -69,33 +48,22 @@ class fg(Enum):
6948
bright_white = Fore.LIGHTWHITE_EX
7049
reset = Fore.RESET
7150

72-
def __str__(self):
51+
def __str__(self) -> str:
7352
"""Make the value the string representation instead of the enum name."""
7453
return self.value
7554

55+
@staticmethod
56+
def colors() -> List[str]:
57+
"""Return a list of color names."""
58+
return [color.name for color in fg]
7659

77-
# Background color presets
78-
BG_COLORS = {
79-
'black': Back.BLACK,
80-
'red': Back.RED,
81-
'green': Back.GREEN,
82-
'yellow': Back.YELLOW,
83-
'blue': Back.BLUE,
84-
'magenta': Back.MAGENTA,
85-
'cyan': Back.CYAN,
86-
'white': Back.WHITE,
87-
'bright_black': Back.LIGHTBLACK_EX,
88-
'bright_red': Back.LIGHTRED_EX,
89-
'bright_green': Back.LIGHTGREEN_EX,
90-
'bright_yellow': Back.LIGHTYELLOW_EX,
91-
'bright_blue': Back.LIGHTBLUE_EX,
92-
'bright_magenta': Back.LIGHTMAGENTA_EX,
93-
'bright_cyan': Back.LIGHTCYAN_EX,
94-
'bright_white': Back.LIGHTWHITE_EX,
95-
'reset': Back.RESET,
96-
}
60+
@staticmethod
61+
def get_value(color: str) -> str:
62+
"""Retrieve color code by name string."""
63+
return fg.__members__[color].value
9764

9865

66+
# Background colors
9967
@unique
10068
class bg(Enum):
10169
"""Enum class for background colors (to support IDE autocompletion)."""
@@ -117,13 +85,23 @@ class bg(Enum):
11785
bright_white = Back.LIGHTWHITE_EX
11886
reset = Back.RESET
11987

120-
def __str__(self):
88+
def __str__(self) -> str:
12189
"""Make the value the string representation instead of the enum name."""
12290
return self.value
12391

92+
@staticmethod
93+
def colors() -> List[str]:
94+
"""Return a list of color names."""
95+
return [color.name for color in bg]
96+
97+
@staticmethod
98+
def get_value(color: str) -> str:
99+
"""Retrieve color code by name string."""
100+
return bg.__members__[color].value
101+
124102

125-
FG_RESET = FG_COLORS['reset']
126-
BG_RESET = BG_COLORS['reset']
103+
FG_RESET = fg.reset.value
104+
BG_RESET = bg.reset.value
127105
RESET_ALL = Style.RESET_ALL
128106

129107
# Text intensities
@@ -181,7 +159,7 @@ def fg_lookup(fg_name: Union[str, fg]) -> str:
181159
return fg_name.value
182160

183161
try:
184-
ansi_escape = FG_COLORS[fg_name.lower()]
162+
ansi_escape = fg.get_value(fg_name.lower())
185163
except KeyError:
186164
raise ValueError('Foreground color {!r} does not exist.'.format(fg_name))
187165
return ansi_escape
@@ -199,7 +177,7 @@ def bg_lookup(bg_name: Union[str, bg]) -> str:
199177
return bg_name.value
200178

201179
try:
202-
ansi_escape = BG_COLORS[bg_name.lower()]
180+
ansi_escape = bg.get_value(bg_name.lower())
203181
except KeyError:
204182
raise ValueError('Background color {!r} does not exist.'.format(bg_name))
205183
return ansi_escape

docs/features/initialization.rst

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ capabilities which you may wish to utilize while initializing the app::
1919
10) How to make custom attributes settable at runtime
2020
"""
2121
import cmd2
22-
from cmd2 import style
23-
from cmd2.ansi import FG_COLORS
22+
from cmd2 import style, fg, bg
2423

2524
class BasicApp(cmd2.Cmd):
2625
CUSTOM_CATEGORY = 'My Custom Commands'
@@ -30,7 +29,7 @@ capabilities which you may wish to utilize while initializing the app::
3029
startup_script='scripts/startup.txt', use_ipython=True)
3130

3231
# Prints an intro banner once upon application startup
33-
self.intro = style('Welcome to cmd2!', fg='red', bg='white', bold=True)
32+
self.intro = style('Welcome to cmd2!', fg=fg.red, bg=bg.white, bold=True)
3433

3534
# Show this as the prompt when asking for input
3635
self.prompt = 'myapp> '
@@ -51,7 +50,7 @@ capabilities which you may wish to utilize while initializing the app::
5150
self.add_settable(cmd2.Settable('foreground_color',
5251
str,
5352
'Foreground color to use with echo command',
54-
choices=FG_COLORS))
53+
choices=fg.colors()))
5554

5655
@cmd2.with_category(CUSTOM_CATEGORY)
5756
def do_intro(self, _):

examples/colors.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ def __init__(self):
4848
speak_parser.add_argument('-p', '--piglatin', action='store_true', help='atinLay')
4949
speak_parser.add_argument('-s', '--shout', action='store_true', help='N00B EMULATION MODE')
5050
speak_parser.add_argument('-r', '--repeat', type=int, help='output [n] times')
51-
speak_parser.add_argument('-f', '--fg', choices=ansi.FG_COLORS, help='foreground color to apply to output')
52-
speak_parser.add_argument('-b', '--bg', choices=ansi.BG_COLORS, help='background color to apply to output')
51+
speak_parser.add_argument('-f', '--fg', choices=ansi.fg.colors(), help='foreground color to apply to output')
52+
speak_parser.add_argument('-b', '--bg', choices=ansi.bg.colors(), help='background color to apply to output')
5353
speak_parser.add_argument('-l', '--bold', action='store_true', help='bold the output')
5454
speak_parser.add_argument('-u', '--underline', action='store_true', help='underline the output')
5555
speak_parser.add_argument('words', nargs='+', help='words to say')

examples/initialization.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313
10) How to make custom attributes settable at runtime
1414
"""
1515
import cmd2
16-
from cmd2 import style
17-
from cmd2.ansi import FG_COLORS
16+
from cmd2 import style, fg, bg
1817

1918

2019
class BasicApp(cmd2.Cmd):
@@ -25,7 +24,7 @@ def __init__(self):
2524
startup_script='scripts/startup.txt', use_ipython=True)
2625

2726
# Prints an intro banner once upon application startup
28-
self.intro = style('Welcome to cmd2!', fg='red', bg='white', bold=True)
27+
self.intro = style('Welcome to cmd2!', fg=fg.red, bg=bg.white, bold=True)
2928

3029
# Show this as the prompt when asking for input
3130
self.prompt = 'myapp> '
@@ -44,7 +43,7 @@ def __init__(self):
4443

4544
# Make echo_fg settable at runtime
4645
self.add_settable(cmd2.Settable('foreground_color', str, 'Foreground color to use with echo command',
47-
choices=FG_COLORS))
46+
choices=fg.colors()))
4847

4948
@cmd2.with_category(CUSTOM_CATEGORY)
5049
def do_intro(self, _):

examples/pirate.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def __init__(self):
2525
self.songcolor = 'blue'
2626

2727
# Make songcolor settable at runtime
28-
self.add_settable(cmd2.Settable('songcolor', str, 'Color to ``sing``', choices=cmd2.ansi.FG_COLORS))
28+
self.add_settable(cmd2.Settable('songcolor', str, 'Color to ``sing``', choices=cmd2.ansi.fg.colors()))
2929

3030
# prompts and defaults
3131
self.gold = 0

tests/test_ansi.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ def test_style_none():
3232
def test_style_fg():
3333
base_str = HELLO_WORLD
3434
fg_color = 'blue'
35-
ansi_str = ansi.FG_COLORS[fg_color] + base_str + ansi.FG_RESET
35+
ansi_str = ansi.fg.get_value(fg_color) + base_str + ansi.FG_RESET
3636
assert ansi.style(base_str, fg=fg_color) == ansi_str
3737

3838

3939
def test_style_bg():
4040
base_str = HELLO_WORLD
4141
bg_color = 'green'
42-
ansi_str = ansi.BG_COLORS[bg_color] + base_str + ansi.BG_RESET
42+
ansi_str = ansi.bg.get_value(bg_color) + base_str + ansi.BG_RESET
4343
assert ansi.style(base_str, bg=bg_color) == ansi_str
4444

4545

@@ -65,7 +65,7 @@ def test_style_multi():
6565
base_str = HELLO_WORLD
6666
fg_color = 'blue'
6767
bg_color = 'green'
68-
ansi_str = (ansi.FG_COLORS[fg_color] + ansi.BG_COLORS[bg_color] +
68+
ansi_str = (ansi.fg.get_value(fg_color) + ansi.bg.get_value(bg_color) +
6969
ansi.INTENSITY_BRIGHT + ansi.INTENSITY_DIM + ansi.UNDERLINE_ENABLE +
7070
base_str +
7171
ansi.FG_RESET + ansi.BG_RESET +
@@ -85,7 +85,7 @@ def test_style_color_not_exist():
8585

8686
def test_fg_lookup_exist():
8787
fg_color = 'green'
88-
assert ansi.fg_lookup(fg_color) == ansi.FG_COLORS[fg_color]
88+
assert ansi.fg_lookup(fg_color) == ansi.fg.get_value(fg_color)
8989

9090

9191
def test_fg_lookup_nonexist():
@@ -95,7 +95,7 @@ def test_fg_lookup_nonexist():
9595

9696
def test_bg_lookup_exist():
9797
bg_color = 'green'
98-
assert ansi.bg_lookup(bg_color) == ansi.BG_COLORS[bg_color]
98+
assert ansi.bg_lookup(bg_color) == ansi.bg.get_value(bg_color)
9999

100100

101101
def test_bg_lookup_nonexist():
@@ -133,3 +133,8 @@ def test_bg_enum():
133133
def test_bg_enum_to_str():
134134
assert str(ansi.bg.blue) == ansi.bg_lookup('blue')
135135

136+
def test_fg_colors():
137+
assert list(ansi.fg.__members__.keys()) == ansi.fg.colors()
138+
139+
def test_bg_colors():
140+
assert list(ansi.bg.__members__.keys()) == ansi.bg.colors()

0 commit comments

Comments
 (0)