summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Van Brunt <kmvanbrunt@gmail.com>2019-07-07 21:45:52 -0400
committerKevin Van Brunt <kmvanbrunt@gmail.com>2019-07-07 21:45:52 -0400
commitbb2dd69bd04f5dccff9474c018eb6b6eea74c6af (patch)
tree543f626a84ac96d97c478e4e1af185ce07831908
parentf8db8b766540920de6c85a26a6740170455b9354 (diff)
downloadcmd2-git-bb2dd69bd04f5dccff9474c018eb6b6eea74c6af.tar.gz
Moved all custom argparse classes intended for normal development to argparse_custom.py.
Lazy loading AutoCompleter in cmd2 instance methods to allow argparse_completer.py to import cmd2.Cmd class. This Architecture makes more sense because AutoCompleter depends on cmd2.Cmd.
-rw-r--r--cmd2/__init__.py1
-rw-r--r--cmd2/argparse_completer.py55
-rw-r--r--cmd2/argparse_custom.py56
-rw-r--r--cmd2/cmd2.py6
-rwxr-xr-xexamples/tab_autocompletion.py5
-rw-r--r--tests/test_argparse_completer.py9
6 files changed, 70 insertions, 62 deletions
diff --git a/cmd2/__init__.py b/cmd2/__init__.py
index d3c92636..3b149601 100644
--- a/cmd2/__init__.py
+++ b/cmd2/__init__.py
@@ -11,6 +11,7 @@ except DistributionNotFound:
pass
from .ansi import style
+from .argparse_custom import Cmd2ArgParser, CompletionItem
from .cmd2 import Cmd, Statement, EmptyStatement, categorize
from .cmd2 import with_argument_list, with_argparser, with_argparser_and_unknown_args, with_category
from .constants import DEFAULT_SHORTCUTS
diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py
index 1a8dd473..1a156f8f 100644
--- a/cmd2/argparse_completer.py
+++ b/cmd2/argparse_completer.py
@@ -62,66 +62,17 @@ import argparse
import shutil
from typing import List, Union
+from . import cmd2
from . import utils
from .ansi import ansi_safe_wcswidth
from .argparse_custom import ATTR_SUPPRESS_TAB_HINT, ATTR_DESCRIPTIVE_COMPLETION_HEADER, ATTR_NARGS_RANGE
-from .argparse_custom import ChoicesCallable, ATTR_CHOICES_CALLABLE
+from .argparse_custom import ChoicesCallable, CompletionItem, ATTR_CHOICES_CALLABLE
from .rl_utils import rl_force_redisplay
# If no descriptive header is supplied, then this will be used instead
DEFAULT_DESCRIPTIVE_HEADER = 'Description'
-class CompletionItem(str):
- """
- Completion item with descriptive text attached
-
- Returning this instead of a regular string for completion results will signal the
- autocompleter to output the completions results in a table of completion tokens
- with descriptions instead of just a table of tokens.
-
- For example, you'd see this:
- TOKEN Description
- MY_TOKEN Info about my token
- SOME_TOKEN Info about some token
- YET_ANOTHER Yet more info
-
- Instead of this:
- TOKEN_ID SOME_TOKEN YET_ANOTHER
-
- This is especially useful if you want to complete ID numbers in a more
- user-friendly manner. For example, you can provide this:
-
- ITEM_ID Item Name
- 1 My item
- 2 Another item
- 3 Yet another item
-
- Instead of this:
- 1 2 3
-
- Example:
- token = 1
- token_description = "My Item"
- completion_item = CompletionItem(token, token_description)
- """
- def __new__(cls, value: object, *args, **kwargs) -> str:
- return super().__new__(cls, value)
-
- # noinspection PyUnusedLocal
- def __init__(self, value: object, desc: str = '', *args, **kwargs) -> None:
- """
- CompletionItem Initializer
-
- :param value: the value being tab completed
- :param desc: description text to display
- :param args: args for str __init__
- :param kwargs: kwargs for str __init__
- """
- super().__init__(*args, **kwargs)
- self.description = desc
-
-
# noinspection PyProtectedMember
def is_potential_flag(token: str, parser: argparse.ArgumentParser) -> bool:
"""Determine if a token looks like a potential flag. Based on argparse._parse_optional()."""
@@ -171,7 +122,7 @@ class AutoCompleter(object):
self.needed = False
self.variable = False
- def __init__(self, parser: argparse.ArgumentParser, cmd2_app, *,
+ def __init__(self, parser: argparse.ArgumentParser, cmd2_app: cmd2.Cmd, *,
token_start_index: int = 1) -> None:
"""
Create an AutoCompleter
diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py
index b05ca6ed..5e3ed7f5 100644
--- a/cmd2/argparse_custom.py
+++ b/cmd2/argparse_custom.py
@@ -26,6 +26,56 @@ ATTR_SUPPRESS_TAB_HINT = 'suppress_tab_hint'
ATTR_DESCRIPTIVE_COMPLETION_HEADER = 'desc_completion_header'
+class CompletionItem(str):
+ """
+ Completion item with descriptive text attached
+
+ Returning this instead of a regular string for completion results will signal the
+ autocompleter to output the completions results in a table of completion tokens
+ with descriptions instead of just a table of tokens.
+
+ For example, you'd see this:
+ TOKEN Description
+ MY_TOKEN Info about my token
+ SOME_TOKEN Info about some token
+ YET_ANOTHER Yet more info
+
+ Instead of this:
+ TOKEN_ID SOME_TOKEN YET_ANOTHER
+
+ This is especially useful if you want to complete ID numbers in a more
+ user-friendly manner. For example, you can provide this:
+
+ ITEM_ID Item Name
+ 1 My item
+ 2 Another item
+ 3 Yet another item
+
+ Instead of this:
+ 1 2 3
+
+ Example:
+ token = 1
+ token_description = "My Item"
+ completion_item = CompletionItem(token, token_description)
+ """
+ def __new__(cls, value: object, *args, **kwargs) -> str:
+ return super().__new__(cls, value)
+
+ # noinspection PyUnusedLocal
+ def __init__(self, value: object, desc: str = '', *args, **kwargs) -> None:
+ """
+ CompletionItem Initializer
+
+ :param value: the value being tab completed
+ :param desc: description text to display
+ :param args: args for str __init__
+ :param kwargs: kwargs for str __init__
+ """
+ super().__init__(*args, **kwargs)
+ self.description = desc
+
+
class ChoicesCallable:
"""
Enables using a callable as the choices provider for an argparse argument.
@@ -77,8 +127,10 @@ def _add_argument_wrapper(self, *args,
:param choices_method: cmd2-app method that provides choices for this argument
:param completer_function: tab-completion function that provides choices for this argument
:param completer_method: cmd2-app tab-completion method that provides choices for this argument
- :param suppress_tab_hint: when AutoCompleter has no choices to show during tab completion, it displays the current
- argument's help text as a hint. Set this to True to suppress the hint. Defaults to False.
+ :param suppress_tab_hint: when AutoCompleter has no results to show during tab completion, it displays the current
+ argument's help text as a hint. Set this to True to suppress the hint. If this argument's
+ help text is set to argparse.SUPPRESS, then tab hints will not display regardless of the
+ value passed for suppress_tab_hint. Defaults to False.
:param descriptive_header: if the provided choices are CompletionItems, then this header will display
during tab completion. Defaults to None.
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index bfda6ae2..0d14d5ad 100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -46,8 +46,7 @@ from . import ansi
from . import constants
from . import plugin
from . import utils
-from .argparse_completer import AutoCompleter, CompletionItem
-from .argparse_custom import Cmd2ArgParser
+from .argparse_custom import Cmd2ArgParser, CompletionItem
from .clipboard import can_clip, get_paste_buffer, write_to_paste_buffer
from .history import History, HistoryItem
from .parsing import StatementParser, Statement, Macro, MacroArg, shlex_split
@@ -1582,6 +1581,7 @@ class Cmd(cmd.Cmd):
def _autocomplete_default(self, text: str, line: str, begidx: int, endidx: int,
argparser: argparse.ArgumentParser) -> List[str]:
"""Default completion function for argparse commands"""
+ from .argparse_completer import AutoCompleter
completer = AutoCompleter(argparser, self)
tokens, _ = self.tokens_for_completion(line, begidx, endidx)
return completer.complete_command(tokens, text, line, begidx, endidx)
@@ -2643,6 +2643,7 @@ class Cmd(cmd.Cmd):
# Check if this is a command with an argparse function
func = self.cmd_func(command)
if func and hasattr(func, 'argparser'):
+ from .argparse_completer import AutoCompleter
completer = AutoCompleter(getattr(func, 'argparser'), self)
matches = completer.complete_command_help(tokens[cmd_index:], text, line, begidx, endidx)
@@ -2673,6 +2674,7 @@ class Cmd(cmd.Cmd):
# If the command function uses argparse, then use argparse's help
if func and hasattr(func, 'argparser'):
+ from .argparse_completer import AutoCompleter
completer = AutoCompleter(getattr(func, 'argparser'), self)
tokens = [args.command] + args.subcommand
self.poutput(completer.format_help(tokens))
diff --git a/examples/tab_autocompletion.py b/examples/tab_autocompletion.py
index 4b13b5c3..a6d5487d 100755
--- a/examples/tab_autocompletion.py
+++ b/examples/tab_autocompletion.py
@@ -8,8 +8,7 @@ import functools
from typing import List
import cmd2
-from cmd2 import argparse_completer, utils
-from cmd2.argparse_custom import Cmd2ArgParser
+from cmd2 import utils, Cmd2ArgParser, CompletionItem
actors = ['Mark Hamill', 'Harrison Ford', 'Carrie Fisher', 'Alec Guinness', 'Peter Mayhew',
'Anthony Daniels', 'Adam Driver', 'Daisy Ridley', 'John Boyega', 'Oscar Isaac',
@@ -116,7 +115,7 @@ class TabCompleteExample(cmd2.Cmd):
for movie_id in utils.natural_sort(self.MOVIE_DATABASE_IDS):
if movie_id in self.MOVIE_DATABASE:
movie_entry = self.MOVIE_DATABASE[movie_id]
- completions_with_desc.append(argparse_completer.CompletionItem(movie_id, movie_entry['title']))
+ completions_with_desc.append(CompletionItem(movie_id, movie_entry['title']))
# Mark that we already sorted the matches
self.matches_sorted = True
diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py
index a3fa6a59..6e092619 100644
--- a/tests/test_argparse_completer.py
+++ b/tests/test_argparse_completer.py
@@ -9,9 +9,8 @@ from typing import List
import pytest
import cmd2
-from cmd2 import with_argparser
-from cmd2.argparse_completer import CompletionItem, is_potential_flag, DEFAULT_DESCRIPTIVE_HEADER
-from cmd2.argparse_custom import Cmd2ArgParser
+from cmd2 import with_argparser, Cmd2ArgParser, CompletionItem
+from cmd2.argparse_completer import is_potential_flag, DEFAULT_DESCRIPTIVE_HEADER
from cmd2.utils import StdSim, basic_complete
from .conftest import run_cmd, complete_tester
@@ -208,6 +207,7 @@ def test_autocomp_positional_choices_completion(ac_app, pos, text, completions):
first_match = complete_tester(text, line, begidx, endidx, ac_app)
assert first_match is not None and ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key)
+
@pytest.mark.parametrize('flag, text, completions', [
('-f', '', completions_from_function),
('--function', 'f', ['function', 'fairly']),
@@ -222,6 +222,7 @@ def test_autocomp_flag_completers(ac_app, flag, text, completions):
first_match = complete_tester(text, line, begidx, endidx, ac_app)
assert first_match is not None and ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key)
+
@pytest.mark.parametrize('pos, text, completions', [
(1, '', completions_from_function),
(1, 'c', ['completions', 'complete']),
@@ -338,6 +339,7 @@ Hint:
'''
+
def test_autocomp_hint_no_help(ac_app, capsys):
text = ''
line = 'hint foo {}'.format(text)
@@ -503,6 +505,7 @@ Hint:
# # Since -- appeared before the -- being completed, nothing should be completed
# assert complete_tester(text, line, begidx, endidx, cmd2_app) is None
+
def test_is_potential_flag():
parser = Cmd2ArgParser()