Package jazzparser :: Package utils :: Module options
[hide private]
[frames] | no frames]

Source Code for Module jazzparser.utils.options

  1  """Framework for specifying multiple options to a module on the command line. 
  2   
  3  For modules like taggers and parsers, the options available will vary  
  4  depending on what component is selected. This framework allows a  
  5  specific component to list its available options and how they should  
  6  be interpreted. 
  7   
  8  This is one of my greater works of genius to be found in this codebase. 
  9  It's incredibly useful so often. 
 10   
 11  """ 
 12  """ 
 13  ============================== License ======================================== 
 14   Copyright (C) 2008, 2010-12 University of Edinburgh, Mark Granroth-Wilding 
 15    
 16   This file is part of The Jazz Parser. 
 17    
 18   The Jazz Parser is free software: you can redistribute it and/or modify 
 19   it under the terms of the GNU General Public License as published by 
 20   the Free Software Foundation, either version 3 of the License, or 
 21   (at your option) any later version. 
 22    
 23   The Jazz Parser is distributed in the hope that it will be useful, 
 24   but WITHOUT ANY WARRANTY; without even the implied warranty of 
 25   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 26   GNU General Public License for more details. 
 27    
 28   You should have received a copy of the GNU General Public License 
 29   along with The Jazz Parser.  If not, see <http://www.gnu.org/licenses/>. 
 30   
 31  ============================ End license ====================================== 
 32   
 33  """ 
 34  __author__ = "Mark Granroth-Wilding <mark.granroth-wilding@ed.ac.uk>"  
