1#!/usr/bin/env python 2# This Source Code Form is subject to the terms of the Mozilla Public 3# License, v. 2.0. If a copy of the MPL was not distributed with this file, 4# You can obtain one at http://mozilla.org/MPL/2.0/. 5 6""" 7Mozilla universal manifest parser 8""" 9 10from optparse import OptionParser 11import os 12import sys 13 14from .manifestparser import ( 15 convert, 16 ManifestParser, 17) 18 19 20class ParserError(Exception): 21 """error for exceptions while parsing the command line""" 22 23 24def parse_args(_args): 25 """ 26 parse and return: 27 --keys=value (or --key value) 28 -tags 29 args 30 """ 31 32 # return values 33 _dict = {} 34 tags = [] 35 args = [] 36 37 # parse the arguments 38 key = None 39 for arg in _args: 40 if arg.startswith('---'): 41 raise ParserError("arguments should start with '-' or '--' only") 42 elif arg.startswith('--'): 43 if key: 44 raise ParserError("Key %s still open" % key) 45 key = arg[2:] 46 if '=' in key: 47 key, value = key.split('=', 1) 48 _dict[key] = value 49 key = None 50 continue 51 elif arg.startswith('-'): 52 if key: 53 raise ParserError("Key %s still open" % key) 54 tags.append(arg[1:]) 55 continue 56 else: 57 if key: 58 _dict[key] = arg 59 continue 60 args.append(arg) 61 62 # return values 63 return (_dict, tags, args) 64 65 66class CLICommand(object): 67 usage = '%prog [options] command' 68 69 def __init__(self, parser): 70 self._parser = parser # master parser 71 72 def parser(self): 73 return OptionParser(usage=self.usage, description=self.__doc__, 74 add_help_option=False) 75 76 77class Copy(CLICommand): 78 usage = '%prog [options] copy manifest directory -tag1 -tag2 --key1=value1 --key2=value2 ...' 79 80 def __call__(self, options, args): 81 # parse the arguments 82 try: 83 kwargs, tags, args = parse_args(args) 84 except ParserError, e: 85 self._parser.error(e.message) 86 87 # make sure we have some manifests, otherwise it will 88 # be quite boring 89 if not len(args) == 2: 90 HelpCLI(self._parser)(options, ['copy']) 91 return 92 93 # read the manifests 94 # TODO: should probably ensure these exist here 95 manifests = ManifestParser() 96 manifests.read(args[0]) 97 98 # print the resultant query 99 manifests.copy(args[1], None, *tags, **kwargs) 100 101 102class CreateCLI(CLICommand): 103 """ 104 create a manifest from a list of directories 105 """ 106 usage = '%prog [options] create directory <directory> <...>' 107 108 def parser(self): 109 parser = CLICommand.parser(self) 110 parser.add_option('-p', '--pattern', dest='pattern', 111 help="glob pattern for files") 112 parser.add_option('-i', '--ignore', dest='ignore', 113 default=[], action='append', 114 help='directories to ignore') 115 parser.add_option('-w', '--in-place', dest='in_place', 116 help='Write .ini files in place; filename to write to') 117 return parser 118 119 def __call__(self, _options, args): 120 parser = self.parser() 121 options, args = parser.parse_args(args) 122 123 # need some directories 124 if not len(args): 125 parser.print_usage() 126 return 127 128 # add the directories to the manifest 129 for arg in args: 130 assert os.path.exists(arg) 131 assert os.path.isdir(arg) 132 manifest = convert(args, pattern=options.pattern, ignore=options.ignore, 133 write=options.in_place) 134 if manifest: 135 print manifest 136 137 138class WriteCLI(CLICommand): 139 """ 140 write a manifest based on a query 141 """ 142 usage = '%prog [options] write manifest <manifest> -tag1 -tag2 --key1=value1 --key2=value2 ...' 143 144 def __call__(self, options, args): 145 146 # parse the arguments 147 try: 148 kwargs, tags, args = parse_args(args) 149 except ParserError, e: 150 self._parser.error(e.message) 151 152 # make sure we have some manifests, otherwise it will 153 # be quite boring 154 if not args: 155 HelpCLI(self._parser)(options, ['write']) 156 return 157 158 # read the manifests 159 # TODO: should probably ensure these exist here 160 manifests = ManifestParser() 161 manifests.read(*args) 162 163 # print the resultant query 164 manifests.write(global_tags=tags, global_kwargs=kwargs) 165 166 167class HelpCLI(CLICommand): 168 """ 169 get help on a command 170 """ 171 usage = '%prog [options] help [command]' 172 173 def __call__(self, options, args): 174 if len(args) == 1 and args[0] in commands: 175 commands[args[0]](self._parser).parser().print_help() 176 else: 177 self._parser.print_help() 178 print '\nCommands:' 179 for command in sorted(commands): 180 print ' %s : %s' % (command, commands[command].__doc__.strip()) 181 182 183class UpdateCLI(CLICommand): 184 """ 185 update the tests as listed in a manifest from a directory 186 """ 187 usage = '%prog [options] update manifest directory -tag1 -tag2 --key1=value1 --key2=value2 ...' 188 189 def __call__(self, options, args): 190 # parse the arguments 191 try: 192 kwargs, tags, args = parse_args(args) 193 except ParserError, e: 194 self._parser.error(e.message) 195 196 # make sure we have some manifests, otherwise it will 197 # be quite boring 198 if not len(args) == 2: 199 HelpCLI(self._parser)(options, ['update']) 200 return 201 202 # read the manifests 203 # TODO: should probably ensure these exist here 204 manifests = ManifestParser() 205 manifests.read(args[0]) 206 207 # print the resultant query 208 manifests.update(args[1], None, *tags, **kwargs) 209 210 211# command -> class mapping 212commands = {'create': CreateCLI, 213 'help': HelpCLI, 214 'update': UpdateCLI, 215 'write': WriteCLI} 216 217 218def main(args=sys.argv[1:]): 219 """console_script entry point""" 220 221 # set up an option parser 222 usage = '%prog [options] [command] ...' 223 description = "%s. Use `help` to display commands" % __doc__.strip() 224 parser = OptionParser(usage=usage, description=description) 225 parser.add_option('-s', '--strict', dest='strict', 226 action='store_true', default=False, 227 help='adhere strictly to errors') 228 parser.disable_interspersed_args() 229 230 options, args = parser.parse_args(args) 231 232 if not args: 233 HelpCLI(parser)(options, args) 234 parser.exit() 235 236 # get the command 237 command = args[0] 238 if command not in commands: 239 parser.error("Command must be one of %s (you gave '%s')" % 240 (', '.join(sorted(commands.keys())), command)) 241 242 handler = commands[command](parser) 243 handler(options, args[1:]) 244 245if __name__ == '__main__': 246 main() 247