Skip to content

Commit 4b4b3ef

Browse files
committed
Merge branch 'macro' into argparse_conversion
2 parents 35e99b2 + ed33f32 commit 4b4b3ef

File tree

7 files changed

+44
-49
lines changed

7 files changed

+44
-49
lines changed

CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@
2525
* ``alias`` is now an argparse command with subcommands to create, list, and delete aliases
2626
* Deprecations
2727
* Deprecated the built-in ``cmd2`` support for colors including ``Cmd.colorize()`` and ``Cmd._colorcodes``
28-
* `unalias` is no longer a command since ``alias delete`` replaced it
29-
* Deletions
28+
* Deletions (potentially breaking changes)
3029
* The ``preparse``, ``postparsing_precmd``, and ``postparsing_postcmd`` methods *deprecated* in the previous release
3130
have been deleted
3231
* The new application lifecycle hook system allows for registration of callbacks to be called at various points
3332
in the lifecycle and is more powerful and flexible than the previous system
33+
* ``alias`` is now a command with subcommands to create, list, and delete aliases. Therefore its syntax
34+
has changed. All current alias commands in startup scripts or transcripts will break with this release.
35+
* `unalias` was deleted since ``alias delete`` replaced it
3436

3537
## 0.9.4 (August 21, 2018)
3638
* Bug Fixes

cmd2/cmd2.py

Lines changed: 27 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1486,10 +1486,10 @@ def complete(self, text: str, state: int) -> Optional[str]:
14861486

14871487
if compfunc is None:
14881488
# There's no completer function, next see if the command uses argparser
1489-
cmd_func = self._cmd_func(command)
1490-
if cmd_func and hasattr(cmd_func, 'argparser'):
1489+
func = self.cmd_func(command)
1490+
if func and hasattr(func, 'argparser'):
14911491
compfunc = functools.partial(self._autocomplete_default,
1492-
argparser=getattr(cmd_func, 'argparser'))
1492+
argparser=getattr(func, 'argparser'))
14931493
else:
14941494
compfunc = self.completedefault
14951495

@@ -1970,17 +1970,16 @@ def _restore_output(self, statement: Statement) -> None:
19701970

19711971
self.redirecting = False
19721972

1973-
def _cmd_func(self, command: str) -> Optional[Callable]:
1973+
def cmd_func(self, command: str) -> Optional[Callable]:
19741974
"""
19751975
Get the function for a command
19761976
:param command: the name of the command
19771977
"""
1978-
func_name = self._cmd_func_name(command)
1978+
func_name = self.cmd_func_name(command)
19791979
if func_name:
19801980
return getattr(self, func_name)
1981-
return None
19821981

