Package kenozooid :: Package cli

Source Code for Package kenozooid.cli

  1  # 
  2  # Kenozooid - dive planning and analysis toolbox. 
  3  # 
  4  # Copyright (C) 2009-2019 by Artur Wroblewski <wrobell@riseup.net> 
  5  # 
  6  # This program is free software: you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation, either version 3 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, 
 12  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 14  # GNU General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 18  # 
 19   
 20  """ 
 21  Commmand line user interface. 
 22  """ 
 23   
 24  import os 
 25  import os.path 
 26  import argparse 
 27   
 28  from kenozooid.component import query, params, inject 
29 30 -class CLICommand(object):
31 """ 32 Kenozooid's command. 33 """ 34 description = '' 35 36 @classmethod
37 - def add_arguments(self, parser):
38 """ 39 Add a command arguments to command line parser. 40 41 :Parameters: 42 parser 43 Parser instance. 44 """
45
46 - def __call__(self, args):
47 """ 48 Execute Kenozooid command. 49 50 May raise ArgumentError exception to indicate wrong arguments. 51 52 :Parameters: 53 args 54 Command arguments. 55 """
56
57 58 59 -class ArgumentError(BaseException):
60 """ 61 Error to indicate incorrect Kenozooid command arguments. 62 """
63
64 65 66 -class NoCommandError(BaseException):
67 """ 68 No Kenozooid command specified. 69 70 :Attributes: 71 parser 72 Parser command for which error is raised. 73 """
74 - def __init__(self, parser):
75 self.parser = parser
76
77 78 79 -class UDDFInputAction(argparse.Action):
80 """ 81 Parse arguments being a list of UDDF input files with '-k' option per 82 input file. 83 """
84 - def __call__(self, parser, namespace, values, opt=None):
85 arg = getattr(namespace, self.dest) 86 if arg is None: 87 arg = [], [] 88 setattr(namespace, self.dest, arg) 89 90 def check(q, f): 91 if not hasattr(parser, '__no_f_check__') and not os.path.exists(f): 92 parser.error('File {} does not exists'.format(f))
93 94 if opt: 95 arg[0].append(None) 96 arg[1].append(None) 97 else: 98 i = 0 99 while i < len(values): 100 if arg[0] and (arg[0][-1], arg[1][-1]) == (None, None): 101 if i + 1 >= len(values): 102 parser.error('option -k: expected 2 arguments') 103 104 q, f = values[i], values[i + 1] 105 i += 2 106 check(q, f) 107 arg[0][-1] = q 108 arg[1][-1] = f 109 elif values[i] == '-k': 110 arg[0].append(None) 111 arg[1].append(None) 112 i += 1 113 else: 114 q, f = None, values[i] 115 i += 1 116 check(q, f) 117 arg[0].append(q) 118 arg[1].append(f) 119 assert len(arg[0]) == len(arg[1])
120
121 122 -def add_commands(parser, prefix=None, title=None):
123 """ 124 Find and add commands to the argument parser. 125 126 :Parameters: 127 parser 128 Argument parser (from argparse module). 129 prefix 130 Prefix of commands to add to argument parser. 131 title 132 Help title of commands. 133 """ 134 m_subp = parser.add_subparsers(dest='subcmd', title=title) 135 c_subp = None # current subparser 136 137 # find Kenozooid commands and sort them by their names 138 commands = sorted(query(CLICommand), key=lambda cls: params(cls)['name']) 139 140 for cls in commands: 141 p = params(cls) 142 name = p['name'] 143 master = p.get('master', False) 144 desc = cls.description 145 title = cls.title if master else None 146 147 if ' ' in name: 148 assert c_subp # no master command, no subcommand 149 cmd, subcmd = name.split() 150 p = c_subp.add_parser(subcmd, help=desc) 151 else: 152 cmd, subcmd = name, name 153 p = m_subp.add_parser(subcmd, help=desc) 154 if master: # add master command 155 c_subp = p.add_subparsers(dest='subcmd', title=title) 156 else: 157 c_subp = None 158 159 p.set_defaults(cmd=cmd, parser=p) 160 cls.add_arguments(p)
161
162 163 -def add_master_command(name, title, desc):
164 """ 165 Add master command. 166 167 The purpose of master command is to 168 169 - group other commands as sub-commands, i.e. 'dive' master command for 170 'list' and 'add' sub-commands means there are 'dive list' and 'dive 171 add' commands. 172 - provide help title and generalized help description of groupped 173 sub-commands 174 175 :Parameters: 176 name 177 Command name. 178 title 179 Command help title. 180 desc 181 Command description. 182 """ 183 184 @inject(CLICommand, name=name, master=True) 185 class Command(object): 186 187 title = None 188 description = desc 189 190 @classmethod 191 def add_arguments(self, parser): 192 pass
193 194 def __call__(self, args): 195 raise NoCommandError(args.parser) 196 197 Command.title = title 198 199 return Command 200
201 202 -def add_uddf_input(parser):
203 """ 204 Add list of UDDF files as input argument to a parser. 205 206 The function adds several options and arguments 207 208 - `-n` is added to fetch dives by their number 209 - multiple UDDF files can be specified 210 - each file can be preceded with `-k` option to indicate which dives 211 should be fetched from a file 212 213 The `-k` and `-n` options are glued with 'and' operator - use only one 214 of them if confused. 215 216 :Parameters: 217 parser 218 ``argparse`` library parser. 219 """ 220 parser.add_argument('-n', 221 dest='dives', 222 help='fetch dives with their number (i.e. 40-42,45 are dives' 223 ' with dive number 40, 41, 42 and 45)') 224 parser.add_argument('-k', 225 dest='input', 226 nargs=0, 227 action=UDDFInputAction, 228 help=argparse.SUPPRESS) 229 parser.add_argument('input', 230 nargs='+', 231 action=UDDFInputAction, 232 metavar='[-k dives] input', 233 help='dives from specified UDDF file (i.e. 1-3,6 is dive' 234 ' 1, 2, 3, and 6 from a file, all by default)') 235 parser.add_argument('input', 236 nargs=argparse.REMAINDER, 237 action=UDDFInputAction, 238 help=argparse.SUPPRESS)
239 240 241 # vim: sw=4:et:ai 242