#!/usr/bin/env python
#
# $Id: UserPrefs.py,v 1.3 2001/11/03 11:05:22 doughellmann Exp $
#
# Copyright 2001 Doug Hellmann.
#
#
# All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and
# its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of Doug
# Hellmann not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
#
# DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
# NO EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
"""User preference management.
The UserPrefs class provides a simple interface for maintaining
user preference values for user applications.
"""
__rcs_info__ = {
#
# Creation Information
#
'module_name' : '$RCSfile: UserPrefs.py,v $',
'rcs_id' : '$Id: UserPrefs.py,v 1.3 2001/11/03 11:05:22 doughellmann Exp $',
'creator' : 'Doug Hellmann <doug@hellfly.net>',
'project' : 'PmwContribD',
'created' : 'Sun, 27-May-2001 18:18:03 EDT',
#
# Current Information
#
'author' : '$Author: doughellmann $',
'version' : '$Revision: 1.3 $',
'date' : '$Date: 2001/11/03 11:05:22 $',
}
#
# Import system modules
#
import string
import re
import os
import UserDict
import sys
import time
import types
import base64
if sys.platform == 'win32':
import registry
#
# Import Local modules
#
#
# Module
#
class UserPrefs(UserDict.UserDict):
"""A class for managing user preferences.
Preferences are stored in a platform-specific storage area. Under
UNIX, this will be a '~/.<classname>' file. On the Macintosh, the
file will be written to the Preferences folder. On win32 systems,
the values will be stored in the registry.
Members:
allowedValues -- The 'allowedValues' class attribute is a
dictionary defining the preferences managed. The keys of the
dictionary should be the names of the preference values. The
values should be tuples of the form (type name, default value,
comment). Type name can be any value which appears as a key
in the converters dictionary (see below). The default value
should be of the correct time. The comment should be a string
which will help the user make sense of the value when it is
written out to the storage area.
storeOnChange -- The 'storeOnChange' instance attribute is a boolean flag
indicating whether or not all preference values should be
written to the storage area each time any value is modified.
converters -- Listed in the class attribute 'converters'
should be a mapping of a type name to a conversion function.
The conversion function should take one parameter and should
attempt to conver that value to the named type. If the value
cannot be converted to the specified type, a 'ValueError'
should be raised to prevent the input value from being stored.
If the value can be converted, the converted value should be
returned.
enumValues -- The 'enumValues' dictionary is keyed on value
names. Associated with the name of the value should be a
sequence of allowed enumeration items.
"""
allowedValues = {}
order = ()
depricated = ()
enumValues = {}
storeOnChange = 1
REGISTRY='registry'
def __init__(self):
#
# Set up the type converters
#
self.storage_converters = {
'string' : self.storageConverter_string,
'password': self.storageConverter_password,
'text' : self.storageConverter_text,
'integer' : self.storageConverter_integer,
'float' : self.storageConverter_float,
'boolean' : self.storageConverter_boolean,
'enum' : self.storageConverter_enum,
'color' : self.storageConverter_color,
'font' : self.storageConverter_font,
}
self.read_converters = {
'string' : self.readConverter_string,
'password': self.readConverter_password,
'text' : self.readConverter_text,
'integer' : self.readConverter_integer,
'float' : self.readConverter_float,
'boolean' : self.readConverter_boolean,
'enum' : self.readConverter_enum,
'color' : self.readConverter_color,
'font' : self.readConverter_font,
}
#
# Compute and initialize the default values
#
defaults = {}
for av in self.allowedValues.keys():
defaults[av] = self.allowedValues[av][1]
UserDict.UserDict.__init__(self, defaults)
#
# Read any values from the storage area
#
self._prefs_storage_name = self.getPrefsStorageName()
#
# Initialize the default values.
#
self.initializeDefaults()
#
# Get values from storage, if they are there.
#
self.readValuesFromStorage()
#
# Write values back to storage, in case they were not
# there.
#
self.writeValuesToStorage()
return
def keys(self):
"""Returns a sequence of names of the preference values.
We override this so that we can control the order of
the values in other places, but so that users of
the class can treat us as a dictionary.
"""
return self.order
def getPrefsStorageName(self):
"""Returns the name of the storage for the preferences.
The location might be a filename or 'self.REGISTRY'.
"""
plat = sys.platform
#print 'platform is ', plat
if plat == 'macintosh':
return os.path.join( os.environ['PREF_FOLDER'],
self.__class__.__name__ )
elif plat == 'win32':
#
# Build up the base registry key name
#
self._registry_key_name = registry.keyNameMake((
'HKEY_CURRENT_USER',
'Software',
'WhackyChat',
))
self._registry_key = None
return self.REGISTRY
else:
return os.path.join( os.environ['HOME'],
'.%s' % self.__class__.__name__ )
def readValuesFromStorage_File(self):
try:
input = open(self._prefs_storage_name, 'rt')
except IOError:
# No file, or can't read it. Do not do
# anything.
pass
else:
for line in input.readlines():
line = string.strip(line)
if not line: continue
if line[0] == '#': continue
#parts = map(string.strip, string.split(line, '='))
try:
eq = string.index(line, '=')
var = string.strip(line[:eq])
val = string.strip(line[eq+1:])
except ValueError:
raise ValueError('Could not parse line "%s" in %s.' \
% (line, self._prefs_storage_name))
if var in self.depricated:
# Report the user of an old configuration value.
print 'The use of the configuration value "%s"' % var
print 'has been depricated.'
print 'The stored value for this parameter will be discarded.'
print
continue
if var not in self.allowedValues.keys():
raise ValueError('Unknown preference value found in %s' \
% self._prefs_storage_name, var, val)
val = self.convert_on_read(var, val)
self[var] = val
input.close()
return
def readValuesFromStorage_Registry(self):
#print 'readValuesFromStorage_Regitry'
if not self._registry_key:
self._registry = registry.Registry()
self._registry_key = self._registry.openKey(
self._registry_key_name)
existing_keys = self._registry_key.subKeyNames()
for key in existing_keys:
sub_key_name = registry.keyNameMake( (self._registry_key_name,
key))
sub_key = self._registry.openKey(sub_key_name)
val, ignore = sub_key.valueQuery()
#print 'read %s as %s' % (sub_key_name, val)
val = self.convert_on_read(key, val)
self[key] = val
return
def readValuesFromStorage(self):
#
# We are giong to set values based on the
# storage contents, so we don't want
# to be writing over the storage every
# time we read a value from it. Temporarily
# set the value to not write, then restore
# the original value when we're done.
#
if self._prefs_storage_name == self.REGISTRY:
self.readValuesFromStorage_Registry()
else:
storeOnChange = self.storeOnChange
self.storeOnChange = 0
self.readValuesFromStorage_File()
self.storeOnChange = storeOnChange
return
def writeValuesToStorage_Registry(self, value_names):
self._registry_key.valueSet('Last updated %s' % time.ctime(time.time()))
if value_names:
var_names = value_names
else:
var_names = self.keys()
for key in var_names:
sub_key_name = registry.keyNameMake( (self._registry_key_name,
key))
sub_key = self._registry.openKey(sub_key_name)
pref_type, pref_default, pref_comment = self.allowedValues[key]
try:
converter = self.storage_converters[pref_type]
representation = converter(key, self[key])
except KeyError:
# No converter, store as a string
representation = str(self[key])
#print 'Storing %s as "%s"' % (key, representation)
representation = str(representation)
sub_key.valueSet(pref_comment, 'Description')
sub_key.valueSet(representation)
return
def writeValuesToStorage_File(self, value_names):
#print 'writing'
output = open(self._prefs_storage_name, 'wt')
output.write('#\n# Last updated: %s\n#\n\n' % time.ctime(time.time()))
varNames = self.keys()
for var in varNames:
prefType, prefDefault, prefComment = self.allowedValues[var]
prefCommentLines = string.split(prefComment, '\n')
#prefType, prefDefault, prefComment = self.allowedValues[var]
try:
converter = self.storage_converters[prefType]
#print 'CONVERTING:', self[var]
representation = converter(var, self[var])
except KeyError:
# No converter, store as a string
representation = str(self[var])
output.write('#\n')
for line in prefCommentLines:
output.write('# %s\n' % line)
output.write('#\n')
output.write('%s = %s\n\n' % (var, representation))
#print '\t%s = %s' % (var, representation)
output.close()
return
def writeValuesToStorage(self, value_names=()):
if self._prefs_storage_name == self.REGISTRY:
self.writeValuesToStorage_Registry(value_names)
else:
self.writeValuesToStorage_File( () )
return
#
# create a shortcut for storing
#
store = writeValuesToStorage
def convert_on_read(self, item, value):
try:
prefType, prefDefault, prefComment = self.allowedValues[item]
except KeyError:
# Not a defined preference
raise ValueError('Unknown preference %s' % item)
# Force the value to the right type
converter = self.read_converters[prefType]
if converter:
try:
value = converter(item, value)
except ValueError, msg:
raise ValueError('Could not convert "%s" to a %s for %s' \
% (value, prefType, item),
str(msg))
return value
def __setitem__(self, item, value):
#print 'Recording %s=%s' % (item, value)
# Validate the preference name
#value = self.convert_on_read(item, value)
UserDict.UserDict.__setitem__(self, item, value)
if self.storeOnChange:
self.store( (item,) )
return
#
# Define basic converters
#
def storageConverter_integer(self, name, value):
return int(value)
def readConverter_integer(self, name, value):
return int(value)
def storageConverter_string(self, name, value):
return str(value)
def readConverter_string(self, name, value):
return value
def storageConverter_password(self, name, value):
return base64.encodestring(str(value))
def readConverter_password(self, name, value):
return base64.decodestring(value)
def storageConverter_text(self, name, value):
s = self.storageConverter_string(name, value)
s = string.replace(s, '\r', ' ')
s = string.replace(s, '\n', '|')
return s
def readConverter_text(self, name, value):
s = self.readConverter_string(name, value)
s = string.replace(s, '|', '\n')
return s
def storageConverter_color(self, name, value):
return str(value)
def readConverter_color(self, name, value):
return str(value)
def storageConverter_float(self, name, value):
return float(value)
def readConverter_float(self, name, value):
return float(value)
def valueToBoolean(self, name, value):
#print 'converting %s:%s' % (name, value)
if type(value) == types.StringType:
value = string.lower(value)
if value in ('yes', 'true'):
value = 1
elif value in ('no', 'false'):
value = 0
else:
value = int(value)
if value:
bool = 1
else:
bool = 0
#print 'bool = ', bool
return bool
def storageConverter_boolean(self, name, value):
return self.valueToBoolean(name, value)
def readConverter_boolean(self, name, value):
return self.valueToBoolean(name, value)
def valueToEnum(self, name, value):
#
# Determine the legal values
#
try:
values = self.enumValues[name]
except KeyError:
raise ValueError('No enumerations defined for %s' % name)
#
# Determine if the requested value
# is one of the legal values.
#
if value in values:
return value
else:
#
# Try to convert the value to an index number
#
try:
valueNum = int(value)
except ValueError:
raise ValueError('Enum value %s must be one of %s' \
% (value, str(values)))
try:
return values[value]
except IndexError:
raise ValueError('Enum value %s must be one of %s' \
% (value, str(values)))
def readConverter_enum(self, name, value):
return self.valueToEnum(name, value)
def storageConverter_enum(self, name, value):
return self.valueToEnum(name, value)
def readConverter_font(self, name, value):
#print 'readConverter_font(%s, %s)' % (name, value)
readval = eval(value)
#print '\ttype=', type(readval)
#print '\tval=', readval
return readval
def storageConverter_font(self, name, value):
#print 'storageConverter_font(%s, %s)' % (name, value)
if type(value) == type(''):
#print '\tconverting to sequence'
seq = eval(value)
#print '\ttype of value:', type(value)
storage = str(value)
#print '\tstorage: ', storage
return storage
def initializeDefaults(self):
"""Initialize internal default values.
Initialize our default values as though they were written
in the storage area.
"""
storeOnChange = self.storeOnChange
self.storeOnChange = 0
for name, valueT in self.allowedValues.items():
default = valueT[1]
self[name] = default
self.storeOnChange = storeOnChange
return
if __name__ == '__main__':
class testprefsrc(UserPrefs):
order = (
'fonttest',
'userName',
'password',
'userId',
'timeout',
'updatetime',
'exampleenum',
'colortest',
'booltest',
)
allowedValues = {
'fonttest' : ('font', '("Courier",)', 'Font for displaying values'),
'userName' : ('string', None, 'The name of the user.'),
'password' : ('password', None, 'Your password.'),
'userId' : ('integer', 0, 'The id number of the user.'),
'timeout' : ('float', 0.0, 'How long to wait?'),
'updatetime' : ('string', time.ctime(time.time()),
'What time is it?'),
'exampleenum' : ('enum', 'one', 'Pick one of the enum.'),
'colortest' : ('color', 'white', 'Favorite color'),
'booltest' : ('boolean', 1, 'Toggle me'),
}
enumValues = {
'exampleenum': ('zero', 'one', 'two', 'three'),
}
def testfunc():
tp = testprefsrc()
print 'tp=', tp
tp['userName'] = 'Doug'
tp['password'] = 'password'
tp['userId'] = '10'
tp['timeout'] = 1
tp['updatetime'] = time.ctime(time.time())
tp['exampleenum'] = 1
print 'tp=', tp
tp['exampleenum'] = 'two'
print 'tp=', tp
tp.store()
for name, value in tp.items():
print '%s=%s (%s)' % (name, value, type(value))
return
testfunc()
|