35 36 37 -class UnprocessedOptionValue(object):
38 """ 39 Simple wrapper for strings to mark that they haven't yet been 40 processed as option values. 41 """
42 - def __init__(self, val):
43 self.value = val
44
45 - def __str__(self): return self.value
46
47 -class ModuleOption(object):
48 """ 49 An option that can be specified on the command line and that is 50 specific to a certain modular component (e.g. parser, tagger). 51 52 Example use of a ModuleOption:: 53 ModuleOption('test', 54 lambda x: int(x), 55 help_text="A test option", 56 default=2, 57 usage="test=X, where X is an int") 58 59 This will accept integer values for the option called "test". If no 60 value is given, it will default to 2. 61 62 A filter function may be given which will be applied to the value during 63 option processing. If the filter raises an exception, the value will be 64 reported as invalid. 65 66 You may also specify multiple filters as a tuple of functions. Each will 67 be applied in order and the first value successfully returned will be 68 used. 69 70 """
71 - def __init__(self, name, filter=None, 72 help_text="No help text available", default=None, 73 usage=None, required=False):
74 self.name = name 75 if filter is not None: 76 self.filter = filter 77 else: 78 self.filter = None 79 self.help_text = help_text 80 self.default = default 81 self._usage = usage 82 self.required = required
83
84 - def _get_usage(self):
85 if self._usage is not None: 86 return self._usage 87 else: 88 return "%s=X" % self.name
89 usage = property(_get_usage) 90
91 - def get_value(self, options):
92 """ 93 Pulls the appropriate value out of a dictionary of options and 94 return it. 95 """ 96 if self.name not in options: 97 if self.required: 98 raise ModuleOptionError, "option '%s' is required, but was not specified" % self.name 99 # Not in the options: return the default value 100 return self.default 101 if type(options[self.name]) == UnprocessedOptionValue: 102 # The values have come from a string and not been processed 103 if self.filter is None: 104 # No processing to do 105 return options[self.name].value 106 elif type(self.filter) == tuple: 107 # Got a tuple of filters: try each in turn 108 errs = [] 109 for filt in self.filter: 110 try: 111 return filt(options[self.name].value) 112 except Exception, err: 113 errs.append(err) 114 # All filters failed 115 raise ModuleOptionError, "invalid value for option '%s': "\ 116 "%s" % (self.name, "; ".join([str(err) for err in errs])) 117 else: 118 try: 119 return self.filter(options[self.name].value) 120 except Exception, err: 121 raise ModuleOptionError, "invalid value for option "\ 122 "'%s': %s" % (self.name, err) 123 else: 124 # No preprocessing to do, just return it as it is 125 return options[self.name]
126 127 @staticmethod
128 - def process_option_string(optstr):
129 """ 130 Takes an option string in the format in which options should 131 be specified on the command line and returns a dictionary 132 ready to pass to the options themselves. 133 134 Module options must be in the format opt1=val1:opt2=val2:etc. 135 Later options override earlier ones. 136 137 C{optstr} may also be a list of strings. In this case, the 138 options in each string will be concatenated. This allows you 139 to take options from multiple CL options using optparse's 140 C{append} action. 141 142 If the optstr is None, or an empty list, returns an empty dict. 143 This allows you to pass in a value directly from an optparse 144 parser. 145 146 """ 147 if type(optstr) == list: 148 optstr = ":".join(optstr) 149 options = {} 150 if optstr is not None and len(optstr.strip()): 151 for optval in optstr.split(":"): 152 opt, __, val = optval.partition("=") 153 if len(val) == 0: 154 raise ModuleOptionError, "Module options must be in the "\ 155 "format opt1=val1:opt2=val2:etc. Got: %s" % optval 156 # We don't know how to process this string yet, so we 157 # mark it unprocessed and leave it to the option. 158 options[opt] = UnprocessedOptionValue(val) 159 return options
160 161 @staticmethod
162 - def process_option_dict(optdict, available_opts):
163 """ 164 Takes a dictionary of options, which may be from command-line 165 strings (via process_option_string) or internal, and a list 166 of allowed options and returns a dictionary of all the option 167 values. 168 """ 169 used_opts = [] 170 options = {} 171 for option in available_opts: 172 # Try getting a value from the dict for this option 173 options[option.name] = option.get_value(optdict) 174 used_opts.append(option.name) 175 # Check whether there were any more options we didn't use 176 for key in optdict.keys(): 177 if key not in used_opts: 178 raise ModuleOptionError, "'%s' is not a valid option." \ 179 % key 180 return options
181
182 -def options_help_text(options, intro=None):
183 """ 184 Produces a load of help text to output to the command line to 185 display the usage of all of the options in the list. 186 """ 187 if len(options) == 0: 188 return "This module has no options" 189 from jazzparser.utils.tableprint import pprint_table 190 from StringIO import StringIO 191 rows = [] 192 # Put required options first 193 for opt in [o for o in options if o.required]: 194 rows.append([opt.name, "%s (REQUIRED)" % opt.usage, opt.help_text]) 195 for opt in [o for o in options if not o.required]: 196 rows.append([opt.name, opt.usage, opt.help_text]) 197 output = StringIO() 198 # Print the options in a nice table 199 pprint_table(output, rows, separator="", 200 justs=[True,True,True], 201 widths=[None,35,40], 202 blank_row=True) 203 strout = output.getvalue() 204 output.close() 205 if intro is not None: 206 strout = "%s\n%s\n%s" % (intro, "="*len(intro), strout) 207 return strout
208
209 -class ModuleOptionError(Exception):
210 pass
211
212 ########### Filters 213 # These are functions that can be used as filters for option types. 214 -def file_option(value):
215 """ 216 A filter function for filenames of existing files. 217 Errors if the file doesn't exist. 218 219 """ 220 if value is None: 221 return None 222 import os 223 filename = os.path.abspath(value) 224 if not os.path.exists(filename): 225 raise ModuleOptionError, "the file %s does not exist" % filename 226 else: 227 return filename
228
229 -def new_file_option(value):
230 """ 231 A filter function for a new filename. 232 Doesn't require the file to exist, but errors if the directory 233 doesn't exist. 234 235 """ 236 if value is None: 237 return None 238 import os 239 filename = os.path.abspath(value) 240 dirname = os.path.dirname(filename) 241 if not os.path.exists(dirname): 242 raise ModuleOptionError, "the directory %s does not exist" % dirname 243 else: 244 return filename
245
246 -def zero_to_one_float(value):
247 """ 248 A filter function for floats that should lie between 0.0 and 1.0. 249 250 Accepts the range ends (0.0 and 1.0). Raises an errror for any incorrectly 251 formatted numbers or numbers outside this range. 252 253 This is useful for probabilities or ratios. 254 255 """ 256 if value is None: 257 return None 258 try: 259 value = float(value) 260 except ValueError: 261 raise ModuleOptionError, "not a float: %s" % value 262 if value < 0.0 or value > 1.0: 263 raise ModuleOptionError, "float not in range 0.0 - 1.0: %s" % value 264 return value
265
266 -def choose_from_dict(dic):
267 """ 268 Filter function constructor. Returns a filter function that will verify 269 that the filtered value is among the C{dic}'s keys and return the 270 corresponding value if it is. 271 272 """ 273 def _filter(value): 274 if value not in dic: 275 raise ModuleOptionError, "invalid option value: %s. Possible values "\ 276 "are: %s" % (value, ", ".join(dic.keys())) 277 return dic[value]
278 return _filter 279
280 -def choose_from_list(lst):
281 """ 282 Filter function constructor. 283 284 Returns a filter function that will verify that the value is one of those 285 in the list and then just return that value if it is. 286 287 """ 288 def _filter(value): 289 if value not in lst: 290 raise ModuleOptionError, "invalid option value: %s. Possible values "\ 291 "are: %s" % (value, ", ".join(lst)) 292 return value
293 return _filter 294