#!/usr/bin/python ############################################################################### #Programmers: # # Danil Dotsenko, dd@accentsolution.com # #Date: 11/18/2006 # #Filename: localConf.py # #Version: 0.4.2 # # # # # # Copyleft 2006 Danil Dotsenko # # # # This program is free software; you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU General Public License for more details. # # # # You should have received a copy of the GNU General Public License # # along with this program; if not, write to the Free Software # # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # ############################################################################### ''' This is a wrapper for ConfigParser-of-the-day. It started out as pyhton's own ConfigParser. But due to several issues with it (lowercasing sections, messing up order of the file) I switched to ConfigObj backend from http://www.voidspace.org.uk/python/configobj.html Jumping from backend to backend usually means changes to the applet, but, with this abstraction layer, we only have to change the functions in this file. The goal is to have the following stable functions: cfgObject([filename]) - makes object a ConfigObj, loading settings from file load(filename) - loads settings from file. Note. this MAY clean out previous contents. If you want to merge, load 2 separate files into dicts and merge. save(filename) set(section, option, [value]) - puts single pair or dict of values in INI. get([section], [option]) - returns dict of whole, section, or option string. Notes: - There is no "multi-homing" i.e. only one config file name is considered main at a time. - All errors and issues are hidden. If data is requested from non-existent section - value is None, not an Error - One unusual function is delayed_action([action], [argument]) When passed WITH arguments (see the function for allowed arguments), it sets an action to be performed later. When passed WITHOUT arguments, it performs the LAST requested action. This is very usefull for situations when a long list of IF's, but you want to delay the save until you are done with all of them. ''' import os.path import configobj class cfgObject: def __init__(self, fileName = None): """ __init__([fileName]) Creates the Config object. Without a file name, the config is initialized empty. With a file name, the config is populated by the settings from file. """ self.encoding = 'utf8' self.delayed = [] self.cfgObject = configobj.ConfigObj() if fileName: self.cfgObject = configobj.ConfigObj(fileName) def load(self, fileName): """ set_file(fileName) This should add (or, more likely REPLACE dependent on configobj library) the configs from a file to the current instance. """ if fileName: # safeguarding for None as fileName if os.path.exists(fileName): self.cfgObject = configobj.ConfigObj(fileName) return True return False def save(self, fileName=None): """ save([fileName]) Saves into the requested file, else into path in self.cfgObject.filename """ if fileName: self.cfgObject.filename = fileName self.cfgObject.write() def get(self,section=None,option=None): """ get([section], [option]) returns either the contents of option or None if option doesn't exist. Returns: - None if option doesn't exist. - dictionary object with all option=value pairs in the section if only section is given. - dictionary object with all option=value pairs in the whole file if nothing is given. - string object if option and section are given. """ answer = None if not section: # neither section, nor option is given. Return the contents of the whole ini as dictinary type with nested dictionaries for sections. answer = self.cfgObject.dict() # note! this returns DICTIONATY type elif not option: # this means only section is given. Return contents of the section as dictinary type. if self.cfgObject.__contains__(section): answer = dict(self.cfgObject[section]) # note! this returns DICTIONARY type elif self.cfgObject.__contains__(section) and self.cfgObject[section].__contains__(option): answer = self.cfgObject[section][option] # note! this usually returns a STRING # we will be nice and decode the most commonly occuring INI strings back into pure unicode. # this rutine will skip list (array) types and nested dictionaries. # if you use them in your settings, decode them manually. # Thus, we only decode 1st level Values that are strings, not Section or Option names. if type(answer) == type(''): #string try: answer = str(answer.decode('utf8')) # if this works, the underlying string was NOT a Unicode string # if it fails, the string was unicode and we need to return it back as such. except: answer = answer.decode('utf8') elif type(answer) == type({}): #dictionary for key in answer.keys(): if type(answer[key]) == type(''): #string try: answer[key] = str(answer[key].decode('utf8')) # if this works, the underlying string was NOT a Unicode string # if it fails, the string was unicode and we need to return it back as such. except: answer[key] = answer[key].decode('utf8') return answer def set(self,section,options,value=None): """ set(section,options,[value]) Unlike ConfigParser's set, we silently create sections and keys. - if option is Dict, value is not needed and we assume that option=value pairs are given and write those to ini object. - if option is String (Unicode), Value is required. """ # ConfigParser does not check what is given to it by Value. # It brakes on save and get(value) if it's true python Unicode # we'll convert all values to UTF8 format # Later, it would be good to find out if user is in utf8 or utf16. # We don't really care about those not in unicode locale, as those people probably don't exist anymore :) if type(section) == type(u''): # unicode try: section = section.encode(self.encoding) except: pass # this trick makes all function-use cases use the same algorythm. if not (type(options) == type({})): options = {options:value} for pair in options.items(): key = pair[0] value = pair[1] if (type(key) == type(u'')) or (type(value) == type(u'')): # unicode try: key = key.encode(self.encoding) value = value.encode(self.encoding) except: pass # silently creating sections if needed. if not self.cfgObject.__contains__(section): self.cfgObject[section]={} self.cfgObject[section][key]=value def delayed_action(self, action=None, argument=None): """ delayed_action(['save'/'load'/'reload'],[agrument]) This is a trick used to delay multiple saves or reloads followed after repeated usage of cfgObject. Call with no arguments to perform the last requested action """ if action == None and bool(self.delayed): if self.delayed[0] == "save": self.save(self.delayed[1]) if self.delayed[0] == "load": self.load(self.delayed[1]) self.delayed = [] else: self.delayed = [action, argument] # ################################################################### def test(): cfg = cfgObject() if cfg.load('test.ini'): print cfg.get('Instances','Active') if __name__ == '__main__': test()