Annotation of hatari/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 by Eero Tamminen <[email protected]>
                      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:     defaultpath = "%s%c.hatari" % (os.path.expanduser("~"), os.path.sep)
                     68: 
                     69:     def __init__(self, cfgfile, defaults = {}, miss_is_error = True):
                     70:         "ConfigStore(cfgfile[,defaults,miss_is_error])"
                     71:         self.defaults = defaults
                     72:         self.miss_is_error = miss_is_error
                     73:         path = self._get_fullpath(cfgfile)
                     74:         self.cfgfile = cfgfile
                     75:         self.load(path)
                     76:     
                     77:     def load(self, path):
                     78:         "load(path), load given configuration file"
                     79:         if path:
                     80:             sections = self._read(path)
                     81:             if sections:
                     82:                 print "Loaded configuration file:", path
                     83:                 self.cfgfile = os.path.basename(path)
                     84:                 self.sections = sections
                     85:             else:
                     86:                 print "ERROR: configuration file '%' loading failed" % path
                     87:                 return
                     88:         else:
                     89:             print "WARNING: configuration file missing, using defaults"
                     90:             self.sections = self.defaults
                     91:         self.path = path
                     92:         self.original = self.get_checkpoint()
                     93:         self.changed = False
                     94: 
                     95:     def is_loaded(self):
                     96:         "is_loaded() -> True if configuration loading succeeded"
                     97:         if self.sections:
                     98:             return True
                     99:         return False
                    100: 
                    101:     def get_path(self):
                    102:         "get_path() -> configuration file path"
                    103:         return self.path
                    104:     
                    105:     def _get_fullpath(self, cfgfile):
                    106:         "get_fullpath(cfgfile) -> path or None, check first CWD & then HOME for cfgfile"
                    107:         # hatari.cfg can be in home or current work dir
                    108:         for path in (os.getcwd(), os.path.expanduser("~")):
                    109:             if path:
                    110:                 path = self._check_path(path, cfgfile)
                    111:                 if path:
                    112:                     return path
                    113:         return None
                    114: 
                    115:     def _check_path(self, path, cfgfile):
                    116:         """check_path(path,cfgfile) -> path
                    117:         
                    118:         return full path if cfg in path/.hatari/ or in path prefixed with '.'"""
                    119:         sep = os.path.sep
                    120:         testpath = "%s%c.hatari%c%s" % (path, sep, sep, cfgfile)
                    121:         if os.path.exists(testpath):
                    122:             return testpath
                    123:         testpath = "%s%c.%s" % (path, sep, cfgfile)
                    124:         if os.path.exists(testpath):
                    125:             return testpath
                    126:         return None
                    127:     
                    128:     def _read(self, path):
                    129:         "_read(path) -> (all keys, section2key mappings)"
                    130:         config = open(path, "r")
                    131:         if not config:
                    132:             return ({}, {})
                    133:         name = "[_orphans_]"
                    134:         seckeys = {}
                    135:         sections = {}
                    136:         for line in config.readlines():
                    137:             line = line.strip()
                    138:             if not line or line[0] == '#':
                    139:                 continue
                    140:             if line[0] == '[':
                    141:                 if line in sections:
                    142:                     print "WARNING: section '%s' twice in configuration" % line
                    143:                 if seckeys:
                    144:                     sections[name] = seckeys
                    145:                     seckeys = {}
                    146:                 name = line
                    147:                 continue
                    148:             if line.find('=') < 0:
                    149:                 print "WARNING: line without key=value pair:\n%s" % line
                    150:                 continue
                    151:             key, text = [string.strip() for string in line.split('=')]
                    152:             seckeys[key] = text_to_value(text)
                    153:         if seckeys:
                    154:             sections[name] = seckeys
                    155:         return sections
                    156: 
                    157:     def get_checkpoint(self):
                    158:         "get_checkpoint() -> checkpoint, get the state of variables at this point"
                    159:         checkpoint = {}
                    160:         for section in self.sections.keys():
                    161:             checkpoint[section] = self.sections[section].copy()
                    162:         return checkpoint
                    163:     
                    164:     def get_checkpoint_changes(self, checkpoint):
                    165:         "get_checkpoint_changes() -> list of (key, value) pairs for later changes"
                    166:         changed = []
                    167:         if not self.changed:
                    168:             return changed
                    169:         for section in self.sections.keys():
                    170:             if section not in checkpoint:
                    171:                 for key, value in self.sections[section].items():
                    172:                     changed.append((key, value))
                    173:                 continue
                    174:             for key, value in self.sections[section].items():                    
                    175:                 if (key not in checkpoint[section] or
                    176:                 value != checkpoint[section][key]):
                    177:                     text = value_to_text(key, value)
                    178:                     changed.append(("%s.%s" % (section, key), text))
                    179:         return changed
                    180:     
                    181:     def revert_to_checkpoint(self, checkpoint):
                    182:         "revert_to_checkpoint(checkpoint), revert to given checkpoint"
                    183:         self.sections = checkpoint
                    184: 
                    185:     def get(self, section, key):
                    186:         return self.sections[section][key]
                    187: 
                    188:     def set(self, section, key, value):
                    189:         "set(section,key,value), set given key to given section"
                    190:         if section not in self.sections:
                    191:             if self.miss_is_error:
                    192:                 raise AttributeError, "no section '%s'" % section
                    193:             self.sections[section] = {}
                    194:         if key not in self.sections[section]:
                    195:             if self.miss_is_error:
                    196:                 raise AttributeError, "key '%s' not in section '%s'" % (key, section)
                    197:             self.sections[section][key] = value
                    198:             self.changed = True
                    199:         elif self.sections[section][key] != value:
                    200:             self.changed = True
                    201:         self.sections[section][key] = value
                    202:         
                    203:     def is_changed(self):
                    204:         "is_changed() -> True if current configuration is changed"
                    205:         return self.changed
                    206: 
                    207:     def get_changes(self):
                    208:         "get_changes(), return (key, value) list for each changed config option"
                    209:         return self.get_checkpoint_changes(self.original)
                    210:     
                    211:     def write(self, fileobj):
                    212:         "write(fileobj), write current configuration to given file object"
                    213:         sections = self.sections.keys()
                    214:         sections.sort()
                    215:         for name in sections:
                    216:             fileobj.write("%s\n" % name)
                    217:             keys = self.sections[name].keys()
                    218:             keys.sort()
                    219:             for key in keys:
                    220:                 value = value_to_text(key, self.sections[name][key])
                    221:                 fileobj.write("%s = %s\n" % (key, value))
                    222:             fileobj.write("\n")
                    223: 
                    224:     def save(self):
                    225:         "save() -> path, if configuration changed, save it"
                    226:         if not self.changed:
                    227:             print "No configuration changes to save, skipping"
                    228:             return None
                    229:         if not self.path:
                    230:             print "WARNING: no existing configuration file, trying to create one"
                    231:             if not os.path.exists(self.defaultpath):
                    232:                 os.mkdir(self.defaultpath)
                    233:             self.path = "%s%c%s" % (self.defaultpath, os.path.sep, self.cfgfile)
                    234:         fileobj = open(self.path, "w")
                    235:         if not fileobj:
                    236:             print "ERROR: opening '%s' for saving failed" % self.path
                    237:             return None
                    238:         self.write(fileobj)
                    239:         print "Saved configuration file:", self.path
                    240:         self.changed = False
                    241:         return path
                    242:     
                    243:     def save_as(self, path):
                    244:         "save_as(path) -> path, save configuration to given file and select it"
                    245:         assert(path)
                    246:         if not os.path.exists(os.path.dirname(path)):
                    247:             os.makedirs(os.path.dirname(path))
                    248:         self.path = path
                    249:         self.changed = True
                    250:         self.save()
                    251:         return path
                    252: 
                    253:     def save_tmp(self, path):
                    254:         "save_tmp(path) -> path, save configuration to given file without selecting it"
                    255:         if not os.path.exists(os.path.dirname(path)):
                    256:             os.makedirs(os.path.dirname(path))
                    257:         fileobj = open(path, "w")
                    258:         if not fileobj:
                    259:             print "ERROR: opening '%s' for saving failed" % path
                    260:             return None
                    261:         self.write(fileobj)
                    262:         print "Saved temporary configuration file:", path
                    263:         return path

unix.superglobalmegacorp.com

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