OLD | NEW |
(Empty) | |
| 1 # This Source Code Form is subject to the terms of the Mozilla Public |
| 2 # License, v. 2.0. If a copy of the MPL was not distributed with this file, |
| 3 # You can obtain one at http://mozilla.org/MPL/2.0/. |
| 4 |
| 5 """ |
| 6 user preferences |
| 7 """ |
| 8 |
| 9 import os |
| 10 import re |
| 11 from ConfigParser import SafeConfigParser as ConfigParser |
| 12 |
| 13 try: |
| 14 import json |
| 15 except ImportError: |
| 16 import simplejson as json |
| 17 |
| 18 class PreferencesReadError(Exception): |
| 19 """read error for prefrences files""" |
| 20 |
| 21 |
| 22 class Preferences(object): |
| 23 """assembly of preferences from various sources""" |
| 24 |
| 25 def __init__(self, prefs=None): |
| 26 self._prefs = [] |
| 27 if prefs: |
| 28 self.add(prefs) |
| 29 |
| 30 def add(self, prefs, cast=False): |
| 31 """ |
| 32 - cast: whether to cast strings to value, e.g. '1' -> 1 |
| 33 """ |
| 34 # wants a list of 2-tuples |
| 35 if isinstance(prefs, dict): |
| 36 prefs = prefs.items() |
| 37 if cast: |
| 38 prefs = [(i, self.cast(j)) for i, j in prefs] |
| 39 self._prefs += prefs |
| 40 |
| 41 def add_file(self, path): |
| 42 """a preferences from a file""" |
| 43 self.add(self.read(path)) |
| 44 |
| 45 def __call__(self): |
| 46 return self._prefs |
| 47 |
| 48 @classmethod |
| 49 def cast(cls, value): |
| 50 """ |
| 51 interpolate a preference from a string |
| 52 from the command line or from e.g. an .ini file, there is no good way to
denote |
| 53 what type the preference value is, as natively it is a string |
| 54 - integers will get cast to integers |
| 55 - true/false will get cast to True/False |
| 56 - anything enclosed in single quotes will be treated as a string with th
e ''s removed from both sides |
| 57 """ |
| 58 |
| 59 if not isinstance(value, basestring): |
| 60 return value # no op |
| 61 quote = "'" |
| 62 if value == 'true': |
| 63 return True |
| 64 if value == 'false': |
| 65 return False |
| 66 try: |
| 67 return int(value) |
| 68 except ValueError: |
| 69 pass |
| 70 if value.startswith(quote) and value.endswith(quote): |
| 71 value = value[1:-1] |
| 72 return value |
| 73 |
| 74 |
| 75 @classmethod |
| 76 def read(cls, path): |
| 77 """read preferences from a file""" |
| 78 |
| 79 section = None # for .ini files |
| 80 basename = os.path.basename(path) |
| 81 if ':' in basename: |
| 82 # section of INI file |
| 83 path, section = path.rsplit(':', 1) |
| 84 |
| 85 if not os.path.exists(path): |
| 86 raise PreferencesReadError("'%s' does not exist" % path) |
| 87 |
| 88 if section: |
| 89 try: |
| 90 return cls.read_ini(path, section) |
| 91 except PreferencesReadError: |
| 92 raise |
| 93 except Exception, e: |
| 94 raise PreferencesReadError(str(e)) |
| 95 |
| 96 # try both JSON and .ini format |
| 97 try: |
| 98 return cls.read_json(path) |
| 99 except Exception, e: |
| 100 try: |
| 101 return cls.read_ini(path) |
| 102 except Exception, f: |
| 103 for exception in e, f: |
| 104 if isinstance(exception, PreferencesReadError): |
| 105 raise exception |
| 106 raise PreferencesReadError("Could not recognize format of %s" %
path) |
| 107 |
| 108 |
| 109 @classmethod |
| 110 def read_ini(cls, path, section=None): |
| 111 """read preferences from an .ini file""" |
| 112 |
| 113 parser = ConfigParser() |
| 114 parser.read(path) |
| 115 |
| 116 if section: |
| 117 if section not in parser.sections(): |
| 118 raise PreferencesReadError("No section '%s' in %s" % (section, p
ath)) |
| 119 retval = parser.items(section, raw=True) |
| 120 else: |
| 121 retval = parser.defaults().items() |
| 122 |
| 123 # cast the preferences since .ini is just strings |
| 124 return [(i, cls.cast(j)) for i, j in retval] |
| 125 |
| 126 @classmethod |
| 127 def read_json(cls, path): |
| 128 """read preferences from a JSON blob""" |
| 129 |
| 130 prefs = json.loads(file(path).read()) |
| 131 |
| 132 if type(prefs) not in [list, dict]: |
| 133 raise PreferencesReadError("Malformed preferences: %s" % path) |
| 134 if isinstance(prefs, list): |
| 135 if [i for i in prefs if type(i) != list or len(i) != 2]: |
| 136 raise PreferencesReadError("Malformed preferences: %s" % path) |
| 137 values = [i[1] for i in prefs] |
| 138 elif isinstance(prefs, dict): |
| 139 values = prefs.values() |
| 140 else: |
| 141 raise PreferencesReadError("Malformed preferences: %s" % path) |
| 142 types = (bool, basestring, int) |
| 143 if [i for i in values |
| 144 if not [isinstance(i, j) for j in types]]: |
| 145 raise PreferencesReadError("Only bool, string, and int values allowe
d") |
| 146 return prefs |
| 147 |
| 148 @classmethod |
| 149 def read_prefs(cls, path, pref_setter='user_pref'): |
| 150 """read preferences from (e.g.) prefs.js""" |
| 151 |
| 152 comment = re.compile('/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/', re.MUL
TILINE) |
| 153 |
| 154 token = '##//' # magical token |
| 155 lines = [i.strip() for i in file(path).readlines() if i.strip()] |
| 156 _lines = [] |
| 157 for line in lines: |
| 158 if line.startswith('#'): |
| 159 continue |
| 160 if '//' in line: |
| 161 line = line.replace('//', token) |
| 162 _lines.append(line) |
| 163 string = '\n'.join(_lines) |
| 164 string = re.sub(comment, '', string) |
| 165 |
| 166 retval = [] |
| 167 def pref(a, b): |
| 168 retval.append((a, b)) |
| 169 lines = [i.strip().rstrip(';') for i in string.split('\n') if i.strip()] |
| 170 |
| 171 _globals = {'retval': retval, 'true': True, 'false': False} |
| 172 _globals[pref_setter] = pref |
| 173 for line in lines: |
| 174 try: |
| 175 eval(line, _globals, {}) |
| 176 except SyntaxError: |
| 177 print line |
| 178 raise |
| 179 |
| 180 # de-magic the token |
| 181 for index, (key, value) in enumerate(retval): |
| 182 if isinstance(value, basestring) and token in value: |
| 183 retval[index] = (key, value.replace(token, '//')) |
| 184 |
| 185 return retval |
| 186 |
| 187 @classmethod |
| 188 def write(_file, prefs, pref_string='user_pref("%s", %s);'): |
| 189 """write preferences to a file""" |
| 190 |
| 191 if isinstance(_file, basestring): |
| 192 f = file(_file, 'w') |
| 193 else: |
| 194 f = _file |
| 195 |
| 196 if isinstance(prefs, dict): |
| 197 prefs = prefs.items() |
| 198 |
| 199 for key, value in prefs: |
| 200 if value is True: |
| 201 print >> f, pref_string % (key, 'true') |
| 202 elif value is False: |
| 203 print >> f, pref_string % (key, 'false') |
| 204 elif isinstance(value, basestring): |
| 205 print >> f, pref_string % (key, repr(string(value))) |
| 206 else: |
| 207 print >> f, pref_string % (key, value) # should be numeric! |
| 208 |
| 209 if isinstance(_file, basestring): |
| 210 f.close() |
OLD | NEW |