scripts/settings.py
changeset 29 64b3e323210f
child 43 fdb9a6d839ae
equal deleted inserted replaced
28:22d872615893 29:64b3e323210f
       
     1 #!/usr/bin/python2.5
       
     2 #
       
     3 # Copyright 2008 the Melange authors.
       
     4 #
       
     5 # Licensed under the Apache License, Version 2.0 (the "License");
       
     6 # you may not use this file except in compliance with the License.
       
     7 # You may obtain a copy of the License at
       
     8 #
       
     9 #   http://www.apache.org/licenses/LICENSE-2.0
       
    10 #
       
    11 # Unless required by applicable law or agreed to in writing, software
       
    12 # distributed under the License is distributed on an "AS IS" BASIS,
       
    13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       
    14 # See the License for the specific language governing permissions and
       
    15 # limitations under the License.
       
    16 
       
    17 """Custom optparse OptionParser and functions for reading Python settings files.
       
    18 
       
    19   Option:  class derived from optparse.Option that adds a 'required' parameter
       
    20   OptionParser:  class derived from optparse.OptionParser for use with Option
       
    21 
       
    22   readPythonSettings():  interprets a valid Python file as a settings file
       
    23 """
       
    24 
       
    25 __authors__ = [
       
    26   # alphabetical order by last name, please
       
    27   '"Todd Larsen" <tlarsen@google.com>',
       
    28 ]
       
    29 
       
    30 
       
    31 import os
       
    32 import optparse
       
    33 import sys
       
    34 
       
    35 
       
    36 DEF_SETTINGS_FILE_DIR = "~"
       
    37 DEF_SETTINGS_FILE_NAME = '.soc_scripts_settings'
       
    38 
       
    39 
       
    40 class Error(Exception):
       
    41   """Base exception class for all exceptions in the settings module."""
       
    42   pass
       
    43 
       
    44 
       
    45 class Option(optparse.Option):
       
    46   """Class derived from optparse.Option that adds a 'required' parameter."""
       
    47 
       
    48   ATTRS = optparse.Option.ATTRS + ['required']
       
    49 
       
    50   def _check_required(self):
       
    51     """Insures that 'required' option can accept a value."""
       
    52     if self.required and (not self.takes_value()):
       
    53       raise optparse.OptionError(
       
    54           "'required' parameter set for option that does not take a value",
       
    55           self)
       
    56 
       
    57   # Make sure _check_required() is called from the constructor!
       
    58   CHECK_METHODS = optparse.Option.CHECK_METHODS + [_check_required]
       
    59 
       
    60   def process(self, opt, value, values, parser):
       
    61     optparse.Option.process(self, opt, value, values, parser)
       
    62     parser.option_seen[self] = 1
       
    63 
       
    64 
       
    65 class OptionParser(optparse.OptionParser):
       
    66   """Class derived from optparse.OptionParser for use with Option."""
       
    67 
       
    68   def _init_parsing_state(self):
       
    69     """Sets up dict to track options seen so far."""
       
    70     optparse.OptionParser._init_parsing_state(self)
       
    71     self.option_seen = {}
       
    72 
       
    73   def error(self, *args):
       
    74     """Convert errors reported by optparse.OptionParser to Error exceptions.
       
    75 
       
    76     Args:
       
    77       *args:  passed through to the Error exception __init__() constructor,
       
    78         usually a list of strings
       
    79 
       
    80     Raises:
       
    81       Error with the supplied *args
       
    82     """
       
    83     raise Error(*args)
       
    84 
       
    85   def check_values(self, values, args):
       
    86     """Checks to make sure all required=True options were supplied.
       
    87 
       
    88     Args:
       
    89       values, args:  passed through unchanged (see Returns:)
       
    90 
       
    91     Returns:
       
    92       (values, args) unchanged.
       
    93 
       
    94     Raises:
       
    95       Error if an option was not supplied that had required=True;  exception
       
    96       positional arguments are the error message strings.
       
    97     """
       
    98     errors = []
       
    99 
       
   100     for option in self.option_list:
       
   101       if (isinstance(option, Option)
       
   102           and option.required
       
   103           and (not self.option_seen.has_key(option))):
       
   104         errors.append(
       
   105             'required %s option not supplied' % option)
       
   106 
       
   107     if errors:
       
   108       self.error(*errors)
       
   109 
       
   110     return values, args
       
   111 
       
   112 
       
   113 def readPythonSettings(defaults={},  # {} OK since defaults is always copied
       
   114                        settings_dir=DEF_SETTINGS_FILE_DIR,
       
   115                        settings_file=DEF_SETTINGS_FILE_NAME):
       
   116   """Executes a Python-syntax settings file and returns the local variables.
       
   117 
       
   118   Args:
       
   119     defaults:  dict of default values to use when settings are not present
       
   120       in the settings file (or if no settings file is present at all);  this
       
   121       dict is *copied* and is not altered at all
       
   122     settings_dir:  optional directory containing settings_file
       
   123     settings_file:  optional settings file name found in settings_dir
       
   124 
       
   125   Returns:
       
   126     dict of setting name/value pairs (possibly including some values from the
       
   127     defaults parameter).  Since the settings file is full-fledged Python
       
   128     source, the values could be any valid Python object.
       
   129 
       
   130   Raises:
       
   131     Error if some error occurred parsing the present settings file;  exception
       
   132     positional arguments are the error message strings.
       
   133   """
       
   134   # do not let the original defaults be altered
       
   135   defaults = defaults.copy()
       
   136 
       
   137   # form absolute path to the settings file, expanding any environment
       
   138   # variables and "~", then removing excess . and .. path elements
       
   139   path = os.path.abspath(
       
   140       os.path.normpath(
       
   141           os.path.expanduser(
       
   142               os.path.expandvars(
       
   143                   os.path.join(settings_dir, settings_file)))))
       
   144 
       
   145   # empty dict to capture the local variables in the settings file
       
   146   settings_locals = {}
       
   147 
       
   148   try:
       
   149     # execute the Python source file and recover the local variables as settings
       
   150     execfile(path, {}, settings_locals)
       
   151   except IOError:
       
   152     # If the settings file is not present, there are no defaults.
       
   153     pass
       
   154   except Exception, error:
       
   155     # Other exceptions usually mean a faulty settings file.
       
   156     raise Error(
       
   157         'faulty settings file:',
       
   158         ('  %s: %s' % (error.__class__.__name__, str(error))),
       
   159         ('  %s' % path))
       
   160 
       
   161   # overwrite defaults copy with values from the (possibly empty) settings file
       
   162   defaults.update(settings_locals)
       
   163 
       
   164   return defaults