1983-
def _cmd_func_name(self, command: str) -> str:
1982+
def cmd_func_name(self, command: str) -> str:
19841983
"""Get the method name associated with a given command.
19851984
19861985
:param command: command to look up method name which implements it
@@ -2006,9 +2005,9 @@ def onecmd(self, statement: Union[Statement, str]) -> bool:
20062005
if statement.command in self.macros:
20072006
stop = self._run_macro(statement)
20082007
else:
2009-
cmd_func = self._cmd_func(statement.command)
2010-
if cmd_func:
2011-
stop = cmd_func(statement)
2008+
func = self.cmd_func(statement.command)
2009+
if func:
2010+
stop = func(statement)
20122011

20132012
# Since we have a valid command store it in the history
20142013
if statement.command not in self.exclude_from_history:
@@ -2588,10 +2587,10 @@ def complete_help_subcommand(self, text: str, line: str, begidx: int, endidx: in
25882587
matches = []
25892588

25902589
# Check if this is a command with an argparse function
2591-
cmd_func = self._cmd_func(command)
2592-
if cmd_func and hasattr(cmd_func, 'argparser'):
2593-
completer = AutoCompleter(getattr(cmd_func, 'argparser'), cmd2_app=self)
2594-
matches = completer.complete_command_help(tokens[cmd_index:], text, line, begidx, endidx)
2590+
func = self.cmd_func(command)
2591+
if func and hasattr(func, 'argparser'):
2592+
completer = AutoCompleter(getattr(func, 'argparser'), cmd2_app=self)
2593+
matches = completer.complete_command_help(tokens[cmd_index:], text, line, begidx, endidx)
25952594

25962595
return matches
25972596

@@ -2611,21 +2610,15 @@ def do_help(self, args: argparse.Namespace) -> None:
26112610
if not args.command or args.verbose:
26122611
self._help_menu(args.verbose)
26132612

2614-
elif args.command:
2613+
else:
26152614
# Getting help for a specific command
2616-
cmd_func = self._cmd_func(args.command)
2617-
if cmd_func:
2618-
# Check to see if this function was decorated with an argparse ArgumentParser
2619-
if hasattr(cmd_func, 'argparser'):
2620-
completer = AutoCompleter(getattr(cmd_func, 'argparser'), cmd2_app=self)
2621-
tokens = [args.command]
2622-
tokens.extend(args.subcommand)
2623-
self.poutput(completer.format_help(tokens))
2624-
else:
2625-
# No special behavior needed, delegate to cmd base class do_help()
2626-
super().do_help(args.command)
2615+
func = self.cmd_func(args.command)
2616+
if func and hasattr(func, 'argparser'):
2617+
completer = AutoCompleter(getattr(func, 'argparser'), cmd2_app=self)
2618+
tokens = [args.command] + args.subcommand
2619+
self.poutput(completer.format_help(tokens))
26272620
else:
2628-
# This could be a help topic
2621+
# No special behavior needed, delegate to cmd base class do_help()
26292622
super().do_help(args.command)
26302623

26312624
def _help_menu(self, verbose: bool=False) -> None:
@@ -2642,12 +2635,12 @@ def _help_menu(self, verbose: bool=False) -> None:
26422635
cmds_cats = {}
26432636

26442637
for command in visible_commands:
2645-
cmd_func = self._cmd_func(command)
2646-
if command in help_topics or cmd_func.__doc__:
2638+
func = self.cmd_func(command)
2639+
if command in help_topics or func.__doc__:
26472640
if command in help_topics:
26482641
help_topics.remove(command)
2649-
if hasattr(cmd_func, HELP_CATEGORY):
2650-
category = getattr(cmd_func, HELP_CATEGORY)
2642+
if hasattr(func, HELP_CATEGORY):
2643+
category = getattr(func, HELP_CATEGORY)
26512644
cmds_cats.setdefault(category, [])
26522645
cmds_cats[category].append(command)
26532646
else:
@@ -2700,13 +2693,13 @@ def _print_topics(self, header: str, cmds: List[str], verbose: bool) -> None:
27002693
func = getattr(self, 'help_' + command)
27012694
except AttributeError:
27022695
# Couldn't find a help function
2703-
cmd_func = self._cmd_func(command)
2696+
func = self.cmd_func(command)
27042697
try:
27052698
# Now see if help_summary has been set
2706-
doc = cmd_func.help_summary
2699+
doc = func.help_summary
27072700
except AttributeError:
27082701
# Last, try to directly access the function's doc-string
2709-
doc = cmd_func.__doc__
2702+
doc = func.__doc__
27102703
else:
27112704
# we found the help function
27122705
result = io.StringIO()

cmd2/parsing.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ class MacroArg:
3434
# Pattern used to find normal argument
3535
# Digits surrounded by exactly 1 brace on a side and 1 or more braces on the opposite side
3636
# Match strings like: {5}, {{{{{4}, {2}}}}}
37-
macro_normal_arg_pattern = re.compile(r'(?<!\{)\{\d+\}|\{\d+\}(?!\})')
37+
macro_normal_arg_pattern = re.compile(r'(?<!{){\d+}|{\d+}(?!})')
3838

3939
# Pattern used to find escaped arguments
4040
# Digits surrounded by 2 or more braces on both sides
4141
# Match strings like: {{5}}, {{{{{4}}, {{2}}}}}
42-
macro_escaped_arg_pattern = re.compile(r'\{{2}\d+\}{2}')
42+
macro_escaped_arg_pattern = re.compile(r'{{2}\d+}{2}')
4343

4444
# Finds a string of digits
4545
digit_pattern = re.compile(r'\d+')
@@ -315,10 +315,11 @@ def is_valid_command(self, word: str) -> Tuple[bool, str]:
315315
if not word:
316316
return False, 'cannot be an empty string'
317317

318-
for (shortcut, expansion) in self.shortcuts:
318+
for (shortcut, _) in self.shortcuts:
319319
if word.startswith(shortcut):
320+
# Build an error string with all shortcuts listed
320321
errmsg = 'cannot start with a shortcut: '
321-
errmsg += ', '.join(shortcut for (shortcut, expansion) in self.shortcuts)
322+
errmsg += ', '.join(shortcut for (shortcut, _) in self.shortcuts)
322323
return False, errmsg
323324

324325
errmsg = 'cannot contain: whitespace, quotes, '

cmd2/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ def natural_keys(input_str: str) -> List[Union[int, str]]:
229229
:param input_str: string to convert
230230
:return: list of strings and integers
231231
"""
232-
return [try_int_or_force_to_lower_case(substr) for substr in re.split('(\d+)', input_str)]
232+
return [try_int_or_force_to_lower_case(substr) for substr in re.split(r'(\d+)', input_str)]
233233

