Skip to content

Commit 447479e

Browse files
committed
Made allow_ansi an application-wide setting and moved it to ansi.py
1 parent 5b7a45e commit 447479e

File tree

4 files changed

+46
-34
lines changed

4 files changed

+46
-34
lines changed

cmd2/ansi.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@
88
from colorama import Fore, Back, Style
99
from wcwidth import wcswidth
1010

11+
# Values for allow_ansi setting
12+
ANSI_NEVER = 'Never'
13+
ANSI_TERMINAL = 'Terminal'
14+
ANSI_ALWAYS = 'Always'
15+
16+
# Controls when ANSI escape sequences are allowed in output
17+
allow_ansi = ANSI_TERMINAL
18+
1119
# Regular expression to match ANSI escape sequences
1220
ANSI_ESCAPE_RE = re.compile(r'\x1b[^m]*m')
1321

cmd2/cmd2.py

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,6 @@ def __init__(self, completekey: str = 'tab', stdin=None, stdout=None, *,
374374
self.quit_on_sigint = False # Quit the loop on interrupt instead of just resetting prompt
375375

376376
# Attributes which ARE dynamically settable at runtime
377-
self.allow_ansi = constants.ANSI_TERMINAL
378377
self.continuation_prompt = '> '
379378
self.debug = False
380379
self.echo = False
@@ -385,16 +384,20 @@ def __init__(self, completekey: str = 'tab', stdin=None, stdout=None, *,
385384
self.timing = False # Prints elapsed time for each command
386385

387386
# To make an attribute settable with the "do_set" command, add it to this ...
388-
self.settable = {'allow_ansi': 'Allow ANSI escape sequences in output (valid values: Terminal, Always, Never)',
389-
'continuation_prompt': 'On 2nd+ line of input',
390-
'debug': 'Show full error stack on error',
391-
'echo': 'Echo command issued into output',
392-
'editor': 'Program used by ``edit``',
393-
'feedback_to_output': 'Include nonessentials in `|`, `>` results',
394-
'locals_in_py': 'Allow access to your application in py via self',
395-
'prompt': 'The prompt issued to solicit input',
396-
'quiet': "Don't print nonessential feedback",
397-
'timing': 'Report execution times'}
387+
self.settable = \
388+
{
389+
# allow_ansi is a special case in which it's an application-wide setting defined in ansi.py
390+
'allow_ansi': 'Allow ANSI escape sequences in output (valid values: Terminal, Always, Never)',
391+
'continuation_prompt': 'On 2nd+ line of input',
392+
'debug': 'Show full error stack on error',
393+
'echo': 'Echo command issued into output',
394+
'editor': 'Program used by ``edit``',
395+
'feedback_to_output': 'Include nonessentials in `|`, `>` results',
396+
'locals_in_py': 'Allow access to your application in py via self',
397+
'prompt': 'The prompt issued to solicit input',
398+
'quiet': "Don't print nonessential feedback",
399+
'timing': 'Report execution times'
400+
}
398401

399402
# Commands to exclude from the help menu and tab completion
400403
self.hidden_commands = ['eof', '_relative_load', '_relative_run_script']
@@ -550,6 +553,16 @@ def __init__(self, completekey: str = 'tab', stdin=None, stdout=None, *,
550553

551554
# ----- Methods related to presenting output to the user -----
552555

556+
@property
557+
def allow_ansi(self) -> str:
558+
"""Read-only property needed to support do_set when it reads allow_ansi"""
559+
return ansi.allow_ansi
560+
561+
@allow_ansi.setter
562+
def allow_ansi(self, new_val: str) -> None:
563+
"""Read-only property needed to support do_set when it sets allow_ansi"""
564+
ansi.allow_ansi = new_val
565+
553566
@property
554567
def visible_prompt(self) -> str:
555568
"""Read-only property to get the visible prompt with any ANSI escape codes stripped.
@@ -581,8 +594,8 @@ def _decolorized_write(self, fileobj: IO, msg: str) -> None:
581594
582595
Honor the current colors setting, which requires us to check whether the fileobject is a tty.
583596
"""
584-
if self.allow_ansi.lower() == constants.ANSI_NEVER.lower() or \
585-
(self.allow_ansi.lower() == constants.ANSI_TERMINAL.lower() and not fileobj.isatty()):
597+
if ansi.allow_ansi.lower() == ansi.ANSI_NEVER.lower() or \
598+
(ansi.allow_ansi.lower() == ansi.ANSI_TERMINAL.lower() and not fileobj.isatty()):
586599
msg = ansi.strip_ansi(msg)
587600
fileobj.write(msg)
588601

@@ -612,7 +625,7 @@ def perror(self, msg: Any, *, end: str = '\n', apply_style: bool = True) -> None
612625
613626
:param msg: message to print (anything convertible to a str with '{}'.format() is OK)
614627
:param end: string appended after the end of the message, default a newline
615-
:param apply_style: If True, then ErrorStyle will be applied to the message text. Set to False in cases
628+
:param apply_style: If True, then ansi.style_error will be applied to the message text. Set to False in cases
616629
where the message text already has the desired style. Defaults to True.
617630
"""
618631
if apply_style:
@@ -691,7 +704,7 @@ def ppaged(self, msg: str, end: str = '\n', chop: bool = False) -> None:
691704
# Don't attempt to use a pager that can block if redirecting or running a script (either text or Python)
692705
# Also only attempt to use a pager if actually running in a real fully functional terminal
693706
if functional_terminal and not self._redirecting and not self._in_py and not self._script_dir:
694-
if self.allow_ansi.lower() == constants.ANSI_NEVER.lower():
707+
if ansi.allow_ansi.lower() == ansi.ANSI_NEVER.lower():
695708
msg_str = ansi.strip_ansi(msg_str)
696709

697710
pager = self.pager

cmd2/constants.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,3 @@
1616
LINE_FEED = '\n'
1717

1818
DEFAULT_SHORTCUTS = {'?': 'help', '!': 'shell', '@': 'run_script', '@@': '_relative_run_script'}
19-
20-
# Values for cmd2's allow_ansi setting
21-
ANSI_NEVER = 'Never'
22-
ANSI_TERMINAL = 'Terminal'
23-
ANSI_ALWAYS = 'Always'

tests/test_cmd2.py

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1459,15 +1459,15 @@ def test_poutput_none(outsim_app):
14591459

14601460
def test_poutput_color_always(outsim_app):
14611461
msg = 'Hello World'
1462-
outsim_app.allow_ansi = 'Always'
1462+
ansi.allow_ansi = ansi.ANSI_ALWAYS
14631463
outsim_app.poutput(ansi.style(msg, fg='cyan'))
14641464
out = outsim_app.stdout.getvalue()
14651465
expected = Fore.CYAN + msg + Fore.RESET + '\n'
14661466
assert out == expected
14671467

14681468
def test_poutput_color_never(outsim_app):
14691469
msg = 'Hello World'
1470-
outsim_app.allow_ansi = 'Never'
1470+
ansi.allow_ansi = ansi.ANSI_NEVER
14711471
outsim_app.poutput(ansi.style(msg, fg='cyan'))
14721472
out = outsim_app.stdout.getvalue()
14731473
expected = msg + '\n'
@@ -1767,7 +1767,7 @@ def test_ppaged(outsim_app):
17671767
def test_ppaged_strips_ansi_when_redirecting(outsim_app):
17681768
msg = 'testing...'
17691769
end = '\n'
1770-
outsim_app.allow_ansi = cmd2.constants.ANSI_TERMINAL
1770+
ansi.allow_ansi = ansi.ANSI_TERMINAL
17711771
outsim_app._redirecting = True
17721772
outsim_app.ppaged(Fore.RED + msg)
17731773
out = outsim_app.stdout.getvalue()
@@ -1776,7 +1776,7 @@ def test_ppaged_strips_ansi_when_redirecting(outsim_app):
17761776
def test_ppaged_strips_ansi_when_redirecting_if_always(outsim_app):
17771777
msg = 'testing...'
17781778
end = '\n'
1779-
outsim_app.allow_ansi = cmd2.constants.ANSI_ALWAYS
1779+
ansi.allow_ansi = ansi.ANSI_ALWAYS
17801780
outsim_app._redirecting = True
17811781
outsim_app.ppaged(Fore.RED + msg)
17821782
out = outsim_app.stdout.getvalue()
@@ -1910,13 +1910,9 @@ def do_echo_error(self, args):
19101910
# perror uses colors by default
19111911
self.perror(args)
19121912

1913-
def test_ansi_default():
1914-
app = AnsiApp()
1915-
assert app.allow_ansi == cmd2.constants.ANSI_TERMINAL
1916-
19171913
def test_ansi_pouterr_always_tty(mocker, capsys):
19181914
app = AnsiApp()
1919-
app.allow_ansi = cmd2.constants.ANSI_ALWAYS
1915+
ansi.allow_ansi = ansi.ANSI_ALWAYS
19201916
mocker.patch.object(app.stdout, 'isatty', return_value=True)
19211917
mocker.patch.object(sys.stderr, 'isatty', return_value=True)
19221918

@@ -1938,7 +1934,7 @@ def test_ansi_pouterr_always_tty(mocker, capsys):
19381934

19391935
def test_ansi_pouterr_always_notty(mocker, capsys):
19401936
app = AnsiApp()
1941-
app.allow_ansi = cmd2.constants.ANSI_ALWAYS
1937+
ansi.allow_ansi = ansi.ANSI_ALWAYS
19421938
mocker.patch.object(app.stdout, 'isatty', return_value=False)
19431939
mocker.patch.object(sys.stderr, 'isatty', return_value=False)
19441940

@@ -1960,7 +1956,7 @@ def test_ansi_pouterr_always_notty(mocker, capsys):
19601956

19611957
def test_ansi_terminal_tty(mocker, capsys):
19621958
app = AnsiApp()
1963-
app.allow_ansi = cmd2.constants.ANSI_TERMINAL
1959+
ansi.allow_ansi = ansi.ANSI_TERMINAL
19641960
mocker.patch.object(app.stdout, 'isatty', return_value=True)
19651961
mocker.patch.object(sys.stderr, 'isatty', return_value=True)
19661962

@@ -1981,7 +1977,7 @@ def test_ansi_terminal_tty(mocker, capsys):
19811977

19821978
def test_ansi_terminal_notty(mocker, capsys):
19831979
app = AnsiApp()
1984-
app.allow_ansi = cmd2.constants.ANSI_TERMINAL
1980+
ansi.allow_ansi = ansi.ANSI_TERMINAL
19851981
mocker.patch.object(app.stdout, 'isatty', return_value=False)
19861982
mocker.patch.object(sys.stderr, 'isatty', return_value=False)
19871983

@@ -1995,7 +1991,7 @@ def test_ansi_terminal_notty(mocker, capsys):
19951991

19961992
def test_ansi_never_tty(mocker, capsys):
19971993
app = AnsiApp()
1998-
app.allow_ansi = cmd2.constants.ANSI_NEVER
1994+
ansi.allow_ansi = ansi.ANSI_NEVER
19991995
mocker.patch.object(app.stdout, 'isatty', return_value=True)
20001996
mocker.patch.object(sys.stderr, 'isatty', return_value=True)
20011997

@@ -2009,7 +2005,7 @@ def test_ansi_never_tty(mocker, capsys):
20092005

20102006
def test_ansi_never_notty(mocker, capsys):
20112007
app = AnsiApp()
2012-
app.allow_ansi = cmd2.constants.ANSI_NEVER
2008+
ansi.allow_ansi = ansi.ANSI_NEVER
20132009
mocker.patch.object(app.stdout, 'isatty', return_value=False)
20142010
mocker.patch.object(sys.stderr, 'isatty', return_value=False)
20152011

0 commit comments

Comments
 (0)