gecko-dev/testing/mozbase/mozprofile/mozprofile/prefs.py

214 строки
6.5 KiB
Python

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
"""
user preferences
"""
import os
import re
from ConfigParser import SafeConfigParser as ConfigParser
try:
import json
except ImportError:
import simplejson as json
class PreferencesReadError(Exception):
"""read error for prefrences files"""
class Preferences(object):
"""assembly of preferences from various sources"""
def __init__(self, prefs=None):
self._prefs = []
if prefs:
self.add(prefs)
def add(self, prefs, cast=False):
"""
- cast: whether to cast strings to value, e.g. '1' -> 1
"""
# wants a list of 2-tuples
if isinstance(prefs, dict):
prefs = prefs.items()
if cast:
prefs = [(i, self.cast(j)) for i, j in prefs]
self._prefs += prefs
def add_file(self, path):
"""a preferences from a file"""
self.add(self.read(path))
def __call__(self):
return self._prefs
@classmethod
def cast(cls, value):
"""
interpolate a preference from a string
from the command line or from e.g. an .ini file, there is no good way to denote
what type the preference value is, as natively it is a string
- integers will get cast to integers
- true/false will get cast to True/False
- anything enclosed in single quotes will be treated as a string with the ''s removed from both sides
"""
if not isinstance(value, basestring):
return value # no op
quote = "'"
if value == 'true':
return True
if value == 'false':
return False
try:
return int(value)
except ValueError:
pass
if value.startswith(quote) and value.endswith(quote):
value = value[1:-1]
return value
@classmethod
def read(cls, path):
"""read preferences from a file"""
section = None # for .ini files
basename = os.path.basename(path)
if ':' in basename:
# section of INI file
path, section = path.rsplit(':', 1)
if not os.path.exists(path):
raise PreferencesReadError("'%s' does not exist" % path)
if section:
try:
return cls.read_ini(path, section)
except PreferencesReadError:
raise
except Exception, e:
raise PreferencesReadError(str(e))
# try both JSON and .ini format
try:
return cls.read_json(path)
except Exception, e:
try:
return cls.read_ini(path)
except Exception, f:
for exception in e, f:
if isinstance(exception, PreferencesReadError):
raise exception
raise PreferencesReadError("Could not recognize format of %s" % path)
@classmethod
def read_ini(cls, path, section=None):
"""read preferences from an .ini file"""
parser = ConfigParser()
parser.read(path)
if section:
if section not in parser.sections():
raise PreferencesReadError("No section '%s' in %s" % (section, path))
retval = parser.items(section, raw=True)
else:
retval = parser.defaults().items()
# cast the preferences since .ini is just strings
return [(i, cls.cast(j)) for i, j in retval]
@classmethod
def read_json(cls, path):
"""read preferences from a JSON blob"""
prefs = json.loads(file(path).read())
if type(prefs) not in [list, dict]:
raise PreferencesReadError("Malformed preferences: %s" % path)
if isinstance(prefs, list):
if [i for i in prefs if type(i) != list or len(i) != 2]:
raise PreferencesReadError("Malformed preferences: %s" % path)
values = [i[1] for i in prefs]
elif isinstance(prefs, dict):
values = prefs.values()
else:
raise PreferencesReadError("Malformed preferences: %s" % path)
types = (bool, basestring, int)
if [i for i in values
if not [isinstance(i, j) for j in types]]:
raise PreferencesReadError("Only bool, string, and int values allowed")
return prefs
@classmethod
def read_prefs(cls, path, pref_setter='user_pref'):
"""read preferences from (e.g.) prefs.js"""
comment = re.compile('/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/', re.MULTILINE)
token = '##//' # magical token
lines = [i.strip() for i in file(path).readlines() if i.strip()]
_lines = []
for line in lines:
if line.startswith('#'):
continue
if '//' in line:
line = line.replace('//', token)
_lines.append(line)
string = '\n'.join(_lines)
string = re.sub(comment, '', string)
retval = []
def pref(a, b):
retval.append((a, b))
lines = [i.strip().rstrip(';') for i in string.split('\n') if i.strip()]
_globals = {'retval': retval, 'true': True, 'false': False}
_globals[pref_setter] = pref
for line in lines:
try:
eval(line, _globals, {})
except SyntaxError:
print line
raise
# de-magic the token
for index, (key, value) in enumerate(retval):
if isinstance(value, basestring) and token in value:
retval[index] = (key, value.replace(token, '//'))
return retval
@classmethod
def write(_file, prefs, pref_string='user_pref("%s", %s);'):
"""write preferences to a file"""
if isinstance(_file, basestring):
f = file(_file, 'w')
else:
f = _file
if isinstance(prefs, dict):
prefs = prefs.items()
for key, value in prefs:
if value is True:
print >> f, pref_string % (key, 'true')
elif value is False:
print >> f, pref_string % (key, 'false')
elif isinstance(value, basestring):
print >> f, pref_string % (key, repr(string(value)))
else:
print >> f, pref_string % (key, value) # should be numeric!
if isinstance(_file, basestring):
f.close()
if __name__ == '__main__':
pass