Created
February 8, 2015 17:24
-
-
Save vineethguna/d72a8f071a783de2d7ca to your computer and use it in GitHub Desktop.
Command Line Tool Design Pattern
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| __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