home *** CD-ROM | disk | FTP | other *** search
- """
-
- inifile.py
-
- Author: Matthias Gudmundsson
- Created: 2002.09.25
- Project: Framework
-
- Description:
-
- Does .ini file handling. By default, two handlers are created and
- added to builtins as the global variables 'boot' and 'prefs':
-
- 'boot' is bound to boot.ini file in the root directory.
- 'prefs' is bound to prefs.ini file in the cache directory.
-
-
- Note! This file is executed by Blue very early on which means that the
- 'boot' and 'prefs' variables are available before any other script is
- executed.
-
- 'boot' contains the startup settings. By default it uses "boot.ini"
- file but that can be overridden by setting a command line argument
- like this: /config=alternative.ini
-
- The boot values are always read-only, but can be updated with
- new entries but the changes will not be written back to disk.
-
- 'prefs' contains all other settings the app might want to store.
- The file used is always "prefs.ini" in the cache folder.
-
- (c) CCP 2000, 2001, 2002
-
-
- Revisions:
-
- 2002.09.25 Created.
-
- """
-
- import blue
- import types
- from base64 import decodestring, encodestring
- from cPickle import loads, dumps
-
-
- """
- Works like a normal .ini file except there are no groups. All non-blank
- lines should include a key value pair, where the key and value is a string,
- separated with a =. If the line starts with # it's ignored and considered
- a comment. If a line starts with a [ or ; it's also ignored, and is there
- for compatibility with external tools which expect an .ini file with groups.
-
- The file is kept opened for the whole time and writes are committed on
- every modification. Order of key-value pairs, comments and linebreaks are
- kept intact.
- """
- class IniFile:
-
-
- __guid__ = "util.IniFile"
-
- nopickles = [
- types.IntType,
- types.FloatType,
- types.LongType,
- types.StringType,
- ]
-
- nodef = [0]
-
-
- # -----------------------------------------------------------------------------------
- # IniFile - Constructor
- # -----------------------------------------------------------------------------------
- # 'shortname' is the name of the config file, without path and dot.extention.
- # 'root' is the location of the config file, if ommitted, blue.os.rootpath is used.
- # -----------------------------------------------------------------------------------
- def __init__(self, shortname, root = None, readOnly = 0):
-
- # Raw file data
- self.data = ""
-
- # Data is divided into blocks where each block is a single line in a text file
- # format. The block descriptor has byte start offset and block size and is
- # ordered by linenumbers.
- self.blocks = []
-
- # Key is the key name
- # Value is list: [lineno, filepos, value string]
- self.keyval = {}
-
- # Fix root path
- if root is None:
- root = blue.os.rootpath
- elif root[-1] not in ['\\', '/']:
- root += "/"
-
- # Construct a full pathname for file
- if shortname[-4:] != '.ini':
-
- filename = root + shortname + ".ini"
- else:
- filename = root + shortname
- self.filename = filename
-
- # Open the file if it exists, create it if not
- inifile = blue.os.CreateInstance("blue.ResFile")
-
- # File may exists as read-only, so let's be prepared for that
- if not readOnly:
- try:
- self.readonly = 0
- if not inifile.Open(filename, 0):
- inifile.Create(filename)
- self.inifile = inifile
- except:
-
- self.readonly = 1
- if inifile.Open(filename, 1):
- self.inifile = inifile
- else:
- self.readonly = 1
- self.inifile = None
- self.lines = []
- return
-
- else:
- self.readonly = 1
- inifile.OpenAlways(filename, 1)
-
-
- # Read in data
- data = str(inifile.Read())
-
- # Split data into lines
- if len(data):
- data = data.replace("\r", "")
- self.lines = data.split("\n")
- if self.lines[-1] == "":
- self.lines.pop()
- else:
- self.lines = []
-
- # Create key index
- for line in self.lines:
- if len(line) > 0 and line[0] not in "[;#":
- sep = line.find("=")
- if sep > 0 and len(line) > sep+1:
- self.keyval[str(line[:sep])] = line
-
-
- # -----------------------------------------------------------------------------------
- # HasKey
- # -----------------------------------------------------------------------------------
- # Check for existence of key-value pair.
- #
- # Returns true if 'key' exists, false otherwise
- # -----------------------------------------------------------------------------------
- def HasKey(self, key):
- return str(key).strip() in self.keyval
-
- # -----------------------------------------------------------------------------------
- # GetKeys
- # -----------------------------------------------------------------------------------
- def GetKeys(self, beginWith=None):
- if beginWith is not None:
- return [key for key in self.keyval.keys() if key[:len(beginWith)] == beginWith]
- return self.keyval.keys()
-
- # -----------------------------------------------------------------------------------
- # GetValue
- # -----------------------------------------------------------------------------------
- # Returns the value associated with the key.
- #
- # If 'key' is not found, then 'default' is registered under the key and returned, but
- # if 'default' is ommitted then a key error is raised.
- # -----------------------------------------------------------------------------------
- def GetValue(self, key, default = nodef):
- key = str(key).strip().replace('|', '||').replace('=', '-|-') # avoid '=' in the key
-
-
-
- if not self.keyval.has_key(key):# not in self.keyval:
- if default is self.nodef:
- raise KeyError(key)
- return default
-
- value = self.keyval[key]
- sep = value.find("=")
- value = value[sep+1:]
-
- if not len(value):
- return value
-
- if value[-1] == '\r':
- value = value[:-1]
-
- # Do type conversion
-
- # Is it pickle?
- if value[:7] == "pickle:":
- return loads(value[7:].replace("", "\n"))
-
- # Try int cast
- try:
- return int(value)
- except:
- pass
-
- # Try long cast
- try:
- return long(value)
- except:
- pass
-
- # Try float cast
- try:
- return float(value)
- except:
- pass
-
- # Try string cast ;)
- return str(value).strip()
-
-
- # -----------------------------------------------------------------------------------
- # SetValue
- # -----------------------------------------------------------------------------------
- # Registers a value under a key.
- #
- # If 'key' already exists, the value is replaced.
- # 'value' can be any picklable python object.
- # If 'forcePickle' is true, the value is always pickled.
- # -----------------------------------------------------------------------------------
- def SetValue(self, key, value, forcePickle = 0):
- key = str(key).strip().replace('|', '||').replace('=', '-|-') # avoid '=' in the key
-
- # Strings with difficult characters must be pickled
- if type(value) == types.StringType:
- for c in value:
- if ord(c) < 32 or ord(c) > 255:
- forcePickle = 1
- break
-
- if forcePickle or type(value) not in self.nopickles:
- value = "pickle:" + dumps(value).replace("\n", "")
- else:
- value = str(value).strip()
-
- line = "%s=%s" % (key, value)
-
- if key in self.keyval:
- old = self.keyval[key]
- if line == self.keyval[key]:
- # no change
- return
-
- lineno = self.lines.index(old)
- self.lines.remove(old)
- self.lines.insert(lineno, line)
- else:
- self.lines.append(line)
-
- self.keyval[key] = line
-
- self.__FlushToDisk()
-
-
-
- # -----------------------------------------------------------------------------------
- # DeleteValue
- # -----------------------------------------------------------------------------------
- # Deletes the key-value line, if exists.
- # -----------------------------------------------------------------------------------
- def DeleteValue(self, key):
- key = str(key).strip()
-
- if key in self.keyval:
- self.lines.remove(self.keyval[key])
- del self.keyval[key]
- self.__FlushToDisk()
-
-
- # -----------------------------------------------------------------------------------
- # FlushToDisk - Commit changes to disk
- # -----------------------------------------------------------------------------------
- def __FlushToDisk(self):
- if self.readonly:
- return
-
- self.inifile.Seek(0)
-
- sortlines = [(line.lower()[:3], line) for line in self.lines]
- sortlines.sort()
- lines = [line[1] for line in sortlines]
- for line in lines:
- self.inifile.Write(line + "\r\n")
-
- newpos = self.inifile.pos
- self.inifile.Seek(0)
- self.inifile.SetSize(newpos)
-
-
- """
- Magic getattr/setattr wrapper for convenience
- """
- class Handler:
-
- def __init__(self, inifile):
- self.__dict__["ini"] = inifile
-
- def __getattr__(self, key):
- if hasattr(self.__dict__["ini"], key):
- return getattr(self.__dict__["ini"], key)
- else:
- return self.__dict__["ini"].GetValue(key)
-
- def __setattr__(self, key, value):
- self.__dict__["ini"].SetValue(key, value)
-
- def __str__(self):
- return "IniFile %s with %s entries" % (self.__dict__["ini"].filename, len(self.__dict__["ini"].keyval))
-
- def __eq__(self,arg):
- return NotImplemented
-
-
- def Init():
- import __builtin__
- if hasattr(__builtin__, "prefs"):
- return
-
- # Initialize bootinfo
- import blue
- file = "boot"
-
- # Command line may override the default boot.ini filename
- for arg in blue.pyos.GetArg():
- if arg[:8] == "/config=":
- file = arg[8:]
- break
-
- # Create the handler
- handler = Handler(IniFile(file, None, 1))
-
- # Shove it into builtins as bootinfo
- __builtin__.boot = handler
-
- # Create settings handler
-
- handler = Handler(IniFile("prefs", blue.os.cachepath))
- __builtin__.prefs = handler
-
-
-
-
- Init()
-