| 
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      |