New commands can be easily added to the pipeline through the command framework.
Commands are managed by the pipeline, and those the module containing the
framework for the pipeline is defined in poppy.pop.command
. To create
a new command, simply derive a class from Command
.
For example, we will create a command to display informations on the pipeline, such as who wrote the pipeline.
from poppy.pop import Command
class WhoCommand(Command):
"""
Define a command displaying informations on the pipeline author.
"""
# a name to reference the command
__command__ = "who_command"
# the name of the command used
__command_name__ = "who"
# the parent of the command
__parent__ = "master"
# the help message displayed to the user
__help__ = "Show the author of the pipeline"
def __call__(self, args):
print("Super Mario, pipeline expert")
That’s all!
Calling the pipeline with the who sub-command will display the name of the author. Now the details of the parameters.
The WhoCommand
inherits the Command
. By doing that, the
WhoCommand
is automatically registered by the framework, and the
command is made available to the pipeline. Some attributes allows to perform
some selection and modify the behaviour of the command.
WhoCommand.__command__
is used to give a reference name to the
command. If the command associated to the WhoCommand
needs to
be referenced somewhere, this is the name defined by
WhoCommand.__command__
that will be used.WhoCommand.__command_name__
is the name of the command. When
invoking the sub-command, this is the
WhoCommand.__command_name__
that will be used.WhoCommand.__parent__
is the reference to the parent
sub-command. If you want that your command to be used as a sub-command of
another command, simply add the reference name of another command inside
this variable.WhoCommand.__help__
contains the message to display to the user
when invoking the help.Warning
In order to the framework be able to discover the command just defined, you should be sure of two things:
Command
classLet’s say you are an exigent user and you want to have the possibility to
select the format for displaying the name of the author. A way to do that is
to create arguments that will be used in the command handler. For this, you
just have to define a method to add argument to the created parser for the
command, parser created by argparse
.
So we add an option to the who command to display only the information on
the author name, and a second one to display only the short description.
Rewriting WhoCommand
:
class WhoCommand(Command):
"""
Define a command displaying informations on the pipeline author.
"""
# a name to reference the command
__command__ = "who_command"
# the name of the command used
__command_name__ = "who"
# the parent of the command
__parent__ = "master"
# the help message displayed to the user
__help__ = "Show the author of the pipeline"
def add_arguments(self, parser):
"""
Add arguments to the parser associated to the command.
"""
# add option for displaying only the author name
parser.add_argument(
"--author",
help="Show only author name",
action="store_true",
)
# same but only for the description
parser.add_argument(
"--description",
help="Show only the description of the author",
action="store_true",
)
def __call__(self, args):
if args.author:
print("Super Mario")
elif args.description:
print("pipeline expert")
else:
print("Super Mario, pipeline expert")
Now doing:
$ pipeline who
Super Mario, pipeline expert
$ pipeline who --author
Super Mario
$ pipeline who --description
pipeline expert
If you want to specify to the user that the two options are mutually
exclusives, you can the arguments inside a group. Simply modify
add_aguments()
to:
def add_arguments(self, parser):
"""
Add arguments to the parser associated to the command.
"""
# create a group
group = parser.add_mutually_exclusive_group()
# add option for displaying only the author name in the group
group.add_argument(
"--author",
help="Show only author name",
action="store_true",
)
# same but only for the description
group.add_argument(
"--description",
help="Show only the description of the author",
action="store_true",
)
Now, an error is displayed to the user if both options are given at the same time.
See also
Details on all the large possibilities on how to add arguments and
actions that can be taken with them are given on argparse
.
Now, you have a working pipeline, with several author contributions, and you
want to show a list of them through the command line interface. This is
clearly related to the who command, but you do not want to simply add
arguments, since you may want to add arguments to make some filtration on
the list you want to show. For this, you will need to add a subcommand to
the who command. This can be easily achieved by creating a new
Command
.
class WhoListCommand(Command):
"""
Command displaying the list of authors and contributors.
"""
# reference for the command
__command__ = "who_list_command"
# command used in cli
__command_name__ = "list"
# here specify that the parent command is the who one
__parent__ = "who_command"
# help message to display for this command
__help__ = "Show a list of contributors to the pipeline"
def __call__(self, args):
"""
With the list of authors taken from somewhere, display it when
invoked as a command.
"""
# simple message
print("Contributors:")
# print the list of authors
print(", ".join(get_authors_list()))
The command is used like this:
$ pipeline who list
Contributors:
Super Mario, Luigi, Peach
This is simply the __parent__
that will order the hierarchy of
commands between them. You can add any arguments as needed to this command
as described above. The arguments and options will be associated to this
command only.
Note
The framework is very flexible and allows quick development of new
commands and change in the hierarchy. Let’s say that you do not want to
use the list command with the who command anymore, and want it to be
a “root” command, independent of the who one. The only thing you have
to do is changing __parent__
to __master__
and rename the
command if you wish with the __command_name__
attribute.
Resulting in a calling interface like this:
$ pipeline list
Contributors:
Super Mario, Luigi, Peach
A command that you define can inherit the commands of a parent command if you specify it. By default, arguments of a command must be placed between the command and its subcommand, else the arguments will not be recognized.
$ pipeline command1 --arg_command1 command2
This command is valid. But the following not:
$ pipeline command1 command2 --arg_command1
while it seems natural to do it this way if for example, –arg_command1 is equivalent to a –verbose option.
To get something similar to the last behaviour, you can use the
__parent_arguments__
attribute, containing a list of parent command
names, whose arguments will be inherited.
Warning
If some arguments are conflicting given their names, the latter definition of the argument will be used in priority, which should with the framework structure, always be the one of the child command.
argparse
will always add an help option by default to any parser
that it creates. But for inheritance, this behaviour is not always wanted,
since the child command will override the help command of the parent
parser. To avoid such situations, you can also create a parser that will not
be used, only as a parent for other parsers. For this, you only need to
override the parser()
of the Command
.
def parser(self, subparser, parents):
"""
Return the parser for the command and options that this command must
use. Take as argument the subparser from the parent parser.
"""
# create a parser with no help
parser = argparse.ArgumentParser(add_help=False)
# add the parser to the list of parents
new_parents = parents + [parser]
# call the Command class method
old_parser = super(WhoCommand, self).parser(subparser, new_parents)
# return this parser
return parser
This way, a new parser is created with its arguments and no help, given to the parser of the command as a parent to get its options, and the parser is returned to be associated to the command, if a child wants to reference its arguments.
Warning
By using this technique, you are modifying the intrinsic behaviour of the framework. While it can be helpful some times, if you don’t know what you are doing or what you want to do can be achieved in an other way, try to avoid this “advanced technique”, since side effects will be unknown.
poppy.core.command.
Command
[source]¶Bases: object
Base class for all accepted commands for the pop command line program.
add_parent_parser
(name, parser)[source]¶Used to add a parser with its options and be able to refer from a command, since the conflict handler of argparse is not well done, as many other things.
poppy.core.command.
CommandManager
[source]¶A class to manage the available defined commands by the user through the plugin command classes.
generate
(options)[source]¶Given a first base subparser and the parents parser, generate the parsers for children commands recursively.
launch
(argv=None)[source]¶Launch the commands by parsing the input of the program and then calling the good command with the good arguments.