diff options
author | Armin Ronacher <armin.ronacher@active-4.com> | 2014-04-29 22:07:15 +0200 |
---|---|---|
committer | Armin Ronacher <armin.ronacher@active-4.com> | 2014-04-29 22:07:15 +0200 |
commit | 055a8e6054700951c95aac3d9709e4538ec7c0b8 (patch) | |
tree | a3cc3a2a9e4a6ce5022201439c4b8ad5dc54e10a | |
parent | 51ebc82f828d392cb5b56ed202f60855ffb12733 (diff) | |
download | click-055a8e6054700951c95aac3d9709e4538ec7c0b8.tar.gz |
Implemented basic command collections
-rw-r--r-- | click/__init__.py | 8 | ||||
-rw-r--r-- | click/core.py | 29 | ||||
-rw-r--r-- | docs/api.rst | 3 | ||||
-rw-r--r-- | docs/commands.rst | 49 |
4 files changed, 85 insertions, 4 deletions
diff --git a/click/__init__.py b/click/__init__.py index 43244bd..624a5ca 100644 --- a/click/__init__.py +++ b/click/__init__.py @@ -15,8 +15,8 @@ """ # Core classes -from .core import Context, Command, MultiCommand, Group, Parameter, \ - Option, Argument +from .core import Context, Command, MultiCommand, Group, CommandCollection, \ + Parameter, Option, Argument # Decorators from .decorators import pass_context, pass_obj, make_pass_decorator, \ @@ -35,8 +35,8 @@ from .exceptions import UsageError, Abort __all__ = [ # Core classes - 'Context', 'Command', 'MultiCommand', 'Group', 'Parameter', - 'Option', 'Argument', + 'Context', 'Command', 'MultiCommand', 'Group', 'CommandCollection', + 'Parameter', 'Option', 'Argument', # Decorators 'pass_context', 'pass_obj', 'make_pass_decorator', 'command', 'group', diff --git a/click/core.py b/click/core.py index eba72ad..7a72c48 100644 --- a/click/core.py +++ b/click/core.py @@ -471,6 +471,35 @@ class Group(MultiCommand): return sorted(self.commands) +class CommandCollection(MultiCommand): + """A command collection is a multi command that merges multiple multi + commands together into one. This is a straightforward implementation + that accepts a list of different multi commands as sources and + provides all the commands for each of them. + """ + + def __init__(self, name=None, sources=None, **attrs): + MultiCommand.__init__(self, name, **attrs) + #: The list of registered multi commands. + self.sources = sources or [] + + def add_source(self, multi_cmd): + """Adds a new multi command to the chain dispatcher.""" + self.sources.append(multi_cmd) + + def get_command(self, ctx, cmd_name): + for source in self.sources: + rv = source.get_command(ctx, cmd_name) + if rv is not None: + return rv + + def list_commands(self, ctx): + rv = set() + for source in self.sources: + rv.update(source.list_commands(ctx)) + return sorted(rv) + + class Parameter(object): """A parameter to a command comes in two versions: they are either :class:`Option`\s or :class:`Argument`\s. Other subclasses are currently diff --git a/docs/api.rst b/docs/api.rst index a957053..79fa046 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -52,6 +52,9 @@ Commands .. autoclass:: Group :members: +.. autoclass:: CommandCollection + :members: + Parameters ---------- diff --git a/docs/commands.rst b/docs/commands.rst index ec3a5b5..23427cf 100644 --- a/docs/commands.rst +++ b/docs/commands.rst @@ -205,3 +205,52 @@ These custom classes can also be used with decorators: @click.command(cls=MyCLI) def cli(): pass + +Merging Multi Commands +---------------------- + +In addition to implementing custom multi commands it can also be +interesting to merge multiple together into one script. While this is +generally not as recommended as nesting one below the other the merging +approach can be useful in some circumstances for a nicer shell experience. + +A default implementation for such a merging system is the +:class:`CommandCollection` class. It accepts a list of other multi +commands and makes the commands available on the same class class. It +accepts a list of other multi commands and makes the commands available on +the same level. + +Example usage: + +.. click:example:: + + import click + + @click.group() + def cli1(): + pass + + @cli1.command() + def cmd1(): + """Command on cli1""" + + @click.group() + def cli2(): + pass + + @cli1.command() + def cmd2(): + """Command on cli2""" + + cli = click.CommandCollection(sources=[cli1, cli2]) + + if __name__ == '__main__': + cli() + +And what it looks like: + +.. click:run:: + + invoke(cli, prog_name='cli', args=['--help']) + +In case a command exists on more than one source, the first source wins. |