summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Van Brunt <kmvanbrunt@gmail.com>2018-09-28 13:53:27 -0400
committerKevin Van Brunt <kmvanbrunt@gmail.com>2018-09-28 13:53:27 -0400
commit4b4b3ef1819dde1c6d1ea7a70bf67e805d23470d (patch)
tree1f39e0d9f1dd4327a3d54fc381901ba7b83ac6cd
parent35e99b267c61ba38023fadfa6687785357c74aaf (diff)
parented33f32b5924be3866c147bf8eaf088e65cd101d (diff)
downloadcmd2-git-4b4b3ef1819dde1c6d1ea7a70bf67e805d23470d.tar.gz
Merge branch 'macro' into argparse_conversion
-rw-r--r--CHANGELOG.md6
-rw-r--r--cmd2/cmd2.py61
-rw-r--r--cmd2/parsing.py9
-rw-r--r--cmd2/utils.py2
-rwxr-xr-xexamples/arg_print.py2
-rw-r--r--tests/conftest.py5
-rw-r--r--tests/test_completion.py8
7 files changed, 44 insertions, 49 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0f4283cd..bad61734 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,12 +25,14 @@
* ``alias`` is now an argparse command with subcommands to create, list, and delete aliases
* Deprecations
* Deprecated the built-in ``cmd2`` support for colors including ``Cmd.colorize()`` and ``Cmd._colorcodes``
- * `unalias` is no longer a command since ``alias delete`` replaced it
-* Deletions
+* Deletions (potentially breaking changes)
* The ``preparse``, ``postparsing_precmd``, and ``postparsing_postcmd`` methods *deprecated* in the previous release
have been deleted
* The new application lifecycle hook system allows for registration of callbacks to be called at various points
in the lifecycle and is more powerful and flexible than the previous system
+ * ``alias`` is now a command with subcommands to create, list, and delete aliases. Therefore its syntax
+ has changed. All current alias commands in startup scripts or transcripts will break with this release.
+ * `unalias` was deleted since ``alias delete`` replaced it
## 0.9.4 (August 21, 2018)
* Bug Fixes
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index 2256558a..a2c2a46f 100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -1486,10 +1486,10 @@ class Cmd(cmd.Cmd):
if compfunc is None:
# There's no completer function, next see if the command uses argparser
- cmd_func = self._cmd_func(command)
- if cmd_func and hasattr(cmd_func, 'argparser'):
+ func = self.cmd_func(command)
+ if func and hasattr(func, 'argparser'):
compfunc = functools.partial(self._autocomplete_default,
- argparser=getattr(cmd_func, 'argparser'))
+ argparser=getattr(func, 'argparser'))
else:
compfunc = self.completedefault
@@ -1970,17 +1970,16 @@ class Cmd(cmd.Cmd):
self.redirecting = False
- def _cmd_func(self, command: str) -> Optional[Callable]:
+ def cmd_func(self, command: str) -> Optional[Callable]:
"""
Get the function for a command
:param command: the name of the command
"""
- func_name = self._cmd_func_name(command)
+ func_name = self.cmd_func_name(command)
if func_name:
return getattr(self, func_name)
- return None
- def _cmd_func_name(self, command: str) -> str:
+ def cmd_func_name(self, command: str) -> str:
"""Get the method name associated with a given command.
:param command: command to look up method name which implements it
@@ -2006,9 +2005,9 @@ class Cmd(cmd.Cmd):
if statement.command in self.macros:
stop = self._run_macro(statement)
else:
- cmd_func = self._cmd_func(statement.command)
- if cmd_func:
- stop = cmd_func(statement)
+ func = self.cmd_func(statement.command)
+ if func:
+ stop = func(statement)
# Since we have a valid command store it in the history
if statement.command not in self.exclude_from_history:
@@ -2588,10 +2587,10 @@ class Cmd(cmd.Cmd):
matches = []
# Check if this is a command with an argparse function
- cmd_func = self._cmd_func(command)
- if cmd_func and hasattr(cmd_func, 'argparser'):
- completer = AutoCompleter(getattr(cmd_func, 'argparser'), cmd2_app=self)
- matches = completer.complete_command_help(tokens[cmd_index:], text, line, begidx, endidx)
+ func = self.cmd_func(command)
+ if func and hasattr(func, 'argparser'):
+ completer = AutoCompleter(getattr(func, 'argparser'), cmd2_app=self)
+ matches = completer.complete_command_help(tokens[cmd_index:], text, line, begidx, endidx)
return matches
@@ -2611,21 +2610,15 @@ class Cmd(cmd.Cmd):
if not args.command or args.verbose:
self._help_menu(args.verbose)
- elif args.command:
+ else:
# Getting help for a specific command
- cmd_func = self._cmd_func(args.command)
- if cmd_func:
- # Check to see if this function was decorated with an argparse ArgumentParser
- if hasattr(cmd_func, 'argparser'):
- completer = AutoCompleter(getattr(cmd_func, 'argparser'), cmd2_app=self)
- tokens = [args.command]
- tokens.extend(args.subcommand)
- self.poutput(completer.format_help(tokens))
- else:
- # No special behavior needed, delegate to cmd base class do_help()
- super().do_help(args.command)
+ func = self.cmd_func(args.command)
+ if func and hasattr(func, 'argparser'):
+ completer = AutoCompleter(getattr(func, 'argparser'), cmd2_app=self)
+ tokens = [args.command] + args.subcommand
+ self.poutput(completer.format_help(tokens))
else:
- # This could be a help topic
+ # No special behavior needed, delegate to cmd base class do_help()
super().do_help(args.command)
def _help_menu(self, verbose: bool=False) -> None:
@@ -2642,12 +2635,12 @@ class Cmd(cmd.Cmd):
cmds_cats = {}
for command in visible_commands:
- cmd_func = self._cmd_func(command)
- if command in help_topics or cmd_func.__doc__:
+ func = self.cmd_func(command)
+ if command in help_topics or func.__doc__:
if command in help_topics:
help_topics.remove(command)
- if hasattr(cmd_func, HELP_CATEGORY):
- category = getattr(cmd_func, HELP_CATEGORY)
+ if hasattr(func, HELP_CATEGORY):
+ category = getattr(func, HELP_CATEGORY)
cmds_cats.setdefault(category, [])
cmds_cats[category].append(command)
else:
@@ -2700,13 +2693,13 @@ class Cmd(cmd.Cmd):
func = getattr(self, 'help_' + command)
except AttributeError:
# Couldn't find a help function
- cmd_func = self._cmd_func(command)
+ func = self.cmd_func(command)
try:
# Now see if help_summary has been set
- doc = cmd_func.help_summary
+ doc = func.help_summary
except AttributeError:
# Last, try to directly access the function's doc-string
- doc = cmd_func.__doc__
+ doc = func.__doc__
else:
# we found the help function
result = io.StringIO()
diff --git a/cmd2/parsing.py b/cmd2/parsing.py
index 27d17d21..e90eac43 100644
--- a/cmd2/parsing.py
+++ b/cmd2/parsing.py
@@ -34,12 +34,12 @@ class MacroArg:
# Pattern used to find normal argument
# Digits surrounded by exactly 1 brace on a side and 1 or more braces on the opposite side
# Match strings like: {5}, {{{{{4}, {2}}}}}
- macro_normal_arg_pattern = re.compile(r'(?<!\{)\{\d+\}|\{\d+\}(?!\})')
+ macro_normal_arg_pattern = re.compile(r'(?<!{){\d+}|{\d+}(?!})')
# Pattern used to find escaped arguments
# Digits surrounded by 2 or more braces on both sides
# Match strings like: {{5}}, {{{{{4}}, {{2}}}}}
- macro_escaped_arg_pattern = re.compile(r'\{{2}\d+\}{2}')
+ macro_escaped_arg_pattern = re.compile(r'{{2}\d+}{2}')
# Finds a string of digits
digit_pattern = re.compile(r'\d+')
@@ -315,10 +315,11 @@ class StatementParser:
if not word:
return False, 'cannot be an empty string'
- for (shortcut, expansion) in self.shortcuts:
+ for (shortcut, _) in self.shortcuts:
if word.startswith(shortcut):
+ # Build an error string with all shortcuts listed
errmsg = 'cannot start with a shortcut: '
- errmsg += ', '.join(shortcut for (shortcut, expansion) in self.shortcuts)
+ errmsg += ', '.join(shortcut for (shortcut, _) in self.shortcuts)
return False, errmsg
errmsg = 'cannot contain: whitespace, quotes, '
diff --git a/cmd2/utils.py b/cmd2/utils.py
index 7f7a9b48..ddd43507 100644
--- a/cmd2/utils.py
+++ b/cmd2/utils.py
@@ -229,7 +229,7 @@ def natural_keys(input_str: str) -> List[Union[int, str]]:
:param input_str: string to convert
:return: list of strings and integers
"""
- return [try_int_or_force_to_lower_case(substr) for substr in re.split('(\d+)', input_str)]
+ return [try_int_or_force_to_lower_case(substr) for substr in re.split(r'(\d+)', input_str)]
def natural_sort(list_to_sort: Iterable[str]) -> List[str]:
diff --git a/examples/arg_print.py b/examples/arg_print.py
index 6b7d030e..f2168126 100755
--- a/examples/arg_print.py
+++ b/examples/arg_print.py
@@ -2,7 +2,7 @@
# coding=utf-8
"""A simple example demonstrating the following:
1) How arguments and options get parsed and passed to commands
- 2) How to change what syntax get parsed as a comment and stripped from the arguments
+ 2) How to change what syntax gets parsed as a comment and stripped from the arguments
This is intended to serve as a live demonstration so that developers can
experiment with and understand how command and argument parsing work.
diff --git a/tests/conftest.py b/tests/conftest.py
index 93348098..da7e8b08 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -160,9 +160,8 @@ def complete_tester(text: str, line: str, begidx: int, endidx: int, app) -> Opti
def get_endidx():
return endidx
+ # Run the readline tab-completion function with readline mocks in place
with mock.patch.object(readline, 'get_line_buffer', get_line):
with mock.patch.object(readline, 'get_begidx', get_begidx):
with mock.patch.object(readline, 'get_endidx', get_endidx):
- # Run the readline tab-completion function with readline mocks in place
- first_match = app.complete(text, 0)
- return first_match
+ return app.complete(text, 0)
diff --git a/tests/test_completion.py b/tests/test_completion.py
index 8bf0cc02..b1ca80b8 100644
--- a/tests/test_completion.py
+++ b/tests/test_completion.py
@@ -101,8 +101,7 @@ def test_complete_empty_arg(cmd2_app):
expected = sorted(cmd2_app.get_visible_commands())
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
- assert first_match is not None and \
- cmd2_app.completion_matches == expected
+ assert first_match is not None and cmd2_app.completion_matches == expected
def test_complete_bogus_command(cmd2_app):
text = ''
@@ -121,14 +120,15 @@ def test_complete_macro(base_app, request):
# Macros do path completion
test_dir = os.path.dirname(request.module.__file__)
- text = os.path.join(test_dir, 'script.py')
+ text = os.path.join(test_dir, 's')
line = 'fake {}'.format(text)
endidx = len(line)
begidx = endidx - len(text)
+ expected = [text + 'cript.py', text + 'cript.txt', text + 'cripts' + os.path.sep]
first_match = complete_tester(text, line, begidx, endidx, base_app)
- assert first_match == text + ' '
+ assert first_match is not None and base_app.completion_matches
def test_cmd2_command_completion_multiple(cmd2_app):