commit a61fa7385cace7c290c44c31ad733bdf2f504860
Author: Jiri Kuncar <jiri.kuncar@gmail.com>
Date: Tue Nov 14 09:24:07 2017 +0100
Add :commands: option for click directive
Improves control over which commands should be shown in documentation
and in which order.
Closes #18
Signed-off-by: Jiri Kuncar <jiri.kuncar@gmail.com>
diff --git a/docs/usage.rst b/docs/usage.rst
index 9966c61..9c7bd43 100644
--- a/docs/usage.rst
+++ b/docs/usage.rst
@@ -30,6 +30,9 @@ directive`__.
`:show-nested:`
Enable full documentation for sub-commands.
+ `:commands:`
+ Document only listed commands.
+
__ http://www.sphinx-doc.org/en/stable/extdev/markupapi.html
__ http://click.pocoo.org/
@@ -54,6 +57,12 @@ module:
"""Greet a user."""
click.echo('Hello %s' % user)
+ @greet.command()
+ def world(user):
+ """Greet the world."""
+ click.echo('Hello world!')
+
+
To document this, use the following:
.. code-block:: rst
@@ -70,6 +79,14 @@ output, add the ``show-nested`` flag.
:prog: hello-world
:show-nested:
+You can also document only selected commands by using ``:commands:`` option.
+
+.. code-block:: rst
+
+ .. click:: hello_world:greet
+ :prog: hello-world
+ :commands: hello
+
Modifying ``sys.path``
----------------------
diff --git a/sphinx_click/ext.py b/sphinx_click/ext.py
index bb13765..c0420c5 100644
--- a/sphinx_click/ext.py
+++ b/sphinx_click/ext.py
@@ -153,16 +153,26 @@ def _format_envvars(ctx):
def _format_subcommand(command):
"""Format a sub-command of a `click.Command` or `click.Group`."""
- yield '.. object:: {}'.format(command[0])
+ yield '.. object:: {}'.format(command.name)
- if command[1].short_help:
+ if command.short_help:
yield ''
for line in statemachine.string2lines(
- command[1].short_help, tab_width=4, convert_whitespace=True):
+ command.short_help, tab_width=4, convert_whitespace=True):
yield _indent(line)
+def _filter_commands(ctx, commands=None):
+ """Return list of used commands."""
+ if commands is None:
+ return sorted(getattr(ctx.command, 'commands', {}).values(),
+ key=lambda item: item.name)
+ else:
+ names = [name.strip() for name in commands.split(',')]
+ lookup = getattr(ctx.command, 'commands', {})
+ return [lookup[name] for name in names if name in lookup]
+
-def _format_command(ctx, show_nested):
+def _format_command(ctx, show_nested, commands=None):
"""Format the output of `click.Command`."""
# description
@@ -213,7 +223,7 @@ def _format_command(ctx, show_nested):
if show_nested:
return
- commands = sorted(getattr(ctx.command, 'commands', {}).items())
+ commands = _filter_commands(ctx, commands)
if commands:
yield '.. rubric:: Commands'
@@ -232,6 +242,7 @@ class ClickDirective(rst.Directive):
option_spec = {
'prog': directives.unchanged_required,
'show-nested': directives.flag,
+ 'commands': directives.unchanged,
}
def _load_module(self, module_path):
@@ -266,7 +277,7 @@ class ClickDirective(rst.Directive):
return getattr(mod, attr_name)
- def _generate_nodes(self, name, command, parent=None, show_nested=False):
+ def _generate_nodes(self, name, command, parent=None, show_nested=False, commands=None):
"""Generate the relevant Sphinx nodes.
Format a `click.Group` or `click.Command`.
@@ -275,6 +286,7 @@ class ClickDirective(rst.Directive):
:param command: Instance of `click.Group` or `click.Command`
:param parent: Instance of `click.Context`, or None
:param show_nested: Whether subcommands should be included in output
+ :param commands: Display only listed commands or skip the section if empty
:returns: A list of nested docutil nodes
"""
ctx = click.Context(command, info_name=name, parent=parent)
@@ -292,7 +304,7 @@ class ClickDirective(rst.Directive):
source_name = ctx.command_path
result = statemachine.ViewList()
- lines = _format_command(ctx, show_nested)
+ lines = _format_command(ctx, show_nested, commands)
for line in lines:
result.append(line, source_name)
@@ -301,11 +313,11 @@ class ClickDirective(rst.Directive):
# Subcommands
if show_nested:
- commands = getattr(ctx.command, 'commands', {})
- for command_name, command_obj in sorted(commands.items()):
+ commands = _filter_commands(ctx, commands)
+ for command in commands:
section.extend(self._generate_nodes(
- command_name,
- command_obj,
+ command.name,
+ command,
ctx,
show_nested))
@@ -322,8 +334,10 @@ class ClickDirective(rst.Directive):
raise self.error(':prog: must be specified')
show_nested = 'show-nested' in self.options
+ commands = self.options.get('commands')
- return self._generate_nodes(prog_name, command, None, show_nested)
+ return self._generate_nodes(
+ prog_name, command, None, show_nested, commands)
def setup(app):
diff --git a/tests/test_formatter.py b/tests/test_formatter.py
index 9288e01..db75f86 100644
--- a/tests/test_formatter.py
+++ b/tests/test_formatter.py
@@ -24,6 +24,8 @@ class GroupTestCase(unittest.TestCase):
output = list(ext._format_command(ctx, show_nested=False))
self.assertEqual(textwrap.dedent("""
+ A sample command group.
+
.. program:: cli
.. code-block:: shell
@@ -48,6 +50,8 @@ class GroupTestCase(unittest.TestCase):
output = list(ext._format_command(ctx, show_nested=False))
self.assertEqual(textwrap.dedent("""
+ A sample command group.
+
.. program:: cli
.. code-block:: shell
@@ -104,6 +108,8 @@ class NestedCommandsTestCase(unittest.TestCase):
output = list(ext._format_command(ctx, show_nested=False))
self.assertEqual(textwrap.dedent("""
+ A sample command group.
+
.. program:: cli
.. code-block:: shell
@@ -126,8 +132,71 @@ class NestedCommandsTestCase(unittest.TestCase):
output = list(ext._format_command(ctx, show_nested=True))
self.assertEqual(textwrap.dedent("""
+ A sample command group.
+
+ .. program:: cli
+ .. code-block:: shell
+
+ cli [OPTIONS] COMMAND [ARGS]...
+ """).lstrip(), '\n'.join(output))
+
+
+class CommandFilterTestCase(unittest.TestCase):
+
+ @staticmethod
+ def _get_ctx():
+
+ @click.group()
+ def cli():
+ """A sample command group."""
+
+ @cli.command()
+ def hello():
+ """A sample command."""
+
+ @cli.command()
+ def world():
+ """A world command."""
+
+ return click.Context(cli, info_name='cli')
+
+ def test_no_commands(self):
+ """Validate an empty command group."""
+
+ ctx = self._get_ctx()
+ output = list(ext._format_command(ctx, show_nested=False, commands=''))
+
+ self.assertEqual(textwrap.dedent("""
+ A sample command group.
+
+ .. program:: cli
+ .. code-block:: shell
+
+ cli [OPTIONS] COMMAND [ARGS]...
+ """).lstrip(), '\n'.join(output))
+
+
+ def test_order_of_commands(self):
+ """Validate the order of commands."""
+
+ ctx = self._get_ctx()
+ output = list(ext._format_command(ctx, show_nested=False, commands='world, hello'))
+
+ self.assertEqual(textwrap.dedent("""
+ A sample command group.
+
.. program:: cli
.. code-block:: shell
cli [OPTIONS] COMMAND [ARGS]...
+
+ .. rubric:: Commands
+
+ .. object:: world
+
+ A world command.
+
+ .. object:: hello
+
+ A sample command.
""").lstrip(), '\n'.join(output))