234234

235235
def natural_sort(list_to_sort: Iterable[str]) -> List[str]:

examples/arg_print.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# coding=utf-8
33
"""A simple example demonstrating the following:
44
1) How arguments and options get parsed and passed to commands
5-
2) How to change what syntax get parsed as a comment and stripped from the arguments
5+
2) How to change what syntax gets parsed as a comment and stripped from the arguments
66
77
This is intended to serve as a live demonstration so that developers can
88
experiment with and understand how command and argument parsing work.

tests/conftest.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,8 @@ def get_begidx():
160160
def get_endidx():
161161
return endidx
162162

163+
# Run the readline tab-completion function with readline mocks in place
163164
with mock.patch.object(readline, 'get_line_buffer', get_line):
164165
with mock.patch.object(readline, 'get_begidx', get_begidx):
165166
with mock.patch.object(readline, 'get_endidx', get_endidx):
166-
# Run the readline tab-completion function with readline mocks in place
167-
first_match = app.complete(text, 0)
168-
return first_match
167+
return app.complete(text, 0)

tests/test_completion.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,7 @@ def test_complete_empty_arg(cmd2_app):
101101
expected = sorted(cmd2_app.get_visible_commands())
102102
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
103103

104-
assert first_match is not None and \
105-
cmd2_app.completion_matches == expected
104+
assert first_match is not None and cmd2_app.completion_matches == expected
106105

107106
def test_complete_bogus_command(cmd2_app):
108107
text = ''
@@ -121,14 +120,15 @@ def test_complete_macro(base_app, request):
121120
# Macros do path completion
122121
test_dir = os.path.dirname(request.module.__file__)
123122

124-
text = os.path.join(test_dir, 'script.py')
123+
text = os.path.join(test_dir, 's')
125124
line = 'fake {}'.format(text)
126125

127126
endidx = len(line)
128127
begidx = endidx - len(text)
129128

129+
expected = [text + 'cript.py', text + 'cript.txt', text + 'cripts' + os.path.sep]
130130
first_match = complete_tester(text, line, begidx, endidx, base_app)
131-
assert first_match == text + ' '
131+
assert first_match is not None and base_app.completion_matches
132132

133133

134134
def test_cmd2_command_completion_multiple(cmd2_app):

0 commit comments

Comments
 (0)