fancy_getopt.py (9222B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\stdlib\distutils\fancy_getopt.py 2 __revision__ = '$Id: fancy_getopt.py 82110 2010-06-20 13:38:51Z kristjan.jonsson $' 3 import sys 4 import string 5 import re 6 import getopt 7 from distutils.errors import DistutilsGetoptError, DistutilsArgError 8 longopt_pat = '[a-zA-Z](?:[a-zA-Z0-9-]*)' 9 longopt_re = re.compile('^%s$' % longopt_pat) 10 neg_alias_re = re.compile('^(%s)=!(%s)$' % (longopt_pat, longopt_pat)) 11 longopt_xlate = string.maketrans('-', '_') 12 13 class FancyGetopt(): 14 15 def __init__(self, option_table = None): 16 self.option_table = option_table 17 self.option_index = {} 18 if self.option_table: 19 self._build_index() 20 self.alias = {} 21 self.negative_alias = {} 22 self.short_opts = [] 23 self.long_opts = [] 24 self.short2long = {} 25 self.attr_name = {} 26 self.takes_arg = {} 27 self.option_order = [] 28 29 def _build_index(self): 30 self.option_index.clear() 31 for option in self.option_table: 32 self.option_index[option[0]] = option 33 34 def set_option_table(self, option_table): 35 self.option_table = option_table 36 self._build_index() 37 38 def add_option(self, long_option, short_option = None, help_string = None): 39 if long_option in self.option_index: 40 raise DistutilsGetoptError, "option conflict: already an option '%s'" % long_option 41 else: 42 option = (long_option, short_option, help_string) 43 self.option_table.append(option) 44 self.option_index[long_option] = option 45 46 def has_option(self, long_option): 47 return long_option in self.option_index 48 49 def get_attr_name(self, long_option): 50 return string.translate(long_option, longopt_xlate) 51 52 def _check_alias_dict(self, aliases, what): 53 for alias, opt in aliases.items(): 54 if alias not in self.option_index: 55 raise DistutilsGetoptError, "invalid %s '%s': option '%s' not defined" % (what, alias, alias) 56 if opt not in self.option_index: 57 raise DistutilsGetoptError, "invalid %s '%s': aliased option '%s' not defined" % (what, alias, opt) 58 59 def set_aliases(self, alias): 60 self._check_alias_dict(alias, 'alias') 61 self.alias = alias 62 63 def set_negative_aliases(self, negative_alias): 64 self._check_alias_dict(negative_alias, 'negative alias') 65 self.negative_alias = negative_alias 66 67 def _grok_option_table(self): 68 self.long_opts = [] 69 self.short_opts = [] 70 self.short2long.clear() 71 self.repeat = {} 72 for option in self.option_table: 73 if len(option) == 3: 74 long, short, help = option 75 repeat = 0 76 elif len(option) == 4: 77 long, short, help, repeat = option 78 else: 79 raise ValueError, 'invalid option tuple: %r' % (option,) 80 if not isinstance(long, str) or len(long) < 2: 81 raise DistutilsGetoptError, "invalid long option '%s': must be a string of length >= 2" % long 82 if not (short is None or isinstance(short, str) and len(short) == 1): 83 raise DistutilsGetoptError, "invalid short option '%s': must a single character or None" % short 84 self.repeat[long] = repeat 85 self.long_opts.append(long) 86 if long[-1] == '=': 87 if short: 88 short = short + ':' 89 long = long[0:-1] 90 self.takes_arg[long] = 1 91 else: 92 alias_to = self.negative_alias.get(long) 93 if alias_to is not None: 94 if self.takes_arg[alias_to]: 95 raise DistutilsGetoptError, "invalid negative alias '%s': aliased option '%s' takes a value" % (long, alias_to) 96 self.long_opts[-1] = long 97 self.takes_arg[long] = 0 98 else: 99 self.takes_arg[long] = 0 100 alias_to = self.alias.get(long) 101 if alias_to is not None: 102 if self.takes_arg[long] != self.takes_arg[alias_to]: 103 raise DistutilsGetoptError, "invalid alias '%s': inconsistent with aliased option '%s' (one of them takes a value, the other doesn't" % (long, alias_to) 104 if not longopt_re.match(long): 105 raise DistutilsGetoptError, ("invalid long option name '%s' " + '(must be letters, numbers, hyphens only') % long 106 self.attr_name[long] = self.get_attr_name(long) 107 if short: 108 self.short_opts.append(short) 109 self.short2long[short[0]] = long 110 111 def getopt(self, args = None, object = None): 112 if args is None: 113 args = sys.argv[1:] 114 if object is None: 115 object = OptionDummy() 116 created_object = 1 117 else: 118 created_object = 0 119 self._grok_option_table() 120 short_opts = string.join(self.short_opts) 121 try: 122 opts, args = getopt.getopt(args, short_opts, self.long_opts) 123 except getopt.error as msg: 124 raise DistutilsArgError, msg 125 126 for opt, val in opts: 127 if len(opt) == 2 and opt[0] == '-': 128 opt = self.short2long[opt[1]] 129 else: 130 opt = opt[2:] 131 alias = self.alias.get(opt) 132 if alias: 133 opt = alias 134 if not self.takes_arg[opt]: 135 alias = self.negative_alias.get(opt) 136 if alias: 137 opt = alias 138 val = 0 139 else: 140 val = 1 141 attr = self.attr_name[opt] 142 if val and self.repeat.get(attr) is not None: 143 val = getattr(object, attr, 0) + 1 144 setattr(object, attr, val) 145 self.option_order.append((opt, val)) 146 147 if created_object: 148 return (args, object) 149 else: 150 return args 151 152 def get_option_order(self): 153 if self.option_order is None: 154 raise RuntimeError, "'getopt()' hasn't been called yet" 155 else: 156 return self.option_order 157 158 def generate_help(self, header = None): 159 max_opt = 0 160 for option in self.option_table: 161 long = option[0] 162 short = option[1] 163 l = len(long) 164 if long[-1] == '=': 165 l = l - 1 166 if short is not None: 167 l = l + 5 168 if l > max_opt: 169 max_opt = l 170 171 opt_width = max_opt + 2 + 2 + 2 172 line_width = 78 173 text_width = line_width - opt_width 174 big_indent = ' ' * opt_width 175 if header: 176 lines = [header] 177 else: 178 lines = ['Option summary:'] 179 for option in self.option_table: 180 long, short, help = option[:3] 181 text = wrap_text(help, text_width) 182 if long[-1] == '=': 183 long = long[0:-1] 184 if short is None: 185 if text: 186 lines.append(' --%-*s %s' % (max_opt, long, text[0])) 187 else: 188 lines.append(' --%-*s ' % (max_opt, long)) 189 else: 190 opt_names = '%s (-%s)' % (long, short) 191 if text: 192 lines.append(' --%-*s %s' % (max_opt, opt_names, text[0])) 193 else: 194 lines.append(' --%-*s' % opt_names) 195 for l in text[1:]: 196 lines.append(big_indent + l) 197 198 return lines 199 200 def print_help(self, header = None, file = None): 201 if file is None: 202 file = sys.stdout 203 for line in self.generate_help(header): 204 file.write(line + '\n') 205 206 207 def fancy_getopt(options, negative_opt, object, args): 208 parser = FancyGetopt(options) 209 parser.set_negative_aliases(negative_opt) 210 return parser.getopt(args, object) 211 212 213 WS_TRANS = string.maketrans(string.whitespace, ' ' * len(string.whitespace)) 214 215 def wrap_text(text, width): 216 if text is None: 217 return [] 218 if len(text) <= width: 219 return [text] 220 text = string.expandtabs(text) 221 text = string.translate(text, WS_TRANS) 222 chunks = re.split('( +|-+)', text) 223 chunks = filter(None, chunks) 224 lines = [] 225 while chunks: 226 cur_line = [] 227 cur_len = 0 228 while chunks: 229 l = len(chunks[0]) 230 if cur_len + l <= width: 231 cur_line.append(chunks[0]) 232 del chunks[0] 233 cur_len = cur_len + l 234 else: 235 if cur_line and cur_line[-1][0] == ' ': 236 del cur_line[-1] 237 break 238 239 if chunks: 240 if cur_len == 0: 241 cur_line.append(chunks[0][0:width]) 242 chunks[0] = chunks[0][width:] 243 if chunks[0][0] == ' ': 244 del chunks[0] 245 lines.append(string.join(cur_line, '')) 246 247 return lines 248 249 250 def translate_longopt(opt): 251 return string.translate(opt, longopt_xlate) 252 253 254 class OptionDummy(): 255 256 def __init__(self, options = []): 257 for opt in options: 258 setattr(self, opt, None)