Skip to content

Commit c966480

Browse files
committed
Changed default styles to use a more flexible approach which could be used to call any function to add style
1 parent 9f07daa commit c966480

File tree

3 files changed

+24
-60
lines changed

3 files changed

+24
-60
lines changed

cmd2/ansi.py

Lines changed: 13 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# coding=utf-8
22
"""Support for ANSI escape codes which are used for things like applying style to text"""
3+
import functools
34
import re
4-
from typing import Any, Optional
5+
from typing import Any
56

67
import colorama
78
from colorama import Fore, Back, Style
@@ -77,48 +78,15 @@ def ansi_safe_wcswidth(text: str) -> int:
7778
UNDERLINE_DISABLE = colorama.ansi.code_to_chars(24)
7879

7980

80-
class TextStyle:
81-
"""Style settings for text"""
82-
83-
def __init__(self, *, fg: str = '', bg: str = '', bold: bool = False, underline: bool = False):
84-
"""
85-
Initializer
86-
:param fg: foreground color. Expects color names in FG_COLORS (e.g. 'lightred'). Defaults to blank.
87-
:param bg: background color. Expects color names in BG_COLORS (e.g. 'black'). Defaults to blank.
88-
:param bold: apply the bold style if True. Defaults to False.
89-
:param underline: apply the underline style if True. Defaults to False.
90-
"""
91-
self.fg = fg
92-
self.bg = bg
93-
self.bold = bold
94-
self.underline = underline
95-
96-
97-
# Default styles. These can be altered to suit an application's needs.
98-
SuccessStyle = TextStyle(fg='green', bold=True)
99-
WarningStyle = TextStyle(fg='lightyellow')
100-
ErrorStyle = TextStyle(fg='lightred')
101-
102-
103-
def style(text: Any, text_style: Optional[TextStyle] = None, *,
104-
fg: Optional[str] = None, bg: Optional[str] = None,
105-
bold: Optional[bool] = None, underline: Optional[bool] = None) -> str:
81+
def style(text: Any, *, fg: str = '', bg: str = '', bold: bool = False, underline: bool = False) -> str:
10682
"""
10783
Applies a style to text
10884
10985
:param text: Any object compatible with str.format()
110-
:param text_style: a TextStyle object. Defaults to None.
111-
112-
The following are keyword-only parameters which allow calling style without creating a TextStyle object
113-
style(text, fg='blue')
114-
115-
They can also be used to override a setting in a provided TextStyle
116-
style(text, ErrorStyle, underline=True)
117-
118-
:param fg: foreground color. Expects color names in FG_COLORS (e.g. 'lightred'). Defaults to None.
119-
:param bg: background color. Expects color names in BG_COLORS (e.g. 'black'). Defaults to None.
120-
:param bold: apply the bold style if True. Defaults to None.
121-
:param underline: apply the underline style if True. Defaults to None.
86+
:param fg: foreground color. Expects color names in FG_COLORS (e.g. 'lightred'). Defaults to no color.
87+
:param bg: background color. Expects color names in BG_COLORS (e.g. 'black'). Defaults to no color.
88+
:param bold: apply the bold style if True. Defaults to False.
89+
:param underline: apply the underline style if True. Defaults to False.
12290
12391
:return: the stylized string
12492
"""
@@ -131,16 +99,6 @@ def style(text: Any, text_style: Optional[TextStyle] = None, *,
13199
# Convert the text object into a string if it isn't already one
132100
text = "{}".format(text)
133101

134-
# Figure out what parameters to use
135-
if fg is None and text_style is not None:
136-
fg = text_style.fg
137-
if bg is None and text_style is not None:
138-
bg = text_style.bg
139-
if bold is None and text_style is not None:
140-
bold = text_style.bold
141-
if underline is None and text_style is not None:
142-
underline = text_style.underline
143-
144102
# Process the style settings
145103
if fg:
146104
try:
@@ -166,3 +124,9 @@ def style(text: Any, text_style: Optional[TextStyle] = None, *,
166124

167125
# Combine the ANSI escape strings with the text
168126
return "".join(additions) + text + "".join(removals)
127+
128+
129+
# Default styles. These can be altered to suit an application's needs.
130+
style_success = functools.partial(style, fg='green', bold=True)
131+
style_warning = functools.partial(style, fg='lightyellow')
132+
style_error = functools.partial(style, fg='lightred')

cmd2/argparse_completer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def my_completer(text: str, line: str, begidx: int, endidx:int, extra_param: str
6666
from argparse import ZERO_OR_MORE, ONE_OR_MORE, ArgumentError, _, _get_action_name, SUPPRESS
6767
from typing import List, Dict, Tuple, Callable, Union
6868

69-
from .ansi import ansi_safe_wcswidth, style, ErrorStyle
69+
from .ansi import ansi_safe_wcswidth, style_error
7070
from .rl_utils import rl_force_redisplay
7171

7272
# attribute that can optionally added to an argparse argument (called an Action) to
@@ -994,7 +994,7 @@ def error(self, message: str) -> None:
994994
linum += 1
995995

996996
self.print_usage(sys.stderr)
997-
formatted_message = style(formatted_message, ErrorStyle)
997+
formatted_message = style_error(formatted_message)
998998
self.exit(2, '{}\n\n'.format(formatted_message))
999999

10001000
def format_help(self) -> str:

cmd2/cmd2.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
rl_warning = "Readline features including tab completion have been disabled since no \n" \
6161
"supported version of readline was found. To resolve this, install \n" \
6262
"pyreadline on Windows or gnureadline on Mac.\n\n"
63-
sys.stderr.write(ansi.style(rl_warning, ansi.WarningStyle))
63+
sys.stderr.write(ansi.style_warning(rl_warning))
6464
else:
6565
from .rl_utils import rl_force_redisplay, readline
6666

@@ -616,7 +616,7 @@ def perror(self, msg: Any, *, end: str = '\n', apply_style: bool = True) -> None
616616
where the message text already has the desired style. Defaults to True.
617617
"""
618618
if apply_style:
619-
final_msg = ansi.style(msg, ansi.ErrorStyle)
619+
final_msg = ansi.style_error(msg)
620620
else:
621621
final_msg = "{}".format(msg)
622622
self._decolorized_write(sys.stderr, final_msg + end)
@@ -639,11 +639,11 @@ def pexcept(self, msg: Any, *, end: str = '\n', apply_style: bool = True) -> Non
639639
final_msg = "{}".format(msg)
640640

641641
if apply_style:
642-
final_msg = ansi.style(final_msg, ansi.ErrorStyle)
642+
final_msg = ansi.style_error(final_msg)
643643

644644
if not self.debug:
645645
warning = "\nTo enable full traceback, run the following command: 'set debug true'"
646-
final_msg += ansi.style(warning, ansi.WarningStyle)
646+
final_msg += ansi.style_warning(warning)
647647

648648
# Set apply_style to False since style has already been applied
649649
self.perror(final_msg, end=end, apply_style=False)
@@ -3246,7 +3246,7 @@ def do_run_pyscript(self, args: argparse.Namespace) -> bool:
32463246
if args.__statement__.command == "pyscript":
32473247
warning = ("pyscript has been renamed and will be removed in the next release, "
32483248
"please use run_pyscript instead\n")
3249-
self.perror(ansi.style(warning, ansi.WarningStyle))
3249+
self.perror(ansi.style_warning(warning))
32503250

32513251
return py_return
32523252

@@ -3555,7 +3555,7 @@ def _generate_transcript(self, history: List[Union[HistoryItem, str]], transcrip
35553555
# Check if all commands ran
35563556
if commands_run < len(history):
35573557
warning = "Command {} triggered a stop and ended transcript generation early".format(commands_run)
3558-
self.perror(ansi.style(warning, ansi.WarningStyle))
3558+
self.perror(ansi.style_warning(warning))
35593559

35603560
# finally, we can write the transcript out to the file
35613561
try:
@@ -3675,7 +3675,7 @@ def do_run_script(self, args: argparse.Namespace) -> Optional[bool]:
36753675
if args.__statement__.command == "load":
36763676
warning = ("load has been renamed and will be removed in the next release, "
36773677
"please use run_script instead\n")
3678-
self.perror(ansi.style(warning, ansi.WarningStyle))
3678+
self.perror(ansi.style_warning(warning))
36793679

36803680
# load has been deprecated
36813681
do_load = do_run_script
@@ -3702,7 +3702,7 @@ def do__relative_run_script(self, args: argparse.Namespace) -> Optional[bool]:
37023702
if args.__statement__.command == "_relative_load":
37033703
warning = ("_relative_load has been renamed and will be removed in the next release, "
37043704
"please use _relative_run_script instead\n")
3705-
self.perror(ansi.style(warning, ansi.WarningStyle))
3705+
self.perror(ansi.style_warning(warning))
37063706

37073707
file_path = args.file_path
37083708
# NOTE: Relative path is an absolute path, it is just relative to the current script directory
@@ -3756,7 +3756,7 @@ class TestMyAppCase(Cmd2TestCase):
37563756
if test_results.wasSuccessful():
37573757
self._decolorized_write(sys.stderr, stream.read())
37583758
finish_msg = '{0} transcript{1} passed in {2:.3f} seconds'.format(num_transcripts, plural, execution_time)
3759-
finish_msg = ansi.style(utils.center_text(finish_msg, pad='='), ansi.SuccessStyle)
3759+
finish_msg = ansi.style_success(utils.center_text(finish_msg, pad='='))
37603760
self.poutput(finish_msg)
37613761
else:
37623762
# Strip off the initial traceback which isn't particularly useful for end users

0 commit comments

Comments
 (0)