Overview:
The SamCommand parsing tools make it easy develop self-documenting shell and python API interfaces in a uniform and consistent fashion. The user sees a consistent front-end to commands, with clear instructions on legal syntax when an illegal command is entered (or when help is requested). The product maintainer needs to define the legal syntax of the command, write an implementation method, and [optionally] an appropriate dispatch handler, but does not need to parse the command line elements on a case-by-case basis.
The SamCommand tools define four objects of interest:
- Command:
- Defines the elements of a legal command, including:
- verb
- requiredOptions
- allowedOptions
- allowedFlags
- envFlags
- allowedArgCount
- envOptions
- requiredEnvOptions
- synonymousOptions
- mutuallyExclusiveOptions
- mustHaveOneOfOptions
- restrictedValueOptions
- restrictedTypeOptions
- incompatibleOptionCombinations
- importModule
- dispatchHandler
- helpText
- seeAlso
- description
- isDeprecated
- apiImport
- apiObjectName
- apiDispatchMethod
- apiReturns
- apiHelpText
- apiObjectsThatReplaceOptions
- additionalAttr
- Contains methods which may be used or overridden for further control over commands, the most useful of which are:
- CommandInterface:
- A command interface is a special deriviative of a command, which can be called from the shell (via its cliDispatch method) or directly from python using kwargs (keyword-value pairs corresponding to the command line options, flags, and argument list). The __call__ method of the CommandInterface object parses the kwargs, builds the corresponding parsed argDict/argList, and passes control to the same implementation that would have been called from the shell. The advantage is that, when called from python, the same command line argument parsing and type-checking is performed, so that the actual implementation can be assured that no matter how it was called, the arguments are of the proper type and number.
CommandInterfaces have the following methods that may be overridden:
- cliDispatch: parse the shell arguments, invoke the implementation, handle the results
- cliPrepareOpts: perform any initial preparation of shell arguments before calling the implementation
- cliProcessResults: perform any post-processing of the results for the shell interface
- implementation: the actual implementation
- CommandSuite:
- A command suite "learns" a list of legal command definitions (and/or implementations). The commands are callable by the shell; the command suite parses the command line arguments according to the laws of this list. If the command is not legal, consistently formatted help text is available on the syntax and requirements of each command. If the command is legal, returns a legal command object to the caller.
Command Suites always include the verb form _generate _documentation, which can be used to write a consistent set of web pages (or text files) with the documentation from any command suite. Methods which are useful in this context are:
- setBgColor: set the overall background color for all documentation generated
- setSeeAlso: set additional seeAlso references for the entire command suite
- setShellCommand: set the shellCommand for all commands in the suite
- setSummaryFile: set the name of the summary index for all CLI usage docs
- setIndexFile: set the name of the index file for all generated docs
- setApiImport: set the apiImport string for all commands in the suite
- setApiObjectName: set the apiObjectName for all commands in the suite
- setWriteApi: flag to write API usage documents as well as CLI usage docs
- setApiSummaryFile: set the name of the summary file for all API usage docs
- CommandInterfaceSuite:
- A command interface suite is similar to a command suite, but each of the commands is also callable directly from the python interface with the same argument parsing and type-checking. The sam_user_pyapi (aka sam), sam_admin_pyapi (aka samAdmin) and sam_mis_pyapi (aka samMis) (among other) utilities are all encoded as instances of a CommandInterfaceSuite.
CommandInterfaceSuites have the following methods of interest:
- buildImplementationModuleList(): build a list of python modules containing the definitions of the CommandInterface objects which should be included in this suite
- getCommandSuite(): return the corresponding commandSuite
- dispatchCommand(args): command line dispatcher
- BlessedCommandInterfaceSuite:
- As of the v6_8 series of sam_common_pylib, it is now possible to create "blessed" command interface suites. The purpose is to improve performance by deferring instantiation of the command interface objects in a suite until/unless they are called.
To bless a command suite, the steps are:
The sam, sam_admin, and sam_mis command suites are all built using this technique.
- Create the command suite in the usual way (see sam_user_pyapi/src directories for examples).
- At build time, run the ${SAM_COMMON_PYLIB_DIR}/SamCommand/BlessedCommandInterfaceSuiteCodeGenerator.py script over the suite to generate the blessed suite. For example, the "sam" command suite is generated using:
python $SAM_COMMON_PYLIB_DIR/SamCommand/BlessedCommandInterfaceSuiteCodeGenerator.py \ --suiteName=sam \ --initString='from SamUserApi import getSam; sam = getSam()' \ --output=$SAM_USER_PYAPI_DIR/bin/sam.pyThe resulting sam.py script contains the "blessed" command definition for each of the interfaces in the suite (meaning, we no longer need to check the input as we build the list of interfaces, we accept this definition with no validation). Each of the blessed command definitions point to a "placeholder" class, which will instantiate the true interface class only when called.
The following examples are taken from the sam suite of utilities.
# # getting help for all available commands: # $ sam help Usage: sam <command> <requiredOptions> <otherOptions> <args> Where <command> is one of the following, sorted by category: Category Miscellaneous: _generate _documentation Category samConsumer: decrement job count end process OR end consumer process OR stop process establish consumer OR create consumer establish persistent process OR establish persistent consumer process OR create persistent process establish process OR establish consumer process OR create process get next file increment job count release file Category samDataset: create dataset OR take snapshot create definition OR create dataset definition translate constraints OR list files translate dimensions Category samDbInfo: get dbserver connection info get dbserver info get dbserver logfile data Category samDumpster: dump consumer dump dbserver dump file dump fss OR dump fsserver OR dump file storage server dump project dump station Category samLocate: add location erase file location OR erase location locate Category samMetadata: declare file get metadata undeclare file OR undeclare metadata update file crc update file size verify metadata OR verify description file Category samProject: declare project to station reserve project OR create project restart project start project start reserved project start unreserved project stop project Category samSubmit: run job run project submit OR submit job Category samUtility: describe metadata attributes describe metadata requirements get dimension info get metadata attribute descriptor get registered dimension categories get registered filetypes OR get registered file types get registered parameter keywords get registered station admins get registered station group admins get registered station groups get registered values # # getting help for limited number of matching commands: # $ sam help get Usage: sam <command> <requiredOptions> <otherOptions> <args> Where <command> is one of the following, sorted by category: Category samConsumer: get next file Category samDbInfo: get dbserver connection info get dbserver info get dbserver logfile data Category samMetadata: get metadata Category samUtility: get dimension info get metadata attribute descriptor get registered dimension categories get registered filetypes OR get registered file types get registered parameter keywords get registered station admins get registered station group admins get registered station groups get registered values # # getting help for a specific command: # $ sam help get next file Command Description: Receive the next file to be analyzed. Usage: sam get next file <--requiredOptions> [--options] [-flags] Where: --requiredOptions: --processId=<value> # Consumer process ID for the process receiving the file --project=<value> # name of the project under which this process is running --station=<value> # name of the station where the project is running --options: --erralertflag=<value> # set to "verbose" for more information in output message --rtfile=<value> # routing file into which results will be written (default: stdout) --timeout=<value> # number of seconds to wait before timing out --flags: -d -v Synonymous Options: --consumer-process-id=<value> # synonym for --processId=<value> --consumerProcessId=<value> # synonym for --processId=<value> --cpid=<value> # synonym for --processId=<value> --process-id=<value> # synonym for --processId=<value> Restricted Types: --erralertflag=( must be string ) --processId=( must be long ) --project=( must be string ) --rtfile=( must be string ) --station=( must be string ) --timeout=( must be long ) You may omit "--project=<value>" if the $SAM_PROJECT environmental variable is set. You may omit "--station=<value>" if the $SAM_STATION environmental variable is set.
A CommandSuite is a suite of related commands which have been defined, and which knows how to parse user-entered command lines and dispatch appropriately.Usage: In it's bare form, the usage of the CommandSuite is:
from SamCommand.CommandSuite import CommandSuite suite = CommandSuite( [<list of commands>] ) suite.learn( <list of commands> ) theCommand = suite.parseArgs(sys.argv) theCommand.dispatch()However, there are more "precautions" that your code should take when actually using the CommandSuite, and there are more features available to you when using the suite. These are illustrated in the example below.
#!/usr/bin/env python import sys from SamCommand.CommandSuite import CommandSuite from SamCommand.CommandDefinition import Command from SamCommand.CommandInterface import mainDispatch c1 = Command( <command definition 1> ) c2 = Command( <command definition 2> ) .. knownCommands = ( c1, c2 ) aSuite = CommandSuite( knownCommands ) # web documentation can be automatically generated; tailor # its appearance for our needs: aSuite.setBgColor("#FFFF00") # standard SAM yellow aSuite.setSeeAlso(["http://d0db.fnal.gov/", # list of other useful pages "http://d0db.fnal.gov/sam"]) .. c3 = Command( <command definition 3> ) c4 = Command( <command definition 4> ) aSuite.learn( (c3, c4) ) exitStatus = mainDispatch(aSuite, sys.argv) sys.exit(exitStatus)
Command Interface:
A command interface is a special type of derived Command which, through the magic of python, will make sure that when the native python implementation is called, the arguments will be parsed as if they had been passed through the shell.
In its simplest form, a command interface looks like:
#!/usr/bin/env python from SamCommand.CommandInterface import CommandInterface class anInterface(CommandInterface): def __init__(self): CommandInterface.__init__( self, commandParameters = { 'verb' : ['do something here'], 'requiredOptions' : [ 'opt1=', 'opt2=' ], 'allowedOptions' : [ 'opt3=' ] } ) def cliProcessResults(self, results): # for shell interface: stringify and print results print( str(results) ) def implementation(self, argDict, argList): # actual implementation is here; # argDict is a case-insensitive, dash-insensitive, # underscore-insensitive dictionary containing # the keyword/value pairs from either sys.argv, # or the user's call to anInterface(a1=v1, a2=v2, ...). opt1 = argDict.get('opt1') opt2 = argDict.get('opt2') opt3 = argDict.get('opt3', 'no argument entered for opt3') # do something here with the opts result = f(opt1, opt2, opt3) # return the final object result return result # call from python: myInterface = anInterface results = myInterface(opt1='this is opt1', opt2='this is opt2')
Command Interface Suite:
A command interface suite is a collection of python-callable command interfaces. In its simplest form, a command interface suite looks like:
# define the command interfaces that will be part of this suite: from SamCommand.CommandInterface import CommandInterface class doThingOne(CommandInterface): def __init__(self): CommandInterface.__init__( self, commandParameters = { 'verb' : ['do thing one'], 'requiredOptions' : ['doWhat='] }) def implementation(self, argDict, argList): return "doing thing one: argDict = %s, argList = %s" % (str(argDict), str(argList)) class doThingTwo(CommandInterface): def __init__(self): CommandInterface.__init__( self, commandParameters = { 'verb' : ['do thing two'], 'requiredOptions' : ['doWhat='] }) def implementation(self, argDict, argList): return "doing thing two: argDict = %s, argList = %s" % (str(argDict), str(argList)) # define the suite itself: from SamCommand.CommandInterfaceSuite import CommandInterfaceSuite class myInterfaceSuite(CommandInterfaceSuite): def buildImplementationModuleList(self): return [ self.__module__ ] def main(args): return myInterfaceSuite().dispatchCommand(args) if __name__ == "__main__": import sys sys.exit( main(sys.argv) )The command interfaces are now available through an instance of the CommandInterfaceSuite object:
# get a commandInterfaceSuite: suite = myInterfaceSuite() # do something with it suite.doThingOne(doWhat='do this thing here') suite.doThingTwo(doWhat='do something else')
Command Definition Syntax:
The syntax of the code to define a command is shown below. Note that only the verb is required; everything else is optional, with suitable defaults. When defining a command element that is a "list", you may use either "lists" or "tuples". The example here shows tuples; examples elsewhere in this document generally use "lists".
#!/usr/bin/env python from SamCommand.CommandDefinition import Command aCommandDefinition = { 'verb' : ( 'main verb form', 'legal synonyms', ...), 'requiredOptions' : ( 'requiredOpt1', 'requiredOpt2', ...), 'allowedOptions' : ( 'allowedOpt1', 'allowedOpt2', ...), 'allowedFlags' : ( 'allowedFlag1', 'allowedFlags2', ...), 'envFlags' : {'d' : 'DEBUG', 'v' : 'VERBOSE', 's' : 'SILENT'}, 'allowedArgCount' : Nargs, 'envOptions' : { 'envOpt1=' : 'ENV_VAR_1', 'envOpt2=' : 'ENV_VAR_2', ...}, 'requiredEnvOptions' : { 'envOptRequired' : 'ENV_VAR_REQUIRED', ...}, 'synonymousOptions' : { 'opt1' : ( 'synOpt1', 'synOpt2', ...), ...}, 'mutuallyExclusiveOptions' : ( ('opt1', 'opt2',...), ( 'opta', 'optb',...) ...), 'mustHaveOneOfOptions' : ( ('opt1', 'opt2', ...), ('opta', 'optb', ...) ...), 'restrictedValueOptions' : { 'opt' : ( 'valOptA', 'valOptB', ...), ...}, 'restrictedTypeOptions' : { 'opt' : WhatType,... }, 'incompatibleOptionCombinations' : [ { 'oldOptions' : [ 'oldOpt1', 'oldOpt2' ], 'newOptions' : [ 'newOpt1', 'newOpt2' ] }, { 'funOptions' : [ 'funThing1', 'funThing2', 'funThing3' ], 'dullOptions' : [ 'dullStuff' ] } ], 'importModule' : 'nameOfModuleContainingCode', 'dispatchHandler' : 'nameOfRoutineToHandleDispatch', 'description' : { 'opt' : 'description of opt', ...}, 'isDeprecated' : 0, 'apiImport' : "from sam import sam", 'apiObjectName' : "sam", 'apiDispatchMethod' : "mainVerbForm", 'apiReturns' : "list of strings", 'apiHelpText' : None, 'apiObjectsThatReplaceOptions' : { 'keyword1' : ['option1', 'option2'], 'keyword2' : ['envOpt1',], }, 'additionalAttr1' : additionalValue1, 'additionalAttr2' : additionalValue2,... } aCommand = Command(aCommandDefinition)
Command Definition Elements:
verb |
A list of strings defining the fundamental command form and any legal
synonyms. Do not include the shell command (e.g., 'sam' or 'sam_admin').
Examples: createDef = Command( {'verb' : [ 'create project definition', 'define project', 'create dataset definition' ] ...}) createDset = Command( {'verb' : [ 'create dataset', 'create project snapshot', 'create snapshot' ]...}) |
requiredOptions |
A list of (long) options which must appear on the command line (unless it
is listed also as a requiredEnvOption -- in which case,
either the option must appear on the commandline OR the associated environmental variable
must be set). Do not
include the initial '--'. A trailing '=' implies that the long option requires a value.
Examples: 'requiredOptions' : ( 'group=' ) 'requiredOptions' : ( 'interactive' ) 'requiredOptions' : ( 'family=', 'appname=', 'version=' )In the first case, the user would need to include --group=someGroupName on the command line or the command would not be considered legal. In the second case, the user would need to include --interactive. In the third case, all three options would need to be entered on the command line. |
allowedOptions |
A list of all (long) options which may appear on the command line. (You do not have
to include which have already been listed as 'requiredOptions'). Do not include the
initial '--'. A trailing '=' implies that the long option requires a value.
Examples: 'allowedOptions' : ( 'groups', 'disks', 'projects', 'fsman', 'all', 'files=' ) 'allowedOptions' : ( 'job=', 'project=', 'all-jobs' ) |
allowedFlags |
A list of all (short) options (i.e., flags) which may appear on the command line.
Do not include the initial '-'. A trailing ':' implies that the short option takes a value.
Examples: 'allowedFlags' : ( 'd', 'i', 'v', 'o:' ) |
envFlags |
A dictionary mapping an allowedFlag to an environmental variable name. If the flag
is not specified on the command line, the environmental variable will be used to determine
the boolean value of the flag.
Examples: 'envFlags' : {'s' : 'SAM_PYAPI_RETRY_SILENTLY'} |
allowedArgCount |
An integer representing the number of arguments (non-options) allowed on the
command line. If allowedArgCount is not specified, the default is 0 (no arguments
allowed). The integer -1 may be used to specify that "one or more" arguments are required.
Examples: Command( { ... 'allowedArgCount' : 2, 'description' : { 'args' : "<input> <output>" } ...}) Command( { ... 'allowedArgCount' : -1, 'description' : { 'args' : "list of tape volumes" } ...}) |
envOptions |
A dictionary whose keys option names, and whose values are environmental variable
names associated with this option. The commandline will always override the
environment.
Examples: Command( { ... 'envOptions' : { 'home=' : HOME, 'path=' : PATH, }, ... })In this case, if "--home=<value>" is specified it will take precedence; otherwise, use the value from $HOME. (Ditto for path). If the option is not on the commandline, and the environmental variable is not set, the argument dictionary will not contain an entry for this option. |
requiredEnvOptions |
A dictionary (just like envOptions) -- but the value must be
set or the command will be deemed illegal. That is, either the
option must be specified on the commandline, or the environmental variable
must be set, in order for the command to parse as valid.
|
synonymousOptions |
A dictionary containing a table of synonyms. The keys to the dictionary are the
primary option names -- that is, the name by which the option is
known within the code, and the name by which the value will be passed back in the
argDict. The values are lists of synonyms -- which will be translated to the
primary value before return. You may omit the "=" on options which take a value,
as long as they have been previously listed in the list of 'allowedOptions' or
'requiredOptions' with the appropriate "=".
Examples:
Command({ ... 'allowedOptions' : [ 'data_file_name=', 'run_number=' ], 'synonymousOptions' : { 'data_file_name' : [ 'filename', 'file', ], 'run_number' : [ 'run', 'number' ] }, 'restrictedTypeOptions' : { 'run_number' : IntType } ... })In this example, the following commands are equivalent: $In all cases, the argDict passed back will include the entries: { 'data_file_name' : 'myFile.py', 'run_number' : 10 } |
mutuallyExclusiveOptions |
A list of sub-lists; each sub-list defines a set of options where only one
from the list may appear on the command line. The options must be listed as allowedOptions, allowedFlags,
or requiredOptions. You do not have to include the trailing '=' or ':' for options which require
values when defining mutually exclusive sets.
Examples: Command( { ... 'mutuallyExclusiveOptions' : ( ('apples', 'oranges', 'cherries'), ('dog', 'cat', 'hamster') ) ...}) Command( { ... 'mutuallyExclusiveOptions' : ( ('job', 'project', 'all-jobs') ) ...})In the first case, the user may select only one fruit, and/or only one animal. In the second case, the user may specify '--job' OR '--project' OR '--all-jobs'. |
mustHaveOneOfOptions |
A list of sub-lists; each sub-list defines a set of options where the user must
specify (at least) one from the list. The options must be listed as allowedOptions, allowedFlags, or
requiredOptions. You do not have to include the trailing '=' or ':' for options which require
values when defining "must have one-of" sets.
Examples: Command( { ... 'mustHaveOneOfOptions' : ( ('apples', 'oranges', 'cherries'), ('dog', 'cat', 'hamster') ) ...}) Command( { ... 'mustHaveOneOfOptions' : ( ('job', 'project', 'all-jobs') ) ...})In the first case, the user must select at least one fruit and at least one one animal. In the second case, the user must specify at least one of '--job', '--project', and '--all-jobs'. |
restrictedValueOptions |
A dictionary of options which are restricted to a certain set of known values. The keys
of the dictionary are the names of the options. The values are the list of known
restricted values.
The options must be listed as allowedOptions, allowedFlags, or requiredOptions.
You do not have to include the trailing '=' or ':' for options which require values when
defining restrictedValues.
Examples: Command( { ... 'restrictedValueOptions' : { 'policy' : ( 'DEFAULT', 'LRU', 'FIFO', 'RANDOM' ), 'type' : ( 'datafile', 'run' ) } ...}) |
restrictedTypeOptions |
A dictionary of options which are restricted to a certain type. The keys of the dictionary
are the names of the options. The values are the required data type of this keyword, either
a native python data type (string, int, long, float), or an instance of a class. The parser
will convert the input arguments to the specified data type. The default data type if none
is specified is StringType.
Examples: Command( { ... 'restrictedTypeOptions' : { 'sizeK' : types.LongType, 'sizeG' : types.IntType, 'pi' : types.FloatType, 'metadata' : SamDataFile.SamMetaData, 'AppFamily' : SamStruct.AppFamily, } ... }) |
incompatibleOptionCombinations |
A list of incompatible option combinations. Each element
in the list is a dictionary. Each key of the dictionary is a "column",
where the user may select "some" from columnA or "some" from columnB,
but not both. (The precise number is determined by other command
definition entities, such as "mutuallyExclusiveOptions" or
"requiredOptions", etc.).
Example: Command( { ... 'incompatibleOptionCombinations' : [ { 'oldFormatOptions' : [ 'dim', 'rpn', 'infix' ], 'newFormatOptions' : [ 'filename', 'size', 'defname', 'datatier', 'eventnum', 'runnum' ] }, { 'byMonthOptions' : [ 'byMonth', 'month=', ], 'byYearOptions' : [ 'byYear', 'year=', ], 'byDayOptions' : [ 'byDay', 'day=', ] } ] ...} )In this example, a user may enter options from the list of "oldFormatOptions" or from the list of "newFormatOptions", but may not mix the two formats. A user may enter from the "byMonthOptions", "byYearOptions", or "byDayOptions", but may not mix options from these groups. |
importModule |
The importModule can be one of the following:
Command( { ... 'importModule' : "SamAdminDispatchHandler" ...}) Command( { ... 'importModule' : "sam_file_client" ...}) Command( { ... 'importModule' : self.__class__.__module__ ...}) |
dispatchHandler |
You do not need to specify a dispatchHandler when writing a CommandInterface;
the implementation acts as the dispatch method.
The dispatch handler can be one of the following:
The dispatch handler, in its crudest form, looks like: def aDispatchHandler( argDict, argList ): try: # do something based on argDict and argList here return 0 except: # raise an exception if desired return 1where argDict and argList are the options, flags, and arguments after successfully parsing the user's command. The argDict contains a few "special" entries that do not come from the commandline, but rather are inserted by the CommandParser for your convenience. They are: argDict['command'] = command # the command object itself The dispatch handler should return a status value so that the main wrapper can call sys.exit() with the appropriate status! |
description |
A dictionary of descriptions (to be used in the on-line help) for the legal
options and arguments. The keys are the name of the option (or primary option,
in the case of synonyms), or the
special value 'args' for the description of arguments. The values are a
text string describing the option or the arguments.
You do not have to include the trailing '=' or ':' for options which require values when
defining the description.
Examples: aCommand = Command( { verb' : ('do this'), 'allowedArgCount' : 2, 'allowedOptions' : ( 'when=', 'how-many-times=', 'carefully' ), 'synonymousOptions' : { 'carefully' : ['with-great-care', 'slowly'] }, 'restrictedValueOptions' : { 'when' : ("today", "tomorrow", "yesterday") }, 'restrictedTypeOptions ' : { 'how-many-times' : IntType }, 'description' : { 'args' : "inFile outFile", 'carefully' : "take special care in doing it" 'when' : "when does it need to be done", 'how' : "how to do it" } } ) |
isDeprecated |
A boolean value which, when set, modifies the command documentation to
indicate that this command is deprecated (no longer actively maintained).
Example: theCommand = Command( {'verb' : ['do something old'], 'isDeprecated' : 1, ...} ) |
helpText |
A string containing additional help for this command; it is appended
to the end of the auto-generated syntactical help.
Example: helpText = "\nHere is additional information about how to use this command." + \ "\nIt can get very in-depth." theCommand = Command( {'verb' : ['do something'], 'helpText' : helpText ...}) |
seeAlso |
A list of strings containing URLs for additional information pertaining to
this particular command. It is appended automatically to the end of the
auto-generated syntactical help for this particular command (not the
entire suite).
Example: seeAlso = ["http://d0db.fnal.gov:/sam_common_pylib/CommandInterface.html"] theCommand = Command( {'verb' : ['do_something'], ... 'seeAlso' : seeAlso, } ) |
apiImport |
This is a documentation string informing the api user what needs to be done
in order to access the API interface (if any has been defined). At the
present time, it is not very smart; it is just text.
Example: apiImport = "from sam import sam" theCommand = Command( { 'verb' : ['do_somethning'], ... 'apiImport' : apiImport, }) |
apiObjectName |
This is a documentation string which is used to tell the user how to call the
implementation. Some implementations are in terms of objects (as in,
"object.doSomething") and others are in terms of functions (as in,
"doSomething"). If the apiObjectName is set to 'None', then we assume the
latter.
Example: theCommand = Command( { 'verb' : ['do something with an object method'], ... 'apiImport' : "from sam import sam", 'apiObjectName' : 'sam', } ) anotherCommand = Command( { 'verb' : ['do something with a function'], ... 'apiImport' : "import moduleDefiningFunction", 'apiObjectName' : None } )(Note that 'None' is the default, it does not need to be listed). |
apiDispatchMethod |
This is a documentation string which tells the user the name of the method or
function that they would use when writing a native API call to this routine
(rather than using the shell call). It should be the function or method
(i.e., the OBJECT, not its name).
Example: theCommand = Command( {'verb' : ['do something'], ... 'apiDispatchMethod' : moduleDefiningFunction.doSomething } ) |
apiReturns |
This is a documentation string that is used instead of the command helpText
(if set) to document the interfaced. If this is not set, the command helpText
will be used.
Example: theCommand = Command( {'verb' : ['do something'], ... 'apiHelpText' : """ This is the python interface to the doSomething command. NOTE that in the python interface, the object is returned. Any other words would go here. Otherwise, we could (and by default would) use the helpText that was set for the main command. """ } ) |
apiObjectsThatReplaceOptions |
On the command line, for which the CommandParser was originally written,
you cannot pass python objects; you can only pass strings. However, api
users can create objects and pass them instead of string representations
of these objects. For example, at the command line you might say
"---inputFile=nameOfFile", and the commandline implementation is "smart"
enough to open the named file and read whatever information it is expecting
from that file (e.g., nameOfFile.metadataDictionary). In the native API,
one may pass that metadataDictionary as a python object. Hence the
apiObjectsThatReplaceOptions.
Example: theCommand = Command( { 'verb' : ['process metadata'], 'requiredOptions' : ['inputFile='], 'apiOptionsThatReplaceObjects' : { 'metadata' : ['inputFile'] }, } ) |
Additional Attributes |
You may pass any other attributes within the dictionary that defines your command.
This provides a shorthand method for
passing additional information to the dispatchHandler. For example,
the following two code segments provide idential results:
aCommand = Command( {'verb' : ('run'), 'doingWhat' : 'running' } )and aCommand = Command( {'verb' : ('run') ) aCommand.doingWhat = 'running'NOTE: if the the command you are defining will be used in the context of a blessed command interface suite, you need to be careful with the data type of any additionalAttributes that you set. In general, it is only supported to set additionalAttributes to string values (as opposed to classes or instances). For example, the following will not work in the context of a blessed command interface suite: # this will NOT work if the suite is blessed: from somePlace import someThing aCommandInterface = CommandInterface(commandParameters = {'verb' : ['do something'], 'doingWhat' : someThing ...})Instead, use the following: # this will work if the suite is blessed: from somePlace import someThing aCommandInterface = CommandInterface(commandParameters = {'verb' : ['do something'], 'doingWhatModuleName' : 'somePlace', 'doingWhatImportName' : 'someThing' ...}) ... # inside the implementation of aCommandInterface: code = "from %s import %s as DOING_WHAT" % (self.doingWhatModuleName, self.doingWhatImportName) ... |
verifyArgs(argDict,argList) |
Designed to be overridden by derived classes that need additional
checking beyond syntactic correctnes of the parsed command object. Should return 0 if the command is not
legal, 1 if the command is legal. This routine is called
after all of the syntactic checking has been done.
Parsing of complications beyond those handled by the base Command class may be handled here. Examples: from SamCommand.CommandDefinition import Command, TRUE, FALSE class MyCommand(Command): def verifyArgs(self, argDict=None, argList=None): if argDict is None: argDict = self.getArgDict() if argList is None: argList = self.getArgList() # parent class verification: returnStatus = Command.verifyArgs(self, argDict, argList) # now our additional verification OK = TRUE BAD = FALSE # # special processing for complications: # if ( argDict.has_key('ABORT') ): returnStatus = returnStatus and BAD if ( argDict.has_key('forbiddenOption') ): self.storeProblemExplanation('The "forbidden" option is FORBIDDEN.') returnStatus = returnStatus and BAD if ( argDict.has_key('obsoleteOption') ): sys.stderr.write("The --obsoleteOption is no longer recommended.\n") sys.stderr.write("Please use --newOption instead.") argDict['newOption'] = argDict['obsoleteOption'] del argDict['obsoleteOption'] if ( argDict.has_key('sings') and argDict.has_key('dances') ): argDict['multiTalented'] = 'yes' self.setArgDict(argDict) return returnStatus |
getArgDict() |
Return the command's argDict as a dictionary.
Example: theCommand = commandSuite.parseArgs(sys.argv) argDict = theCommand.getArgDict() for option in argDict.keys(): setSomethingBasedOn(argDict[option]) |
getArgList() |
Return the command's argList as a list.
Example: theCommand = commandSuite.parseArgs(sys.argv) argList = theCommand.getArgList() for argument in argList: doSomethingTo(argument) |
appendHelpText(aString) |
Append additional help information to the syntactical help generated
by the base class.
Examples: class MyCommand(Command): def __init__(self, *args): Command.__init__(self, args) additionalHelp = """ Here is more help on the command. Why would you use this command? When? What is it good for? What information beyond syntax do you need for this command to be useful? """ self.appendHelpText(additionalHelp) |
printHelp() | Print the help text associated with this command. Example:
theCommand.printHelp() |
getHelpText() | Retrieve the help text associated with this command as a string object.
Example:
aMessage = theCommand.getHelpText() print aMessage |
dispatch() |
A routine which imports the specified dispatchHandler from the specified
importModule, and executes the dispatch handler code. The appropriate
exit status should be returned to the caller.
The base class code is shown below, since I can't think of any realistic examples of how it might be modified (but I'm sure somebody else will). Command.dispatch(self): #================================================== # Dispatch the command appropriately #================================================== def dispatch(self): returnStatus = 0 if ( type(self.importModule) == StringType ): importCode = 'import %s' % self.importModule exec importCode if ( type(self.dispatchHandler) == StringType ): if ( type(self.importModule) == StringType ): dispatchCode = \ 'returnStatus = %s.%s(self.getArgDict(), self.getArgList())' % \ (self.importModule, self.dispatchHandler) else: dispatchCode = \ 'returnStatus = %s(self.getArgDict(), self.getArgList())' % \ self.dispatchHandler exec dispatchCode elif ( type(self.dispatchHandler) == FunctionType ): returnStatus = self.dispatchHandler(self.getArgDict(), self.getArgList()) elif ( type(self.dispatchHandler) == MethodType ): returnStatus = self.dispatchHandler(self.getArgDict(), self.getArgList()) return returnStatus |