Skip to content

Commit 54f9a7a

Browse files
committed
Merge remote-tracking branch 'origin/master' into pyscript
2 parents 9351245 + 1306eeb commit 54f9a7a

File tree

4 files changed

+49
-37
lines changed

4 files changed

+49
-37
lines changed

cmd2/cmd2.py

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@
4949
import pyparsing
5050
import pyperclip
5151

52+
from . import constants
53+
from . import utils
54+
5255
# Set up readline
5356
from .rl_utils import rl_force_redisplay, readline, rl_type, RlType
5457
from .argparse_completer import AutoCompleter, ACArgumentParser
@@ -134,10 +137,6 @@ def __subclasshook__(cls, C):
134137
# Strip outer quotes for convenience if POSIX_SHLEX = False
135138
STRIP_QUOTES_FOR_NON_POSIX = True
136139

137-
# Used for tab completion and word breaks. Do not change.
138-
QUOTES = ['"', "'"]
139-
REDIRECTION_CHARS = ['|', '<', '>']
140-
141140
# optional attribute, when tagged on a function, allows cmd2 to categorize commands
142141
HELP_CATEGORY = 'help_category'
143142
HELP_SUMMARY = 'help_summary'
@@ -406,19 +405,6 @@ class EmptyStatement(Exception):
406405
pass
407406

408407

409-
# Regular expression to match ANSI escape codes
410-
ANSI_ESCAPE_RE = re.compile(r'\x1b[^m]*m')
411-
412-
413-
def strip_ansi(text: str) -> str:
414-
"""Strip ANSI escape codes from a string.
415-
416-
:param text: string which may contain ANSI escape codes
417-
:return: the same string with any ANSI escape codes removed
418-
"""
419-
return ANSI_ESCAPE_RE.sub('', text)
420-
421-
422408
def _pop_readline_history(clear_history: bool=True) -> List[str]:
423409
"""Returns a copy of readline's history and optionally clears it (default)"""
424410
# noinspection PyArgumentList
@@ -863,7 +849,7 @@ def visible_prompt(self):
863849
864850
:return: str - prompt stripped of any ANSI escape codes
865851
"""
866-
return strip_ansi(self.prompt)
852+
return utils.strip_ansi(self.prompt)
867853

868854
def _finalize_app_parameters(self):
869855
self.commentGrammars.ignore(pyparsing.quotedString).setParseAction(lambda x: '')
@@ -1029,7 +1015,7 @@ def tokens_for_completion(self, line, begidx, endidx):
10291015
Both items are None
10301016
"""
10311017
unclosed_quote = ''
1032-
quotes_to_try = copy.copy(QUOTES)
1018+
quotes_to_try = copy.copy(constants.QUOTES)
10331019

10341020
tmp_line = line[:endidx]
10351021
tmp_endidx = endidx
@@ -1072,7 +1058,7 @@ def tokens_for_completion(self, line, begidx, endidx):
10721058
for cur_initial_token in initial_tokens:
10731059

10741060
# Save tokens up to 1 character in length or quoted tokens. No need to parse these.
1075-
if len(cur_initial_token) <= 1 or cur_initial_token[0] in QUOTES:
1061+
if len(cur_initial_token) <= 1 or cur_initial_token[0] in constants.QUOTES:
10761062
raw_tokens.append(cur_initial_token)
10771063
continue
10781064

@@ -1084,10 +1070,10 @@ def tokens_for_completion(self, line, begidx, endidx):
10841070
cur_raw_token = ''
10851071

10861072
while True:
1087-
if cur_char not in REDIRECTION_CHARS:
1073+
if cur_char not in constants.REDIRECTION_CHARS:
10881074

10891075
# Keep appending to cur_raw_token until we hit a redirect char
1090-
while cur_char not in REDIRECTION_CHARS:
1076+
while cur_char not in constants.REDIRECTION_CHARS:
10911077
cur_raw_token += cur_char
10921078
cur_index += 1
10931079
if cur_index < len(cur_initial_token):
@@ -1493,7 +1479,7 @@ def _redirect_complete(self, text, line, begidx, endidx, compfunc):
14931479
if len(raw_tokens) > 1:
14941480

14951481
# Build a list of all redirection tokens
1496-
all_redirects = REDIRECTION_CHARS + ['>>']
1482+
all_redirects = constants.REDIRECTION_CHARS + ['>>']
14971483

14981484
# Check if there are redirection strings prior to the token being completed
14991485
seen_pipe = False
@@ -1703,7 +1689,7 @@ def complete(self, text, state):
17031689
raw_completion_token = raw_tokens[-1]
17041690

17051691
# Check if the token being completed has an opening quote
1706-
if raw_completion_token and raw_completion_token[0] in QUOTES:
1692+
if raw_completion_token and raw_completion_token[0] in constants.QUOTES:
17071693

17081694
# Since the token is still being completed, we know the opening quote is unclosed
17091695
unclosed_quote = raw_completion_token[0]
@@ -2399,11 +2385,11 @@ def _cmdloop(self):
23992385
readline.set_completer(self.complete)
24002386

