summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd Leonhardt <todd.leonhardt@gmail.com>2018-10-26 13:10:20 -0400
committerGitHub <noreply@github.com>2018-10-26 13:10:20 -0400
commitbb6cc1d618745ffe43b1bfabf93efaf592b2c3ad (patch)
treef2a70b762442baaaa956cb1338a5c74d9bc0aa9a
parent759468b337d7ad6304c9f26ce0dc481d20588c8f (diff)
parentbb9e5e5e3d64bfaf2ee67f056e5d0df405a2b8f3 (diff)
downloadcmd2-git-bb6cc1d618745ffe43b1bfabf93efaf592b2c3ad.tar.gz
Merge branch 'master' into document_completion
-rw-r--r--CHANGELOG.md2
-rw-r--r--cmd2/cmd2.py22
-rw-r--r--cmd2/transcript.py2
-rw-r--r--docs/freefeatures.rst2
-rwxr-xr-xexamples/python_scripting.py2
-rw-r--r--tests/test_completion.py44
6 files changed, 56 insertions, 18 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9f2a23db..278df4bf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,8 @@
* **cmdloop** now only attempts to register a custom signal handler for SIGINT if running in the main thread
* Deletions (potentially breaking changes)
* Deleted ``Cmd.colorize()`` and ``Cmd._colorcodes`` which were deprecated in 0.9.5
+ * Replaced ``dir_exe_only`` and ``dir_only`` flags in ``path_complete`` with optional ``path_filter`` function
+ that is used to filter paths out of completion results.
## 0.9.6 (October 13, 2018)
* Bug Fixes
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index b9e6aa99..f8dd63d3 100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -1007,16 +1007,17 @@ class Cmd(cmd.Cmd):
return matches
# noinspection PyUnusedLocal
- def path_complete(self, text: str, line: str, begidx: int, endidx: int, dir_exe_only: bool=False,
- dir_only: bool=False) -> List[str]:
+ def path_complete(self, text: str, line: str, begidx: int, endidx: int,
+ path_filter: Optional[Callable[[str], bool]] = None) -> List[str]:
"""Performs completion of local file system paths
:param text: the string prefix we are attempting to match (all returned matches must begin with it)
:param line: the current input line with leading whitespace removed
:param begidx: the beginning index of the prefix text
:param endidx: the ending index of the prefix text
- :param dir_exe_only: only return directories and executables, not non-executable files
- :param dir_only: only return directories
+ :param path_filter: optional filter function that determines if a path belongs in the results
+ this function takes a path as its argument and returns True if the path should
+ be kept in the results
:return: a list of possible tab completions
"""
@@ -1076,7 +1077,7 @@ class Cmd(cmd.Cmd):
search_str = os.path.join(os.getcwd(), '*')
cwd_added = True
else:
- # Purposely don't match any path containing wildcards - what we are doing is complicated enough!
+ # Purposely don't match any path containing wildcards
wildcards = ['*', '?']
for wildcard in wildcards:
if wildcard in text:
@@ -1112,11 +1113,9 @@ class Cmd(cmd.Cmd):
# Find all matching path completions
matches = glob.glob(search_str)
- # Filter based on type
- if dir_exe_only:
- matches = [c for c in matches if os.path.isdir(c) or os.access(c, os.X_OK)]
- elif dir_only:
- matches = [c for c in matches if os.path.isdir(c)]
+ # Filter out results that don't belong
+ if path_filter is not None:
+ matches = [c for c in matches if path_filter(c)]
# Don't append a space or closing quote to directory
if len(matches) == 1 and os.path.isdir(matches[0]):
@@ -1199,7 +1198,8 @@ class Cmd(cmd.Cmd):
# Otherwise look for executables in the given path
else:
- return self.path_complete(text, line, begidx, endidx, dir_exe_only=True)
+ return self.path_complete(text, line, begidx, endidx,
+ lambda path: os.path.isdir(path) or os.access(path, os.X_OK))
def _redirect_complete(self, text: str, line: str, begidx: int, endidx: int, compfunc: Callable) -> List[str]:
"""Called by complete() as the first tab completion function for all commands
diff --git a/cmd2/transcript.py b/cmd2/transcript.py
index baaa6caf..fe43188f 100644
--- a/cmd2/transcript.py
+++ b/cmd2/transcript.py
@@ -89,7 +89,7 @@ class Cmd2TestCase(unittest.TestCase):
if utils.strip_ansi(line).startswith(self.cmdapp.visible_prompt):
message = '\nFile {}, line {}\nCommand was:\n{}\nExpected: (nothing)\nGot:\n{}\n'.format(
fname, line_num, command, result)
- self.assert_(not (result.strip()), message)
+ self.assertTrue(not (result.strip()), message)
continue
expected = []
while not utils.strip_ansi(line).startswith(self.cmdapp.visible_prompt):
diff --git a/docs/freefeatures.rst b/docs/freefeatures.rst
index 0a95a829..7864f146 100644
--- a/docs/freefeatures.rst
+++ b/docs/freefeatures.rst
@@ -350,4 +350,4 @@ path completion of directories only for this command by adding a line of code si
which inherits from ``cmd2.Cmd``::
# Make sure you have an "import functools" somewhere at the top
- complete_bar = functools.partialmethod(cmd2.Cmd.path_complete, dir_only=True)
+ complete_bar = functools.partialmethod(cmd2.Cmd.path_complete, path_filter=os.path.isdir)
diff --git a/examples/python_scripting.py b/examples/python_scripting.py
index 0b0030a5..7847b8b6 100755
--- a/examples/python_scripting.py
+++ b/examples/python_scripting.py
@@ -88,7 +88,7 @@ class CmdLineApp(cmd2.Cmd):
# Enable tab completion for cd command
def complete_cd(self, text, line, begidx, endidx):
- return self.path_complete(text, line, begidx, endidx, dir_only=True)
+ return self.path_complete(text, line, begidx, endidx, path_filter=os.path.isdir)
dir_parser = argparse.ArgumentParser()
dir_parser.add_argument('-l', '--long', action='store_true', help="display in long format with one item per line")
diff --git a/tests/test_completion.py b/tests/test_completion.py
index 0df06423..dffb03ec 100644
--- a/tests/test_completion.py
+++ b/tests/test_completion.py
@@ -257,6 +257,21 @@ def test_shell_command_completion_does_path_completion_when_after_command(cmd2_a
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
assert first_match is not None and cmd2_app.completion_matches == [text + '.py ']
+def test_shell_commmand_complete_in_path(cmd2_app, request):
+ test_dir = os.path.dirname(request.module.__file__)
+
+ text = os.path.join(test_dir, 's')
+ line = 'shell {}'.format(text)
+
+ endidx = len(line)
+ begidx = endidx - len(text)
+
+ # Since this will look for directories and executables in the given path,
+ # we expect to see the scripts dir among the results
+ expected = os.path.join(test_dir, 'scripts' + os.path.sep)
+ first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
+ assert first_match is not None and expected in cmd2_app.completion_matches
+
def test_path_completion_single_end(cmd2_app, request):
test_dir = os.path.dirname(request.module.__file__)
@@ -316,8 +331,8 @@ def test_default_to_shell_completion(cmd2_app, request):
assert first_match is not None and cmd2_app.completion_matches == [text + '.py ']
-def test_path_completion_cwd(cmd2_app):
- # Run path complete with no search text
+def test_path_completion_no_text(cmd2_app):
+ # Run path complete with no search text which should show what's in cwd
text = ''
line = 'shell ls {}'.format(text)
endidx = len(line)
@@ -330,13 +345,34 @@ def test_path_completion_cwd(cmd2_app):
endidx = len(line)
begidx = endidx - len(text)
- # We have to strip off the text from the beginning since the matches are entire paths
+ # We have to strip off the path from the beginning since the matches are entire paths
completions_cwd = [match.replace(text, '', 1) for match in cmd2_app.path_complete(text, line, begidx, endidx)]
# Verify that the first test gave results for entries in the cwd
assert completions_no_text == completions_cwd
assert completions_cwd
+def test_path_completion_no_path(cmd2_app):
+ # Run path complete with search text that isn't preceded by a path. This should use CWD as the path.
+ text = 's'
+ line = 'shell ls {}'.format(text)
+ endidx = len(line)
+ begidx = endidx - len(text)
+ completions_no_text = cmd2_app.path_complete(text, line, begidx, endidx)
+
+ # Run path complete with path set to the CWD
+ text = os.getcwd() + os.path.sep + 's'
+ line = 'shell ls {}'.format(text)
+ endidx = len(line)
+ begidx = endidx - len(text)
+
+ # We have to strip off the path from the beginning since the matches are entire paths (Leave the 's')
+ completions_cwd = [match.replace(text[:-1], '', 1) for match in cmd2_app.path_complete(text, line, begidx, endidx)]
+
+ # Verify that the first test gave results for entries in the cwd
+ assert completions_no_text == completions_cwd
+ assert completions_cwd
+
def test_path_completion_doesnt_match_wildcards(cmd2_app, request):
test_dir = os.path.dirname(request.module.__file__)
@@ -399,7 +435,7 @@ def test_path_completion_directories_only(cmd2_app, request):
expected = [text + 'cripts' + os.path.sep]
- assert cmd2_app.path_complete(text, line, begidx, endidx, dir_only=True) == expected
+ assert cmd2_app.path_complete(text, line, begidx, endidx, os.path.isdir) == expected
def test_basic_completion_single(cmd2_app):
text = 'Pi'