Commandant is a framework for building command-oriented tools.
Project description
Commandant is a framework for building command-oriented tools.
A command driven program takes a command name as its first argument. Subsequent arguments and options are passed to the command to customize its behaviour. Commandant is inspired by Bazaar’s user interface and is, in fact, a thin wrapper on top of bzrlib.
License
Commandant is a framework for building command-oriented tools. Copyright (C) 2009-2010 Jamshed Kakar.
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
On Ubuntu systems, the complete text of the GNU General Public Version 2 License is in /usr/share/common-licenses/GPL-2.
Using Commandant
Commandant can be used as a command runner. The bin/commandant program can present an application made up of commands and help topics grouped together in a directory. The example program described in the following sections is available in the example directory. You can try it out from the current directory by running the following commands:
$ alias example="bin/commandant example" $ source example/tab-completion.sh
Create a Commandant program
Commands are grouped into Commandant programs. A Commandant program is made up of an arbitrary number of commands stored in a directory:
$ mkdir -p ~/example
An alias can be used to provide a name that can used to run commands in the Commandant program:
$ alias example="commandant ~/example"
Getting help
Commands provides builtin help and version commands. Running the example program by itself shows basic help information:
$ example Commandant -- a framework for building command-oriented tools http://launchpad.net/commandant Basic commands: commandant help commands List all commands commandant help topics List all help topics
Passing the commands topic to the help command lists the commands that are available, with a short summary about each one:
$ example help commands help Show help about a command or topic. version Show version of commandant.
Passing the topics topic to the help command lists the help topics that are available, with a short summary about each one:
$ example help topics commands Basic help for all commands. topics Topics list.
The version command shows the version of Commandant being used:
$ example version commandant 0.1
Create an executable command
One of the easiest ways to add a command to a Commandant program is by creating a shell script and making it executable:
$ echo -e '#!/bin/sh\necho Hello, world!' > ~/example/hello $ chmod +x ~/example/hello
The new hello command in the example program is now registered and ready to use:
$ example help commands hello help Show help about a command or topic. version Show version of commandant.
You should see ‘Hello, world!’ printed to your screen when you run it:
$ example hello Hello, world!
Create an executable command that takes arguments
Commandant will pass all arguments beyond the command name to the executable for that command:
$ echo -e '#!/bin/sh\necho $*' > ~/example/echo $ chmod +x ~/example/echo
Again, just by putting an executable file in the command directory, the new echo command has been added to the example program:
$ example help commands echo hello help Show help about a command or topic. version Show version of commandant.
The new echo command will repeat whatever we tell it:
$ example echo Hello there! Hello there!
Providing help for commands
The commands in the example program have been very easy to add, but they could be easier to use. Commandant’s builtin help system can be extended to provide help topics for user-provided commands. Files in the command directory with a .txt extension, and with the same name as a command, will be treated as help content for that command. Adding help content for the hello command is quite easy:
$ cat ~/example/hello.txt Greet the world! Print 'Hello, world!' to the screen.
The first line in a help topic is used as a short description. This short description is used when listing commands:
$ example help commands echo hello Greet the world! help Show help about a command or topic. version Show version of commandant.
Notice that the hello command uses the short description from the help topic. The complete help text can be seen by passing the command name to the help command:
$ example help hello Print 'Hello, world!' to the screen.
Providing a custom splash page
The stock help text shown when the help command is run points users to the list of commands and help topics. It can be overridden by providing a file called basic.txt:
$ cat ~/example/basic.txt example -- A collection of command examples that work with Commandant. http://launchpad.net/commandant Basic commands: example help commands List all commands example help topics List all help topics
The contents of this file are shown when the help command is run without a topic:
$ example help example -- A collection of command examples that work with Commandant. http://launchpad.net/commandant Basic commands: example help commands List all commands example help topics List all help topics
Providing help topics
The builtin help system can also be used to provide general help topics, not bound to any command name. Files in the command directory with a .txt extension, and with a name that doesn’t match any command name, will be treated as help topics. Adding help to describe a concept, for example, is quite easy:
$ cat ~/example/greetings.txt Greetings are a way to initiate communication. Greeting (also called accosting) is a way for human beings (as well as other members of the animal kingdom) to intentionally communicate awareness of each other's presence, to show attention to, and to suggest a type of relationship or social status between individuals or groups of people coming in contact with each other. Taken from http://en.wikipedia.org/wiki/Greeting.
As with help files for commands, the first line contains a short summary with help text following. The topic will now appear in the topics list:
$ example help topics commands Basic help for all commands. greetings Greetings are a way to initiate communication. topics Topics list.
The help text can be seen by passing the topic name to the help command:
$ example help greetings Greeting (also called accosting) is a way for human beings (as well as other members of the animal kingdom) to intentionally communicate awareness of each other's presence, to show attention to, and to suggest a type of relationship or social status between individuals or groups of people coming in contact with each other. Taken from http://en.wikipedia.org/wiki/Greeting.
Create a Python command
Executable commands such as shell scripts are great for some tasks, but they are unweildy for others. Commandant builds on bzrlib’s command API for implementing Python commands, which are useful in situations where executable commands don’t work well, such as when complex command-line argument and option parsing is required. When Commandant loads commands from the command directory it imports Python commands from files with a .py extension. The commands in the files need to subclass the Command class and need to be named using the cmd_<name> naming convention:
$ cat example/rock-fact.py from bzrlib.commands import Command class cmd_rock_fact(Command): """Show a fact about rocks. This command prints a fascinating fact about rocks. """ def run(self): print >>self.outf, "Rocks are really hard."
Just like with executable commands, adding a Python command is as easy as adding a file to the command directory. An outf attribute will be set on the command object when it’s run and should be used when printing text:
$ example help commands echo hello Greet the world! help Show help about a command or topic. rock-fact Show a fact about rocks. version Show version of commandant.
The new command is available using the name of the class, without the cmd_ part, and with underscores converted to dashes. The doctring is used to provide builtin help. The first line is used as the summary and the subsequent content is used as the help text, just like in help files for executable commands:
$ example help rock-fact This command prints a fascinating fact about rocks.
More than one Command implementation can be provided in a single Python file.
Create a Python command that takes arguments
One of the main advantages of writing Python commands is being able to express command-line argument and option parameters. bzrlib uses this data to automatically provide parsing and integration with the help system:
$ cat example/fortune.py from random import randint from bzrlib.commands import Command FORTUNES = ["%s will win a million dollars", "%s will find deep satisfaction", "%s will develop a strong relationship"] class cmd_fortune(Command): """Show a fortune. This command prints a fortune. """ takes_args = ["name"] takes_options = [ Option("crude", help="Add a crude suffix to the fortune.")] def run(self, name=None, crude=None): fortune = FORTUNES[randint(0, len(FORTUNES) - 1)] % (name,) if crude: fortune = "%s in bed" % (fortune,) print >>self.outf, fortune
The functionality provides by bzrlib’s Command implementation makes it possible to write commands with very rich command-line interfaces.
Create a Python command that uses Twisted
Twisted is a popular framework for asynchronous network programming. Commandant has builtin support for writing commands that need to run in a Twisted reactor. Simply subclass TwistedCommand and implement a command as you normally would. It’s run() method will be called inside a running reactor:
$ cat example/get-page.py from twisted.internet import reactor from twisted.internet.ssl import ClientContextFactory from twisted.web.client import HTTPClientFactory from commandant.commands import TwistedCommand class cmd_get_page(TwistedCommand): """Download a web page using Twisted and print it to the screen. This command uses Twisted to download a web page and demonstrates how to write an asynchronous command with Commandant. """ takes_args = ["url"] def run(self, url): """Fetch the page at C{url} and print it to the screen.""" client = HTTPClientFactory(url) if client.scheme == "https": factory = ClientContextFactory() reactor.connectSSL(client.host, client.port, client, factory) else: reactor.connectTCP(client.host, client.port, client) def write_response(self, result): print >>self.outf, result.decode("utf-8") def write_failure(self, failure): print >>self.outf, failure client.deferred.addCallback(write_response) client.deferred.addErrback(write_failure) return client.deferred
When the command finishes, the reactor will be stopped.
Embedding Commandant in an application
To this point, the examples have centered around commands and help topics grouped together in directory, with bin/commandant used to present a frontend. This mode of using Commandant is useful in certain situations, but much of the time embedding Commandant directly in an application is more desirable.
Bootstrapping an application
The first step is to create an entry point which registers commands and help topics and then subsequently runs your program. The CommandController is both a registry and dispatching device:
from commandant import builtins from commandant.controller import CommandController def main(argv): """Run the command named in C{argv}. If a command name isn't provided the C{help} command is shown. @param argv: A list command-line arguments. The first argument should be the name of the command to run. Any further arguments are passed to the command. """ if len(argv) < 2: argv.append("help") controller = CommandController("name", "version", "summary", "url") controller.load_module(builtins) controller.install_bzrlib_hooks() controller.run(argv[1:])
The name, version, summary and URL are used in generated help text. The builtins module contains the builtin help and version commands, and the basic, commands, hidden-commands and topics help topics.
Registering application commands
Commands can be grouped in a module and registered with the command controller. Just like in the examples above, command classes should be named using the cmd_command_name naming convention and will be loaded automatically when the module is registered. If all the commands in the examples above were grouped in an example.commands module they could be registered just like the builtin commands:
from example import commands controller.load_module(commands)
Create a Python help topic
In the examples above, help topics are text files in a directory. When embedding Commandant in an application, its easier to use Python for help topics:
from commandant.help_topics import DocstringHelpTopic class topic_sample_document(DocstringHelpTopic): """This first line is the short topic summary. The rest of the docstring is the help topic content and will be shown when the 'example help sample-document' command is run. """
Registering help topics
Registering help topics is just like registering commands. They can be grouped in a module and registered with the command controller. Topics should use the topic_document_name naming convention:
from example import help_topics controller.load_module(help_topics)
Providing a custom splash page
The stock help text shown when the help command is run points users to the list of commands and help topics that have been registered with the controller. If a topic_basic help topic has been registered it will be shown instead of the builtin splash page.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.