config.py

Go to the documentation of this file.
00001 """\
00002 @file config.py
00003 @brief Utility module for parsing and accessing the indra.xml config file.
00004 
00005 $LicenseInfo:firstyear=2006&license=mit$
00006 
00007 Copyright (c) 2006-2008, Linden Research, Inc.
00008 
00009 Permission is hereby granted, free of charge, to any person obtaining a copy
00010 of this software and associated documentation files (the "Software"), to deal
00011 in the Software without restriction, including without limitation the rights
00012 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00013 copies of the Software, and to permit persons to whom the Software is
00014 furnished to do so, subject to the following conditions:
00015 
00016 The above copyright notice and this permission notice shall be included in
00017 all copies or substantial portions of the Software.
00018 
00019 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00020 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00021 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00022 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00023 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00024 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00025 THE SOFTWARE.
00026 $/LicenseInfo$
00027 """
00028 
00029 import copy
00030 import os
00031 import traceback
00032 import time
00033 import types
00034 
00035 from os.path import dirname, getmtime, join, realpath
00036 from indra.base import llsd
00037 
00038 _g_config = None
00039 
00040 class IndraConfig(object):
00041     """
00042     IndraConfig loads a 'indra' xml configuration file
00043     and loads into memory.  This representation in memory
00044     can get updated to overwrite values or add new values.
00045 
00046     The xml configuration file is considered a live file and changes
00047     to the file are checked and reloaded periodically.  If a value had
00048     been overwritten via the update or set method, the loaded values
00049     from the file are ignored (the values from the update/set methods
00050     override)
00051     """
00052     def __init__(self, indra_config_file):
00053         self._indra_config_file = indra_config_file
00054         self._reload_check_interval = 30 # seconds
00055         self._last_check_time = 0
00056         self._last_mod_time = 0
00057 
00058         self._config_overrides = {}
00059         self._config_file_dict = {}
00060         self._combined_dict = {}
00061 
00062         self._load()
00063 
00064     def _load(self):
00065         if self._indra_config_file is None:
00066             return
00067 
00068         config_file = open(self._indra_config_file)
00069         self._config_file_dict = llsd.parse(config_file.read())
00070         self._combine_dictionaries()
00071         config_file.close()
00072 
00073         self._last_mod_time = self._get_last_modified_time()
00074         self._last_check_time = time.time() # now
00075 
00076     def _get_last_modified_time(self):
00077         """
00078         Returns the mtime (last modified time) of the config file,
00079         if such exists.
00080         """
00081         if self._indra_config_file is not None:
00082             return os.path.getmtime(self._indra_config_file)
00083 
00084         return 0
00085 
00086     def _combine_dictionaries(self):
00087         self._combined_dict = {}
00088         self._combined_dict.update(self._config_file_dict)
00089         self._combined_dict.update(self._config_overrides)
00090 
00091     def _reload_if_necessary(self):
00092         now = time.time()
00093 
00094         if (now - self._last_check_time) > self._reload_check_interval:
00095             self._last_check_time = now
00096             try:
00097                 modtime = self._get_last_modified_time()
00098                 if modtime > self._last_mod_time:
00099                     self._load()
00100             except OSError, e:
00101                 if e.errno == errno.ENOENT: # file not found
00102                     # someone messed with our internal state
00103                     # or removed the file
00104 
00105                     print 'WARNING: Configuration file has been removed ' + (self._indra_config_file)
00106                     print 'Disabling reloading of configuration file.'
00107 
00108                     traceback.print_exc()
00109 
00110                     self._indra_config_file = None
00111                     self._last_check_time = 0
00112                     self._last_mod_time = 0
00113                 else:
00114                     raise  # pass the exception along to the caller
00115 
00116     def __getitem__(self, key):
00117         self._reload_if_necessary()
00118 
00119         return self._combined_dict[key]
00120 
00121     def get(self, key, default = None):
00122         try:
00123             return self.__getitem__(key)
00124         except KeyError:
00125             return default
00126 
00127     def __setitem__(self, key, value):
00128         """
00129         Sets the value of the config setting of key to be newval
00130 
00131         Once any key/value pair is changed via the set method,
00132         that key/value pair will remain set with that value until
00133         change via the update or set method
00134         """
00135         self._config_overrides[key] = value
00136         self._combine_dictionaries()
00137 
00138     def set(self, key, newval):
00139         return self.__setitem__(key, newval)
00140 
00141     def update(self, new_conf):
00142         """
00143         Load an XML file and apply its map as overrides or additions
00144         to the existing config.  Update can be a file or a dict.
00145 
00146         Once any key/value pair is changed via the update method,
00147         that key/value pair will remain set with that value until
00148         change via the update or set method
00149         """
00150         if isinstance(new_conf, dict):
00151             overrides = new_conf
00152         else:
00153             # assuming that it is a filename
00154             config_file = open(new_conf)
00155             overrides = llsd.parse(config_file.read())
00156             config_file.close()
00157   
00158         self._config_overrides.update(overrides)
00159         self._combine_dictionaries()
00160 
00161     def as_dict(self):
00162         """
00163         Returns immutable copy of the IndraConfig as a dictionary
00164         """
00165         return copy.deepcopy(self._combined_dict)
00166 
00167 def load(indra_xml_file = None):
00168     global _g_config
00169 
00170     if indra_xml_file is None:
00171         ## going from:
00172         ## "/opt/linden/indra/lib/python/indra/base/config.py"
00173         ## to:
00174         ## "/opt/linden/etc/indra.xml"
00175         indra_xml_file = realpath(
00176             dirname(realpath(__file__)) + "../../../../../../etc/indra.xml")
00177 
00178     _g_config = IndraConfig(indra_xml_file)
00179 
00180 def dump(indra_xml_file, indra_cfg = None, update_in_mem=False):
00181     '''
00182     Dump config contents into a file
00183     Kindof reverse of load.
00184     Optionally takes a new config to dump.
00185     Does NOT update global config unless requested.
00186     '''
00187     global _g_config
00188 
00189     if not indra_cfg:
00190         if _g_config is None:
00191             return
00192 
00193         indra_cfg = _g_config.as_dict()
00194 
00195     if not indra_cfg:
00196         return
00197 
00198     config_file = open(indra_xml_file, 'w')
00199     _config_xml = llsd.format_xml(indra_cfg)
00200     config_file.write(_config_xml)
00201     config_file.close()
00202 
00203     if update_in_mem:
00204         update(indra_cfg)
00205 
00206 def update(new_conf):
00207     global _g_config
00208 
00209     if _g_config is None:
00210         # To keep with how this function behaved
00211         # previously, a call to update
00212         # before the global is defined
00213         # make a new global config which does not
00214         # load data from a file.
00215         _g_config = IndraConfig(None)
00216 
00217     return _g_config.update(new_conf)
00218 
00219 def get(key, default = None):
00220     global _g_config
00221 
00222     if _g_config is None:
00223         load()
00224 
00225     return _g_config.get(key, default)
00226 
00227 def set(key, newval):
00228     """
00229     Sets the value of the config setting of key to be newval
00230 
00231     Once any key/value pair is changed via the set method,
00232     that key/value pair will remain set with that value until
00233     change via the update or set method or program termination
00234     """
00235     global _g_config
00236 
00237     if _g_config is None:
00238         _g_config = IndraConfig(None)
00239 
00240     _g_config.set(key, newval)
00241 
00242 def get_config():
00243     global _g_config
00244     return _g_config

Generated on Fri May 16 08:31:53 2008 for SecondLife by  doxygen 1.5.5