24012387
# Break words on whitespace and quotes when tab completing
2402-
completer_delims = " \t\n" + ''.join(QUOTES)
2388+
completer_delims = " \t\n" + ''.join(constants.QUOTES)
24032389

24042390
if self.allow_redirection:
24052391
# If redirection is allowed, then break words on those characters too
2406-
completer_delims += ''.join(REDIRECTION_CHARS)
2392+
completer_delims += ''.join(constants.REDIRECTION_CHARS)
24072393

24082394
readline.set_completer_delims(completer_delims)
24092395

@@ -2848,13 +2834,13 @@ def do_shell(self, command):
28482834
# Check if the token is quoted. Since shlex.split() passed, there isn't
28492835
# an unclosed quote, so we only need to check the first character.
28502836
first_char = tokens[index][0]
2851-
if first_char in QUOTES:
2837+
if first_char in constants.QUOTES:
28522838
tokens[index] = strip_quotes(tokens[index])
28532839

28542840
tokens[index] = os.path.expanduser(tokens[index])
28552841

28562842
# Restore the quotes
2857-
if first_char in QUOTES:
2843+
if first_char in constants.QUOTES:
28582844
tokens[index] = first_char + tokens[index] + first_char
28592845

28602846
expanded_command = ' '.join(tokens)
@@ -3724,13 +3710,13 @@ def runTest(self): # was testall
37243710
def _test_transcript(self, fname, transcript):
37253711
line_num = 0
37263712
finished = False
3727-
line = strip_ansi(next(transcript))
3713+
line = utils.strip_ansi(next(transcript))
37283714
line_num += 1
37293715
while not finished:
37303716
# Scroll forward to where actual commands begin
37313717
while not line.startswith(self.cmdapp.visible_prompt):
37323718
try:
3733-
line = strip_ansi(next(transcript))
3719+
line = utils.strip_ansi(next(transcript))
37343720
except StopIteration:
37353721
finished = True
37363722
break
@@ -3754,13 +3740,13 @@ def _test_transcript(self, fname, transcript):
37543740
self.cmdapp.onecmd_plus_hooks(command)
37553741
result = self.cmdapp.stdout.read()
37563742
# Read the expected result from transcript
3757-
if strip_ansi(line).startswith(self.cmdapp.visible_prompt):
3743+
if utils.strip_ansi(line).startswith(self.cmdapp.visible_prompt):
37583744
message = '\nFile {}, line {}\nCommand was:\n{}\nExpected: (nothing)\nGot:\n{}\n'.format(
37593745
fname, line_num, command, result)
37603746
self.assert_(not (result.strip()), message)
37613747
continue
37623748
expected = []
3763-
while not strip_ansi(line).startswith(self.cmdapp.visible_prompt):
3749+
while not utils.strip_ansi(line).startswith(self.cmdapp.visible_prompt):
37643750
expected.append(line)
37653751
try:
37663752
line = next(transcript)

cmd2/constants.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#
2+
# coding=utf-8
3+
"""Constants and definitions"""
4+
5+
import re
6+
7+
# Used for command parsing, tab completion and word breaks. Do not change.
8+
QUOTES = ['"', "'"]
9+
REDIRECTION_CHARS = ['|', '<', '>']
10+
11+
# Regular expression to match ANSI escape codes
12+
ANSI_ESCAPE_RE = re.compile(r'\x1b[^m]*m')

cmd2/utils.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#
2+
# coding=utf-8
3+
"""Shared utility functions"""
4+
5+
from . import constants
6+
7+
def strip_ansi(text: str) -> str:
8+
"""Strip ANSI escape codes from a string.
9+
10+
:param text: string which may contain ANSI escape codes
11+
:return: the same string with any ANSI escape codes removed
12+
"""
13+
return constants.ANSI_ESCAPE_RE.sub('', text)

tests/test_completion.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -322,10 +322,11 @@ def test_path_completion_doesnt_match_wildcards(cmd2_app, request):
322322
# Currently path completion doesn't accept wildcards, so will always return empty results
323323
assert cmd2_app.path_complete(text, line, begidx, endidx) == []
324324

325-
def test_path_completion_expand_user_dir(cmd2_app):
326-
# Get the current user. We can't use getpass.getuser() since
327-
# that doesn't work when running these tests on Windows in AppVeyor.
328-
user = os.path.basename(os.path.expanduser('~'))
325+
@pytest.mark.skipif(sys.platform == 'win32', reason="getpass.getuser() does not work on Windows in AppVeyor because "
326+
"no user name environment variables are set")
327+
def test_path_completion_complete_user(cmd2_app):
328+
import getpass
329+
user = getpass.getuser()
329330

330331
text = '~{}'.format(user)
331332
line = 'shell fake {}'.format(text)
@@ -336,7 +337,7 @@ def test_path_completion_expand_user_dir(cmd2_app):
336337
expected = text + os.path.sep
337338
assert expected in completions
338339

339-
def test_path_completion_user_expansion(cmd2_app):
340+
def test_path_completion_user_path_expansion(cmd2_app):
340341
# Run path with a tilde and a slash
341342
if sys.platform.startswith('win'):
342343
cmd = 'dir'

0 commit comments

Comments
 (0)