Skip to content

Commit 3dcff64

Browse files
authored
Merge pull request #576 from python-cmd2/extra_args
You can now call a macro with extra arguments
2 parents b216987 + 53b976a commit 3dcff64

File tree

4 files changed

+38
-18
lines changed

4 files changed

+38
-18
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## 0.9.6 (TBD)
22
* Enhancements
33
* All platforms now depend on [wcwidth](https://pypi.python.org/pypi/wcwidth) to assist with asynchronous alerts.
4+
* Macros now accept extra arguments when called. These will be tacked onto the resolved command.
45

56
## 0.9.5 (October 11, 2018)
67
* Bug Fixes

cmd2/cmd2.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2033,18 +2033,22 @@ def _run_macro(self, statement: Statement) -> bool:
20332033
:param statement: the parsed statement from the command line
20342034
:return: a flag indicating whether the interpretation of commands should stop
20352035
"""
2036+
from itertools import islice
2037+
20362038
if statement.command not in self.macros.keys():
20372039
raise KeyError('{} is not a macro'.format(statement.command))
20382040

20392041
macro = self.macros[statement.command]
20402042

2041-
# For macros, every argument must be provided and there can be no extra arguments.
2042-
if len(statement.arg_list) != macro.required_arg_count:
2043-
self.perror("The macro '{}' expects {} argument(s)".format(statement.command, macro.required_arg_count),
2043+
# Make sure enough arguments were passed in
2044+
if len(statement.arg_list) < macro.minimum_arg_count:
2045+
self.perror("The macro '{}' expects at least {} argument(s)".format(statement.command,
2046+
macro.minimum_arg_count),
20442047
traceback_war=False)
20452048
return False
20462049

2047-
# Resolve the arguments in reverse
2050+
# Resolve the arguments in reverse and read their values from statement.argv since those
2051+
# are unquoted. Macro args should have been quoted when the macro was created.
20482052
resolved = macro.value
20492053
reverse_arg_list = sorted(macro.arg_list, key=lambda ma: ma.start_index, reverse=True)
20502054

@@ -2059,6 +2063,10 @@ def _run_macro(self, statement: Statement) -> bool:
20592063
parts = resolved.rsplit(to_replace, maxsplit=1)
20602064
resolved = parts[0] + replacement + parts[1]
20612065

2066+
# Append extra arguments and use statement.arg_list since these arguments need their quotes preserved
2067+
for arg in islice(statement.arg_list, macro.minimum_arg_count, None):
2068+
resolved += ' ' + arg
2069+
20622070
# Run the resolved command
20632071
return self.onecmd_plus_hooks(resolved)
20642072

@@ -2407,7 +2415,7 @@ def macro_create(self, args: argparse.Namespace):
24072415

24082416
# Set the macro
24092417
result = "overwritten" if args.name in self.macros else "created"
2410-
self.macros[args.name] = Macro(name=args.name, value=value, required_arg_count=max_arg_num, arg_list=arg_list)
2418+
self.macros[args.name] = Macro(name=args.name, value=value, minimum_arg_count=max_arg_num, arg_list=arg_list)
24112419
self.poutput("Macro '{}' {}".format(args.name, result))
24122420

24132421
def macro_delete(self, args: argparse.Namespace):
@@ -2469,6 +2477,8 @@ def macro_list(self, args: argparse.Namespace):
24692477
"Notes:\n"
24702478
" To use the literal string {1} in your command, escape it this way: {{1}}.\n"
24712479
"\n"
2480+
" Extra arguments passed when calling a macro are tacked onto resolved command.\n"
2481+
"\n"
24722482
" An argument number can be repeated in a macro. In the following example the\n"
24732483
" first argument will populate both {1} instances.\n"
24742484
"\n"

cmd2/parsing.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ class Macro:
5555
# The string the macro resolves to
5656
value = attr.ib(validator=attr.validators.instance_of(str))
5757

58-
# The required number of args the user has to pass to this macro
59-
required_arg_count = attr.ib(validator=attr.validators.instance_of(int))
58+
# The minimum number of args the user has to pass to this macro
59+
minimum_arg_count = attr.ib(validator=attr.validators.instance_of(int))
6060

6161
# Used to fill in argument placeholders in the macro
6262
arg_list = attr.ib(default=attr.Factory(list), validator=attr.validators.instance_of(list))

tests/test_cmd2.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2014,6 +2014,25 @@ def test_macro_create_with_escaped_args(base_app, capsys):
20142014
out = run_cmd(base_app, 'fake')
20152015
assert 'No help on {1}' in out[0]
20162016

2017+
def test_macro_usage_with_missing_args(base_app, capsys):
2018+
# Create the macro
2019+
out = run_cmd(base_app, 'macro create fake help {1} {2}')
2020+
assert out == normalize("Macro 'fake' created")
2021+
2022+
# Run the macro
2023+
run_cmd(base_app, 'fake arg1')
2024+
out, err = capsys.readouterr()
2025+
assert "expects at least 2 argument(s)" in err
2026+
2027+
def test_macro_usage_with_exta_args(base_app, capsys):
2028+
# Create the macro
2029+
out = run_cmd(base_app, 'macro create fake help {1}')
2030+
assert out == normalize("Macro 'fake' created")
2031+
2032+
# Run the macro
2033+
out = run_cmd(base_app, 'fake alias create')
2034+
assert "Usage: alias create" in out[0]
2035+
20172036
def test_macro_create_with_missing_arg_nums(base_app, capsys):
20182037
# Create the macro
20192038
run_cmd(base_app, 'macro create fake help {1} {3}')
@@ -2026,16 +2045,6 @@ def test_macro_create_with_invalid_arg_num(base_app, capsys):
20262045
out, err = capsys.readouterr()
20272046
assert "Argument numbers must be greater than 0" in err
20282047

2029-
def test_macro_create_with_wrong_arg_count(base_app, capsys):
2030-
# Create the macro
2031-
out = run_cmd(base_app, 'macro create fake help {1} {2}')
2032-
assert out == normalize("Macro 'fake' created")
2033-
2034-
# Run the macro
2035-
run_cmd(base_app, 'fake arg1')
2036-
out, err = capsys.readouterr()
2037-
assert "expects 2 argument(s)" in err
2038-
20392048
def test_macro_create_with_unicode_numbered_arg(base_app, capsys):
20402049
# Create the macro expecting 1 argument
20412050
out = run_cmd(base_app, 'macro create fake help {\N{ARABIC-INDIC DIGIT ONE}}')
@@ -2044,7 +2053,7 @@ def test_macro_create_with_unicode_numbered_arg(base_app, capsys):
20442053
# Run the macro
20452054
out = run_cmd(base_app, 'fake')
20462055
out, err = capsys.readouterr()
2047-
assert "expects 1 argument(s)" in err
2056+
assert "expects at least 1 argument(s)" in err
20482057

20492058
def test_macro_create_with_missing_unicode_arg_nums(base_app, capsys):
20502059
run_cmd(base_app, 'macro create fake help {1} {\N{ARABIC-INDIC DIGIT THREE}}')

0 commit comments

Comments
 (0)