Skip to content

Commit

Permalink
Rename add_commands() args: "namespace*" → "group*" (#165)
Browse files Browse the repository at this point in the history
  • Loading branch information
neithere committed Aug 26, 2023
1 parent dacda5a commit a168336
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 71 deletions.
9 changes: 9 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ Backwards incompatible changes:
instead of returning without error. For most users, this means failed commands
will now exit with a failure status instead of a success. (#161)

Deprecated:

- Renamed arguments in `add_commands()` (#165):

- `namespace` → `group_name`
- `namespace_kwargs` → `group_kwargs`

The old names are deprecated and will be removed in v.0.30.

Enhancements:

- Can control exit status (see Backwards Incompatible Changes above) when raising
Expand Down
4 changes: 2 additions & 2 deletions docs/source/subparsers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Subparsers
~~~~~~~~~~

The statement ``parser.add_commands([bar, quux])`` builds two subparsers named
`bar` and `quux`. A "subparser" is an argument parser bound to a namespace. In
`bar` and `quux`. A "subparser" is an argument parser bound to a group name. In
other words, it works with everything after a certain positional argument.
`Argh` implements commands by creating a subparser for every function.

Expand Down Expand Up @@ -31,7 +31,7 @@ The equivalent code without `Argh` would be::
Now consider this expression::

parser = ArghParser()
parser.add_commands([bar, quux], namespace='foo')
parser.add_commands([bar, quux], group_name='foo')
parser.dispatch()

It produces a command hierarchy for the command-line expressions ``foo bar``
Expand Down
5 changes: 2 additions & 3 deletions docs/source/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -233,10 +233,9 @@ The commands will be accessible under the related functions' names::
Subcommands
...........

If the application has too many commands, they can be grouped into namespaces::
If the application has too many commands, they can be grouped::

argh.add_commands(parser, [serve, ping], namespace='www',
title='Web-related commands')
argh.add_commands(parser, [serve, ping], group_name='www')

The resulting CLI is as follows::

Expand Down
102 changes: 66 additions & 36 deletions src/argh/assembling.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,13 +308,15 @@ def set_default_command(parser, function):
def add_commands(
parser,
functions,
namespace=None,
namespace_kwargs=None,
group_name=None,
group_kwargs=None,
func_kwargs=None,
# deprecated args:
title=None,
description=None,
help=None,
namespace=None,
namespace_kwargs=None,
):
"""
Adds given functions as commands to given parser.
Expand All @@ -333,13 +335,13 @@ def add_commands(
function name. Note that the underscores in the name are replaced with
hyphens, i.e. function name "foo_bar" becomes command name "foo-bar".
:param namespace:
:param group_name:
an optional string representing the group of commands. For example, if
a command named "hello" is added without the namespace, it will be
available as "prog.py hello"; if the namespace if specified as "greet",
a command named "hello" is added without the group name, it will be
available as "prog.py hello"; if the group name if specified as "greet",
then the command will be accessible as "prog.py greet hello". The
namespace itself is not callable, so "prog.py greet" will fail and only
group itself is not callable, so "prog.py greet" will fail and only
display a help message.
:param func_kwargs:
Expand All @@ -349,12 +351,28 @@ def add_commands(
dictionary have the highest priority, so a function's docstring is
overridden by a `help` in `func_kwargs` (if present).
:param namespace_kwargs:
:param group_kwargs:
a `dict` of keyword arguments to be passed to the nested ArgumentParser
instance under given `namespace`.
instance under given `group_name`.
Deprecated params that should be renamed:
:param namespace:
.. deprecated:: 0.29.0
This argument will be removed in Argh v.0.30.
Please use `group_name` instead.
:param namespace_kwargs:
Deprecated params that should be moved into `namespace_kwargs`:
.. deprecated:: 0.29.0
This argument will be removed in Argh v.0.30.
Please use `group_kwargs` instead.
Deprecated params that should be moved into `group_kwargs`:
:param title:
Expand Down Expand Up @@ -392,58 +410,72 @@ def add_commands(
results in `AssemblingError`.
"""
# FIXME "namespace" is a correct name but it clashes with the "namespace"
# that represents arguments (argparse.Namespace and our ArghNamespace).
# We should rename the argument here.
namespace_kwargs = namespace_kwargs or {}
group_kwargs = group_kwargs or {}

# TODO remove this in 0.30
# ------------------------------------------------------------------------
# TODO remove all of these in 0.30
#
if namespace:
warnings.warn(
"Argument `namespace` is deprecated in add_commands(), "
+ "it will be removed in Argh 0.30. "
+ "Please use `group_name` instead.",
DeprecationWarning,
)
group_name = namespace
if namespace_kwargs:
warnings.warn(
"Argument `namespace_kwargs` is deprecated in add_commands(), "
+ "it will be removed in Argh 0.30. "
+ "Please use `group_kwargs` instead.",
DeprecationWarning,
)
group_kwargs = namespace_kwargs
if title:
warnings.warn(
"Argument `title` is deprecated in add_commands(), "
+ "it will be removed in Argh 0.30. "
+ "Please use `parser_kwargs` instead.",
DeprecationWarning,
)
namespace_kwargs["description"] = title
group_kwargs["description"] = title
if help:
warnings.warn(
"Argument `help` is deprecated in add_commands(), "
+ "it will be removed in Argh 0.30. "
+ "Please use `parser_kwargs` instead.",
DeprecationWarning,
)
namespace_kwargs["help"] = help
group_kwargs["help"] = help
if description:
warnings.warn(
"Argument `description` is deprecated in add_commands(), "
+ "it will be removed in Argh 0.30. "
+ "Please use `parser_kwargs` instead.",
DeprecationWarning,
)
namespace_kwargs["description"] = description
group_kwargs["description"] = description
#
# /
# ------------------------------------------------------------------------

subparsers_action = get_subparsers(parser, create=True)

if namespace:
if group_name:
# Make a nested parser and init a deeper _SubParsersAction under it.

# Create a named group of commands. It will be listed along with
# root-level commands in ``app.py --help``; in that context its `title`
# can be used as a short description on the right side of its name.
# Normally `title` is shown above the list of commands
# in ``app.py my-namespace --help``.
# in ``app.py my-group --help``.
subsubparser_kw = {
"help": namespace_kwargs.get("title"),
"help": group_kwargs.get("title"),
}
subsubparser = subparsers_action.add_parser(namespace, **subsubparser_kw)
subparsers_action = subsubparser.add_subparsers(**namespace_kwargs)
subsubparser = subparsers_action.add_parser(group_name, **subsubparser_kw)
subparsers_action = subsubparser.add_subparsers(**group_kwargs)
else:
if namespace_kwargs:
raise ValueError("`parser_kwargs` only makes sense " "with `namespace`.")
if group_kwargs:
raise ValueError("`group_kwargs` only makes sense with `group_name`.")

for func in functions:
cmd_name, func_parser_kwargs = _extract_command_meta_from_func(func)
Expand Down Expand Up @@ -474,23 +506,21 @@ def _extract_command_meta_from_func(func):
return cmd_name, func_parser_kwargs


def add_subcommands(parser, namespace, functions, **namespace_kwargs):
def add_subcommands(parser, group_name, functions, **group_kwargs):
"""
A wrapper for :func:`add_commands`.
These examples are equivalent::
add_commands(parser, [get, put], namespace='db',
namespace_kwargs={
'title': 'database commands',
'help': 'CRUD for our silly database'
add_commands(parser, [get, put], group_name="db",
group_kwargs={
"title": "database commands",
"help": "CRUD for our silly database"
})
add_subcommands(parser, 'db', [get, put],
title='database commands',
help='CRUD for our silly database')
add_subcommands(parser, "db", [get, put],
title="database commands",
help="CRUD for our database")
"""
add_commands(
parser, functions, namespace=namespace, namespace_kwargs=namespace_kwargs
)
add_commands(parser, functions, group_name=group_name, group_kwargs=group_kwargs)
16 changes: 8 additions & 8 deletions tests/test_assembling.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,14 +143,14 @@ def three():
assert ns_default.get_function() == three


def test_add_command_with_namespace_kwargs_but_no_namespace_name():
def test_add_command_with_group_kwargs_but_no_group_name():
def one():
return 1

p = argh.ArghParser()
err_msg = "`parser_kwargs` only makes sense with `namespace`"
err_msg = "`group_kwargs` only makes sense with `group_name`"
with pytest.raises(ValueError, match=err_msg):
p.add_commands([one], namespace_kwargs={"help": "foo"})
p.add_commands([one], group_kwargs={"help": "foo"})


def test_set_default_command_mixed_arg_types():
Expand Down Expand Up @@ -287,18 +287,18 @@ def test_set_default_command_deprecation_warnings():
with pytest.warns(
DeprecationWarning, match="Argument `title` is deprecated in add_commands()"
):
argh.add_commands(parser, [], namespace="a", title="bar")
argh.add_commands(parser, [], group_name="a", title="bar")

with pytest.warns(
DeprecationWarning,
match="Argument `description` is deprecated in add_commands()",
):
argh.add_commands(parser, [], namespace="b", description="bar")
argh.add_commands(parser, [], group_name="b", description="bar")

with pytest.warns(
DeprecationWarning, match="Argument `help` is deprecated in add_commands()"
):
argh.add_commands(parser, [], namespace="c", help="bar")
argh.add_commands(parser, [], group_name="c", help="bar")


@mock.patch("argh.assembling.add_commands")
Expand All @@ -319,8 +319,8 @@ def get_items():
mock_add_commands.assert_called_with(
mock_parser,
[get_items],
namespace="db",
namespace_kwargs={
group_name="db",
group_kwargs={
"title": "database commands",
"help": "CRUD for our silly database",
},
Expand Down

0 comments on commit a168336

Please sign in to comment.