| 0 | 1 #  Copyright (c) 2004 Gavin E. Crooks <gec@compbio.berkeley.edu> | 
|  | 2 # | 
|  | 3 #  This software is distributed under the MIT Open Source License. | 
|  | 4 #  <http://www.opensource.org/licenses/mit-license.html> | 
|  | 5 # | 
|  | 6 #  Permission is hereby granted, free of charge, to any person obtaining a | 
|  | 7 #  copy of this software and associated documentation files (the "Software"), | 
|  | 8 #  to deal in the Software without restriction, including without limitation | 
|  | 9 #  the rights to use, copy, modify, merge, publish, distribute, sublicense, | 
|  | 10 #  and/or sell copies of the Software, and to permit persons to whom the | 
|  | 11 #  Software is furnished to do so, subject to the following conditions: | 
|  | 12 # | 
|  | 13 #  The above copyright notice and this permission notice shall be included | 
|  | 14 #  in all copies or substantial portions of the Software. | 
|  | 15 # | 
|  | 16 #  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
|  | 17 #  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
|  | 18 #  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 
|  | 19 #  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
|  | 20 #  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
|  | 21 #  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
|  | 22 #  THE SOFTWARE. | 
|  | 23 # | 
|  | 24 | 
|  | 25 """Custom extensions to OptionParse for parsing command line options.""" | 
|  | 26 # FIXME: Docstring | 
|  | 27 | 
|  | 28 # TODO: Add profiling option | 
|  | 29 | 
|  | 30 # DeOptionParser : | 
|  | 31 # | 
|  | 32 #  http://docs.python.org/lib/module-optparse.html | 
|  | 33 # | 
|  | 34 # Random_options : | 
|  | 35 #   Set random generator and seed. Use options.random as | 
|  | 36 #   source of random numbers | 
|  | 37 # Copyright : | 
|  | 38 #   print copyright information | 
|  | 39 | 
|  | 40 # Documentation : | 
|  | 41 #   print extended document information | 
|  | 42 # | 
|  | 43 # Additional file_in and file_out types | 
|  | 44 | 
|  | 45 import sys | 
|  | 46 from copy import copy | 
|  | 47 from optparse import Option | 
|  | 48 from optparse import OptionParser | 
|  | 49 from optparse import IndentedHelpFormatter | 
|  | 50 from optparse import OptionValueError | 
|  | 51 import random | 
|  | 52 | 
|  | 53 | 
|  | 54 | 
|  | 55 def _copyright_callback(option, opt, value, parser): | 
|  | 56     if option or  opt or  value or parser: pass # Shut up lint checker | 
|  | 57     print parser.copyright | 
|  | 58     sys.exit() | 
|  | 59 | 
|  | 60 def _doc_callback(option, opt, value, parser): | 
|  | 61     if option or  opt or  value or parser: pass # Shut up lint checker | 
|  | 62     print parser.long_description | 
|  | 63     sys.exit() | 
|  | 64 | 
|  | 65 | 
|  | 66 class DeHelpFormatter(IndentedHelpFormatter) : | 
|  | 67     def __init__ (self, | 
|  | 68                   indent_increment=2, | 
|  | 69                   max_help_position=32, | 
|  | 70                   width=78, | 
|  | 71                   short_first=1): | 
|  | 72         IndentedHelpFormatter.__init__( | 
|  | 73             self, indent_increment, max_help_position, | 
|  | 74             width, short_first) | 
|  | 75 | 
|  | 76     def format_option_strings (self, option): | 
|  | 77         """Return a comma-separated list of option strings & metavariables.""" | 
|  | 78         if option.takes_value(): | 
|  | 79             metavar = option.metavar or option.dest.upper() | 
|  | 80             short_opts = option._short_opts | 
|  | 81             long_opts = [lopt + " " + metavar for lopt in option._long_opts] | 
|  | 82         else: | 
|  | 83             short_opts = option._short_opts | 
|  | 84             long_opts = option._long_opts | 
|  | 85 | 
|  | 86         if not short_opts : short_opts = ["  ",] | 
|  | 87 | 
|  | 88         if self.short_first: | 
|  | 89             opts = short_opts + long_opts | 
|  | 90         else: | 
|  | 91             opts = long_opts + short_opts | 
|  | 92 | 
|  | 93         return " ".join(opts) | 
|  | 94 | 
|  | 95 | 
|  | 96 | 
|  | 97 def _check_file_in(option, opt, value): | 
|  | 98     if option or  opt or  value : pass # Shut up lint checker | 
|  | 99     try: | 
|  | 100         return file(value, "r") | 
|  | 101     except IOError: | 
|  | 102         raise OptionValueError( | 
|  | 103             "option %s: cannot open file: %s" % (opt, value) ) | 
|  | 104 | 
|  | 105 def _check_file_out(option, opt, value): | 
|  | 106     if option or  opt or  value : pass # Shut up lint checker | 
|  | 107     try: | 
|  | 108         return file(value, "w+") | 
|  | 109     except IOError: | 
|  | 110         raise OptionValueError( | 
|  | 111             "option %s: cannot open file: %s" % (opt, value) ) | 
|  | 112 | 
|  | 113 def _check_boolean(option, opt, value) : | 
|  | 114     if option or  opt or  value : pass # Shut up lint checker | 
|  | 115     v = value.lower() | 
|  | 116     choices = {'no': False, 'false':False, '0': False, | 
|  | 117             'yes': True, 'true': True, '1':True } | 
|  | 118     try: | 
|  | 119         return choices[v] | 
|  | 120     except KeyError: | 
|  | 121         raise OptionValueError( | 
|  | 122             "option %s: invalid choice: '%s' " \ | 
|  | 123             "(choose from 'yes' or 'no', 'true' or 'false')" %  (opt, value)) | 
|  | 124 | 
|  | 125 def _check_dict(option, opt, value) : | 
|  | 126     if option or  opt or  value : pass # Shut up lint checker | 
|  | 127     v = value.lower() | 
|  | 128     choices = option.choices | 
|  | 129     try: | 
|  | 130         return choices[v] | 
|  | 131     except KeyError: | 
|  | 132         raise OptionValueError( | 
|  | 133             "option %s: invalid choice: '%s' " \ | 
|  | 134             "(choose from '%s')" %  (opt, value, "', '".join(choices))) | 
|  | 135 | 
|  | 136 | 
|  | 137 | 
|  | 138 class DeOption(Option): | 
|  | 139     TYPES = Option.TYPES + ("file_in","file_out", "boolean", "dict") | 
|  | 140     TYPE_CHECKER = copy(Option.TYPE_CHECKER) | 
|  | 141     TYPE_CHECKER["file_in"] = _check_file_in | 
|  | 142     TYPE_CHECKER["file_out"] = _check_file_out | 
|  | 143     TYPE_CHECKER["boolean"] = _check_boolean | 
|  | 144     TYPE_CHECKER["dict"] = _check_dict | 
|  | 145     choices = None | 
|  | 146 | 
|  | 147     def _new_check_choice(self): | 
|  | 148         if self.type == "dict": | 
|  | 149             if self.choices is None: | 
|  | 150                 raise OptionValueError( | 
|  | 151                     "must supply a dictionary of choices for type 'dict'") | 
|  | 152             elif not isinstance(self.choices, dict): | 
|  | 153                 raise OptionValueError( | 
|  | 154                     "choices must be a dictinary ('%s' supplied)" | 
|  | 155                     % str(type(self.choices)).split("'")[1]) | 
|  | 156             return | 
|  | 157         self._check_choice() | 
|  | 158 | 
|  | 159     # Have to override _check_choices so that we can parse | 
|  | 160     # a dict through to check_dict | 
|  | 161     CHECK_METHODS = Option.CHECK_METHODS | 
|  | 162     CHECK_METHODS[2] = _new_check_choice | 
|  | 163 | 
|  | 164 | 
|  | 165 | 
|  | 166 | 
|  | 167 | 
|  | 168 class DeOptionParser(OptionParser) : | 
|  | 169     def __init__(self, | 
|  | 170                 usage=None, | 
|  | 171                 option_list=None, | 
|  | 172                 option_class=DeOption, | 
|  | 173                 version=None, | 
|  | 174                 conflict_handler="error", | 
|  | 175                 description=None, | 
|  | 176                 long_description = None, | 
|  | 177                 formatter=DeHelpFormatter(), | 
|  | 178                 add_help_option=True, | 
|  | 179                 prog=None, | 
|  | 180                 copyright=None, | 
|  | 181                 add_verbose_options=True, | 
|  | 182                 add_random_options=False | 
|  | 183                 ): | 
|  | 184 | 
|  | 185         OptionParser.__init__(self, | 
|  | 186                               usage, | 
|  | 187                               option_list, | 
|  | 188                               option_class, | 
|  | 189                               version, | 
|  | 190                               conflict_handler, | 
|  | 191                               description, | 
|  | 192                               formatter, | 
|  | 193                               add_help_option, | 
|  | 194                               prog ) | 
|  | 195 | 
|  | 196         if long_description : | 
|  | 197             self.long_description = long_description | 
|  | 198             self.add_option("--doc", | 
|  | 199                             action="callback", | 
|  | 200                             callback=_doc_callback, | 
|  | 201                             help="Detailed documentation") | 
|  | 202 | 
|  | 203         if copyright : | 
|  | 204             self.copyright = copyright | 
|  | 205             self.add_option("--copyright", | 
|  | 206                             action="callback", | 
|  | 207                             callback=_copyright_callback, | 
|  | 208                             help="") | 
|  | 209 | 
|  | 210         if add_verbose_options : | 
|  | 211             self.add_option("-q", "--quite", | 
|  | 212                 action="store_false", | 
|  | 213                 dest="verbose", | 
|  | 214                 default=False, | 
|  | 215                 help="Run quietly (default)") | 
|  | 216 | 
|  | 217             self.add_option("-v", "--verbose", | 
|  | 218                 action="store_true", | 
|  | 219                 dest="verbose", | 
|  | 220                 default=False, | 
|  | 221                 help="Verbose output (Not quite)") | 
|  | 222 | 
|  | 223         self.random_options = False | 
|  | 224         if add_random_options : | 
|  | 225             self.random_options = True | 
|  | 226             self.add_option("--seed", | 
|  | 227                 action="store", | 
|  | 228                 type = "int", | 
|  | 229                 dest="random_seed", | 
|  | 230                 help="Initial seed for pseudo-random number generator. (default: System time)", | 
|  | 231                 metavar="INTEGER" ) | 
|  | 232 | 
|  | 233             self.add_option("--generator", | 
|  | 234                 action="store", | 
|  | 235                 dest="random_generator", | 
|  | 236                 default="MersenneTwister", | 
|  | 237                 help="Select MersenneTwister (default) or WichmannHill pseudo-random number generator", | 
|  | 238                 metavar="TYPE" ) | 
|  | 239 | 
|  | 240     def parse_args(self,args, values=None) : | 
|  | 241         (options, args) = OptionParser.parse_args(self, args, values) | 
|  | 242 | 
|  | 243         if self.random_options : | 
|  | 244             if options.random_generator is None or options.random_generator =="MersenneTwister" : | 
|  | 245                 r = random.Random() | 
|  | 246             elif options.random_generator == "WichmannHill" : | 
|  | 247                 r = random.WichmannHill() | 
|  | 248             else : | 
|  | 249                 self.error("Acceptible generators are MersenneTwister (default) or WichmannHill") | 
|  | 250             if options.random_seed : | 
|  | 251                 r.seed(options.random_seed) | 
|  | 252 | 
|  | 253             options.__dict__["random"] = r | 
|  | 254 | 
|  | 255 | 
|  | 256         return (options, args) | 
|  | 257 | 
|  | 258 |