Annotation of previous/python-ui/config.py, revision 1.1.1.1

1.1       root        1: #!/usr/bin/env python
                      2: #
                      3: # Class and helper functions for handling (Hatari) INI style
                      4: # configuration files: loading, saving, setting/getting variables,
                      5: # mapping them to sections, listing changes
                      6: #
                      7: # Copyright (C) 2008-2010 by Eero Tamminen <eerot at berlios>
                      8: #
                      9: # This program is free software; you can redistribute it and/or modify
                     10: # it under the terms of the GNU General Public License as published by
                     11: # the Free Software Foundation; either version 2 of the License, or
                     12: # (at your option) any later version.
                     13: #
                     14: # This program is distributed in the hope that it will be useful,
                     15: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     16: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     17: # GNU General Public License for more details.
                     18: 
                     19: import os
                     20: 
                     21: # ------------------------------------------------------
                     22: # Helper functions for type safe Hatari configuration variable access.
                     23: # Map booleans, integers and strings to Python types, and back to strings.
                     24: 
                     25: def value_to_text(key, value):
                     26:     "value_to_text(key, value) -> text, convert Python type to string"
                     27:     valtype = type(value)
                     28:     if valtype == bool:
                     29:         assert(key[0] == "b") # bool prefix
                     30:         if value:
                     31:             text = "TRUE"
                     32:         else:
                     33:             text = "FALSE"
                     34:     elif valtype == int:
                     35:         assert(key[0] in ("n", "k")) # numeric/keycode prefix
                     36:         text = str(value)
                     37:     else:
                     38:         assert(key[0] == "s") # string prefix
                     39:         if value == None:
                     40:             text = ""
                     41:         else:
                     42:             text = value
                     43:     return text
                     44: 
                     45: def text_to_value(text):
                     46:     "text_to_value(text) -> value, convert INI file values to real types"
                     47:     # bool?
                     48:     upper = text.upper()
                     49:     if upper == "FALSE":
                     50:         value = False
                     51:     elif upper == "TRUE":
                     52:         value = True
                     53:     else:
                     54:         try:
                     55:             # integer?
                     56:             value = int(text)
                     57:         except ValueError:
                     58:             # string
                     59:             value = text
                     60:     return value
                     61: 
                     62: 
                     63: # ------------------------------------------------------
                     64: # Handle INI style configuration files as used by Hatari
                     65: 
                     66: class ConfigStore:
                     67:     def __init__(self, userconfdir, defaults = {}, miss_is_error = True):
                     68:         "ConfigStore(userconfdir, fgfile[,defaults,miss_is_error])"
                     69:         self.defaults = defaults
                     70:         self.userpath = self._get_full_userpath(userconfdir)
                     71:         self.miss_is_error = miss_is_error
                     72:     
                     73:     def _get_full_userpath(self, leafdir):
                     74:         "get_userpath(leafdir) -> config file default save path from HOME, CWD or their subdir"
                     75:         # user's hatari.cfg can be in home or current work dir,
                     76:         # current dir is used only if $HOME fails
                     77:         for path in (os.getenv("HOME"), os.getenv("HOMEPATH"), os.getcwd()):
                     78:             if path and os.path.exists(path) and os.path.isdir(path):
                     79:                 if leafdir:
                     80:                     hpath = "%s%c%s" % (path, os.path.sep, leafdir)
                     81:                     if os.path.exists(hpath) and os.path.isdir(hpath):
                     82:                         return hpath
                     83:                 return path
                     84:         return None
                     85: 
                     86:     def get_filepath(self, filename):
                     87:         "get_filepath(filename) -> return correct full path to config file"
                     88:         # user config has preference over system one
                     89:         for path in (self.userpath, os.getenv("HATARI_SYSTEM_CONFDIR")):
                     90:             if path:
                     91:                 file = "%s%c%s" % (path, os.path.sep, filename)
                     92:                 if os.path.isfile(file):
                     93:                     return file
                     94:         # writing needs path name although it's missing for reading
                     95:         return "%s%c%s" % (self.userpath, os.path.sep, filename)
                     96:     
                     97:     def load(self, path):
                     98:         "load(path) -> load given configuration file"
                     99:         if os.path.isfile(path):
                    100:             sections = self._read(path)
                    101:             if sections:
                    102:                 self.sections = sections
                    103:             else:
                    104:                 print "ERROR: configuration file loading failed!"
                    105:                 return
                    106:         else:
                    107:             print "WARNING: configuration file missing!"
                    108:             if self.defaults:
                    109:                 print "-> using dummy 'defaults'."
                    110:             self.sections = self.defaults
                    111:         self.path = path
                    112:         self.cfgfile = os.path.basename(path)
                    113:         self.original = self.get_checkpoint()
                    114:         self.changed = False
                    115: 
                    116:     def is_loaded(self):
                    117:         "is_loaded() -> True if configuration loading succeeded"
                    118:         if self.sections:
                    119:             return True
                    120:         return False
                    121: 
                    122:     def get_path(self):
                    123:         "get_path() -> configuration file path"
                    124:         return self.path
                    125:     
                    126:     def _read(self, path):
                    127:         "_read(path) -> (all keys, section2key mappings)"
                    128:         print "Reading configuration file '%s'..." % path
                    129:         config = open(path, "r")
                    130:         if not config:
                    131:             return ({}, {})
                    132:         name = "[_orphans_]"
                    133:         seckeys = {}
                    134:         sections = {}
                    135:         for line in config.readlines():
                    136:             line = line.strip()
                    137:             if not line or line[0] == '#':
                    138:                 continue
                    139:             if line[0] == '[':
                    140:                 if line in sections:
                    141:                     print "WARNING: section '%s' twice in configuration" % line
                    142:                 if seckeys:
                    143:                     sections[name] = seckeys
                    144:                     seckeys = {}
                    145:                 name = line
                    146:                 continue
                    147:             if line.find('=') < 0:
                    148:                 print "WARNING: line without key=value pair:\n%s" % line
                    149:                 continue
                    150:             key, text = [string.strip() for string in line.split('=')]
                    151:             seckeys[key] = text_to_value(text)
                    152:         if seckeys:
                    153:             sections[name] = seckeys
                    154:         return sections
                    155: 
                    156:     def get_checkpoint(self):
                    157:         "get_checkpoint() -> checkpoint, get the state of variables at this point"
                    158:         checkpoint = {}
                    159:         for section in self.sections.keys():
                    160:             checkpoint[section] = self.sections[section].copy()
                    161:         return checkpoint
                    162:     
                    163:     def get_checkpoint_changes(self, checkpoint):
                    164:         "get_checkpoint_changes() -> list of (key, value) pairs for later changes"
                    165:         changed = []
                    166:         if not self.changed:
                    167:             return changed
                    168:         for section in self.sections.keys():
                    169:             if section not in checkpoint:
                    170:                 for key, value in self.sections[section].items():
                    171:                     changed.append((key, value))
                    172:                 continue
                    173:             for key, value in self.sections[section].items():                    
                    174:                 if (key not in checkpoint[section] or
                    175:                 value != checkpoint[section][key]):
                    176:                     text = value_to_text(key, value)
                    177:                     changed.append(("%s.%s" % (section, key), text))
                    178:         return changed
                    179:     
                    180:     def revert_to_checkpoint(self, checkpoint):
                    181:         "revert_to_checkpoint(checkpoint), revert to given checkpoint"
                    182:         self.sections = checkpoint
                    183: 
                    184:     def get(self, section, key):
                    185:         return self.sections[section][key]
                    186: 
                    187:     def set(self, section, key, value):
                    188:         "set(section,key,value), set given key to given section"
                    189:         if section not in self.sections:
                    190:             if self.miss_is_error:
                    191:                 raise AttributeError, "no section '%s'" % section
                    192:             self.sections[section] = {}
                    193:         if key not in self.sections[section]:
                    194:             if self.miss_is_error:
                    195:                 raise AttributeError, "key '%s' not in section '%s'" % (key, section)
                    196:             self.sections[section][key] = value
                    197:             self.changed = True
                    198:         elif self.sections[section][key] != value:
                    199:             self.changed = True
                    200:         self.sections[section][key] = value
                    201:         
                    202:     def is_changed(self):
                    203:         "is_changed() -> True if current configuration is changed"
                    204:         return self.changed
                    205: 
                    206:     def get_changes(self):
                    207:         "get_changes(), return (key, value) list for each changed config option"
                    208:         return self.get_checkpoint_changes(self.original)
                    209:     
                    210:     def write(self, fileobj):
                    211:         "write(fileobj), write current configuration to given file object"
                    212:         sections = self.sections.keys()
                    213:         sections.sort()
                    214:         for name in sections:
                    215:             fileobj.write("%s\n" % name)
                    216:             keys = self.sections[name].keys()
                    217:             keys.sort()
                    218:             for key in keys:
                    219:                 value = value_to_text(key, self.sections[name][key])
                    220:                 fileobj.write("%s = %s\n" % (key, value))
                    221:             fileobj.write("\n")
                    222: 
                    223:     def save(self):
                    224:         "save() -> path, if configuration changed, save it"
                    225:         if not self.changed:
                    226:             print "No configuration changes to save, skipping"
                    227:             return None
                    228:         fileobj = None
                    229:         if self.path:
                    230:             try:
                    231:                 fileobj = open(self.path, "w")
                    232:             except:
                    233:                 pass
                    234:         if not fileobj:
                    235:             print "WARNING: non-existing/writable configuration file, creating a new one..."
                    236:             if not os.path.exists(self.userpath):
                    237:                 os.makedirs(self.userpath)
                    238:             self.path = "%s%c%s" % (self.userpath, os.path.sep, self.cfgfile)
                    239:             fileobj = open(self.path, "w")
                    240:         if not fileobj:
                    241:             print "ERROR: opening '%s' for saving failed" % self.path
                    242:             return None
                    243:         self.write(fileobj)
                    244:         print "Saved configuration file:", self.path
                    245:         self.changed = False
                    246:         return self.path
                    247:     
                    248:     def save_as(self, path):
                    249:         "save_as(path) -> path, save configuration to given file and select it"
                    250:         assert(path)
                    251:         if not os.path.exists(os.path.dirname(path)):
                    252:             os.makedirs(os.path.dirname(path))
                    253:         self.path = path
                    254:         self.changed = True
                    255:         return self.save()
                    256: 
                    257:     def save_tmp(self, path):
                    258:         "save_tmp(path) -> path, save configuration to given file without selecting it"
                    259:         if not os.path.exists(os.path.dirname(path)):
                    260:             os.makedirs(os.path.dirname(path))
                    261:         fileobj = open(path, "w")
                    262:         if not fileobj:
                    263:             print "ERROR: opening '%s' for saving failed" % path
                    264:             return None
                    265:         self.write(fileobj)
                    266:         print "Saved temporary configuration file:", path
                    267:         return path

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.