The cmdparser package contains two modules which are useful for writing text command parsers, particularly using the builtin Python cmd module.
The package consists of two modules:
These two modules are discussed below briefly. For more information see the docstrings of the two modules, and also the ttrack command-line application (from which these libraries originated) makes a good example of their use.
Install the cmdparser package from PyPI. For example, to install using pip:
pip install cmdparser
This module allows the creation of parse trees from textual command specifications of the following form:
ham ( eggs | chips [spam] | beans [spam [...]] )
These parse trees can then be used to check for matches against particular command strings, and also allow valid completions of partial command strings to be listed. To build a parse tree and use it in a few examples, see the following example code:
from cmdparser import cmdparser
parse_tree = cmdparser.parse_spec("one (two|three) <four> [five]")
# Returns None to indicate successful parse
parse_tree.check_match(("one", "two", "anything"))
# Returns an appropriate parsing error message
parse_tree.check_match(("one", "three", "anything", "six"))
# Returns the list ["two", "three"]
parse_tree.get_completions(("one",))
As well as dealing with fixed token strings, dynamic tokens can also be set up where the list of strings accepted can change over time, or where arbitrary strings or lists of strings can be accepted. See the module’s docstrings for specifics of the classes available, but as an example:
from cmdparser import cmdparser
class ColourToken(cmdparser.Token):
def get_values(self, context):
# Static list here, but could easily be dynamic
return ["red", "orange", "yellow", "green", "blue", "purple"]
def my_ident_factory(token):
if token == "number":
return cmdparser.IntegerToken(token)
elif token == "colour":
return ColourToken(token)
return None
parse_tree = cmdparser.parse_tree("take <number> <colour> balls",
ident_factory=my_ident_factory)
# Returns None to indicate successful parse, and the "cmd_fields" dict will
# be initialised as:
# { "take": ["take"], "<number>": ["23"],
# "<colour>": ["blue"], "balls": ["balls"] }
cmd_fields = {}
parse_tree.check_match(("take", "23", "blue", "balls"), fields=cmd_fields)
# Returns an appropriate parsing error message
parse_tree.check_match(("take", "all", "red", "balls"))
# Returns the list ["red", "orange", "yellow", ..., "purple"]
parse_tree.get_completions(("take", "5"))
There are four classes which are suitable base classes for user-derived tokens:
There are also decorators for use with command handlers derived from cmd.Cmd which allow command strings to be automatically extracted from docstring help text, allowing command parsing and completion to be transparently added to the command-handling methods of the class.
To implement the cmd.Cmd class, various methods of the form do_XXX() are implemented. To add the cmdparser integration, these methods must contain a docstring the first line(s) of which form a command specification as outlined above, followed by a blank line and then any descriptive text for the operation of the command. The prototype is also altered, taking three arguments - the usual self argument, a list of parsed command line items and a fields dictionary as demonstrated in the example immediately above.
Once the methods have been suitably modified, the CmdMethodDecorator decorator should be applied to each of them, and the CmdClassDecorator decorator should be applied to the class definition as a whole:
from cmdparser import cmdparser
@cmdparser.CmdClassDecorator()
class CommandHandler(cmd.Cmd):
@cmdparser.CmdMethodDecorator():
def do_command(self, args, fields):
"""command ( add | delete ) <name>
This is an example command to demonstrate use of the cmd
decorators.
"""
# Method body goes here - it will only be called if a command
# parses successfully according to the specification above.
Note that due to the design of the cmd.Cmd class, the first token in the specification must be the same as the method name after the do_ prefix. An exception will be raised if this is not the case.
The method decorator adds some wrapper code which parses the entered command according to the specification, and displays an error message if parsing fails. Should parsing succeed, the implementation method itself is called with the parsed arguments and fields passed as from the check_match() method of the parse tree. Note that when using these decorators, the cmd.Cmd class instance is passed as the context parameter to many of the token methods, which can be useful for recovering dynamic state.
The class decorator then adds tab-completion methods for every decorated command method, so applications need not concern themselves with this at all.
It is not necessary to decorate every command method - for very simple commands which take no arguments it may be simpler to leave them bare. In this case, of course, the method prototype must match what is expected by cmd.Cmd (i.e. a single string parameter beyond the self parameter). However, if any method is decorated then the class decorator is required to add the completion methods.
Finally, note that as a convenience the docstring help for commands has the leading whitespace of the second line stripped from all lines (on the assumption that the first line immediately follows a triple quote and hence has no indentation). Lines are also wrapped to 80 columns in the help text.
Building on the parse trees within the cmdparser module, this module adds specific token types to parse human-readable specifications of date and time. It allows both absolute and relative dates to be specified, and these are converted to datetime and other instances as appropriate.
Some examples of the type of specifications supported:
The following classes are currently defined:
See the docstrings of the classes for more details and the spec class attribute for the complete specification of phrases that each class accepts.
If you have any questions, problems or requests, please get in touch with me at andy@andy-pearce.com. If you want to submit a bug, please do so via GitHub’s issue tracker for the TTrack application, with which cmdparser shares a repository.
If you want to make changes, the source code is available at GitHub - feel free to send me pull requests if you make an improvement you feel others would find useful.