Annotation of hatari/python-ui/config.py, revision 1.1.1.6

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

unix.superglobalmegacorp.com

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