Skip to content

Instantly share code, notes, and snippets.

@vineethguna
Created February 8, 2015 17:24
Show Gist options
  • Save vineethguna/d72a8f071a783de2d7ca to your computer and use it in GitHub Desktop.
Save vineethguna/d72a8f071a783de2d7ca to your computer and use it in GitHub Desktop.
Command Line Tool Design Pattern
__author__ = 'Vineeth'
'''
This tutorial code block lets you know about a design pattern for command line interface tools
To illustrate i will take an example. The command line tool consists of a main command and sub commands
Each command and sub command contains of switches or arguments
I will take the main command name as "main_command"
And sub commands as "sub_command_1", "sub_command_2", "sub_command_3" so on
This code block uses argparse library in python to achieve the design pattern
'''
import argparse
# This class definition contains the Main command functions
class MainCommand(object):
def __init__(self):
self.arg_parser = argparse.ArgumentParser(description='This is the main command')
self._add_switches()
self.subparsers = self.arg_parser.add_subparsers(dest='command', help='List of commands')
# This method is used to add sub commands switches or arguments to the main command
def add_subparsers(self, sub_commands_map):
for sub_command in sub_commands_map:
sub_command_details = sub_commands_map[sub_command]
new_sub_command_parser = self.subparsers.add_parser(sub_command, help=sub_command_details['desc'])
if hasattr(sub_command_details['class'], 'add_switches'):
sub_command_details['class'].add_switches(new_sub_command_parser)
def parse_args(self):
return self.arg_parser.parse_args()
# The below method is used to add switches or arguments to the main command
def _add_switches(self):
self.arg_parser.add_argument('-a', dest='arg1', help='This is a argument for main command and "a" is switch')
self.arg_parser.add_argument('-b', dest='arg2', help='This is a argument for main command and "b" is switch')
# You can add as many arguments as you want
'''
Sub command are constructed in such a way that, each sub command represents a class
Each class should follow a pattern
The pattern is each class should have a the below methods which are compulsory
set_context_from_args() - This method will try to take the switches or arguments data and try to set the context
validate() - This method is used for validation of the received switches or aguments data
execute() - This is the main method where you need to write the logic for the sub command
add_switches() - This is a static method. This method is called by main command class to add sub parser for each
sub command. All the switches or arguments for the sub command needs to be configured in this method
'''
class SubCommand1(object):
def __init__(self):
self.sub_arg_1 = None
self.sub_arg_2 = None
def set_context_from_args(self, args):
self.validate(args)
self.sub_arg_1 = args.sub_cmd_1_arg_1
self.sub_arg_1 = args.sub_cmd_1_arg_2
def validate(self, args):
# Do your validation here
pass
def execute(self):
# Logic to be executed for this sub command
pass
@staticmethod
def add_switches(sub_parser):
sub_parser.add_argument('-a', dest='sub_cmd_1_arg_1',
help='This is a argument for main command and "a" is switch')
sub_parser.add_argument('-b', dest='sub_cmd_1_arg_2',
help='This is a argument for main command and "b" is switch')
# You can add as many arguments as you want
class SubCommand2(object):
def __init__(self):
self.sub_arg_1 = None
self.sub_arg_2 = None
def set_context_from_args(self, args):
self.validate(args)
self.sub_arg_1 = args.sub_cmd_2_arg_1
self.sub_arg_1 = args.sub_cmd_2_arg_2
def validate(self, args):
# Do your validation here
pass
def execute(self):
# Logic to be executed for this sub command
pass
@staticmethod
def add_switches(sub_parser):
sub_parser.add_argument('-a', dest='sub_cmd_2_arg_1',
help='This is a argument for main command and "a" is switch')
sub_parser.add_argument('-b', dest='sub_cmd_2_arg_2',
help='This is a argument for main command and "b" is switch')
# You can add as many arguments as you want
if __name__ == '__main__':
'''
Define a command dict to define the sub commands and their respective classes
The keys in the dict are name of sub commands
The value is again a dict with two keys
One key is 'class' which defines the class for the sub command
The other is 'desc' which defines the description of the sub command
So adding a new sub command is very easy as you just need to define a new key in the below dict with its
class and description details, and implement the class with the above described pattern
'''
sub_command_map = {
'sub_command_1': {'class': SubCommand1, 'desc': 'This is sub command 1'},
'sub_command_2': {'class': SubCommand2, 'desc': 'This is sub command 2'}
}
# Construct the main command
main_command = MainCommand()
# Add sub commands
main_command.add_subparsers(sub_command_map)
# parse the arguments and switches of main command as well as sub command
args = main_command.parse_args()
# set context for the passed sub command
sub_command = sub_command_map[args.command]['class'](args) # args.command contains the name of the sub command
# call the execute function of the sub command which contains the logic
sub_command.execute()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment