00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 __version__ = '0.4'
00019
00020 import sys
00021 import locale
00022 import os.path
00023 import re
00024
00025 r_float = re.compile('\d*\.\d+')
00026 section_delimeter = '/'
00027
00028 class DictNode(object):
00029 def __init__(self, values, encoding=None, root=None, section=[], orders=[]):
00030 self._items = values
00031 self._orders = orders
00032 self._encoding = encoding
00033 self._root = root
00034 self._section = section
00035
00036 def __getitem__(self, name):
00037 if self._items.has_key(name):
00038 value = self._items[name]
00039 if isinstance(value, dict):
00040 return DictNode(value, self._encoding, self._root, self._section + [name])
00041 else:
00042 return value
00043 else:
00044 self._items[name] = {}
00045 self._root.setorder(self.get_full_keyname(name))
00046 return DictNode(self._items[name], self._encoding, self._root, self._section + [name])
00047
00048 def __setitem__(self, name, value):
00049 if section_delimeter in name:
00050 sec = name.split(section_delimeter)
00051 obj = self._items
00052
00053 _s = self._section[:]
00054 for i in sec[:-1]:
00055 _s.append(i)
00056 if obj.has_key(i):
00057 if isinstance(obj[i], dict):
00058 obj = obj[i]
00059 else:
00060 obj[i] = {}
00061 obj = obj[i]
00062 else:
00063 obj[i] = {}
00064 self._root.setorder(section_delimeter.join(_s))
00065 obj = obj[i]
00066 obj[sec[-1]] = value
00067 self._root.setorder(section_delimeter.join(_s + [sec[-1]]))
00068 else:
00069 self._items[name] = value
00070 self._root.setorder(self.get_full_keyname(name))
00071
00072 def __delitem__(self, name):
00073 if self._items.has_key(name):
00074 del self._items[name]
00075
00076 def __repr__(self):
00077 return repr(self._items)
00078
00079 def __getattr__(self, name):
00080 return self.__getitem__(name)
00081
00082 def __setattr__(self, name, value):
00083 if name.startswith('_'):
00084 if name == '_comment':
00085 self._root._comments[section_delimeter.join(self._section)] = value
00086 else:
00087 self.__dict__[name] = value
00088 else:
00089 self.__setitem__(name, value)
00090
00091 def comment(self, name, comment):
00092 if name:
00093 self._root._comments[section_delimeter.join(self._section + [name])] = comment
00094 else:
00095 self._root._comments[section_delimeter.join(self._section)] = comment
00096
00097 def __delattr__(self, name):
00098 if self._items.has_key(name):
00099 del self._items[name]
00100
00101 def __str__(self):
00102 return repr(self._items)
00103
00104 def __len__(self):
00105 return len(self._items)
00106
00107 def has_key(self, name):
00108 return self._items.has_key(name)
00109
00110 def items(self):
00111 return self._items.items()
00112
00113 def setdefault(self, name, value):
00114 return self._items.setdefault(name, value)
00115
00116 def get(self, name, default=None):
00117 return self._items.get(name, default)
00118
00119 def keys(self):
00120 return self._items.keys()
00121
00122 def values(self):
00123 return self._items.values()
00124
00125 def get_full_keyname(self, key):
00126 return section_delimeter.join(self._section + [key])
00127
00128 class DictIni(DictNode):
00129 def __init__(self, inifile=None, values=None, encoding=None, commentdelimeter='#'):
00130 self._items = {}
00131 self._inifile = inifile
00132 self._root = self
00133 self._section = []
00134 self._commentdelimeter = commentdelimeter
00135 self._comments = {}
00136 self._orders = {}
00137 self._ID = 1
00138 self._encoding = getdefaultencoding(encoding)
00139 if values is not None:
00140 self._items = values
00141
00142 if self._inifile and os.path.exists(self._inifile):
00143 self.read(self._inifile, self._encoding)
00144
00145 def setfilename(self, filename):
00146 self._inifile = filename
00147
00148 def getfilename(self):
00149 return self._inifile
00150
00151 def save(self, inifile=None, encoding=None):
00152 if inifile is None:
00153 inifile = self._inifile
00154
00155 if isinstance(inifile, (str, unicode)):
00156 f = file(inifile, 'w')
00157 elif isinstance(inifile, file):
00158 f = inifile
00159 else:
00160 f = inifile
00161
00162 if not f:
00163 f = sys.stdout
00164
00165 if encoding is None:
00166 encoding = self._encoding
00167
00168 f.write(self._savedict([], self._items, encoding))
00169 if isinstance(inifile, (str, unicode)):
00170 f.close()
00171
00172 def _savedict(self, section, values, encoding):
00173 if values:
00174 buf = []
00175 default = []
00176 for key, value in self._getorderitems(values.items()):
00177 if isinstance(value, dict):
00178 sec = section[:]
00179 sec.append(key)
00180 buf.append(self._savedict(sec, value, encoding))
00181 else:
00182 c = self._comments.get(section_delimeter.join(section + [key]), '')
00183 if c:
00184 lines = c.splitlines()
00185 default.append('\n'.join(['%s %s' % (self._commentdelimeter, x) for x in lines]))
00186
00187 default.append("%s = %s" % (key, uni_prt(value, encoding)))
00188 if default:
00189 buf.insert(0, '\n'.join(default))
00190 buf.insert(0, '[%s]' % section_delimeter.join(section))
00191 c = self._comments.get(section_delimeter.join(section), '')
00192 if c:
00193 lines = c.splitlines()
00194 buf.insert(0, '\n'.join(['%s %s' % (self._commentdelimeter, x) for x in lines]))
00195 return '\n'.join(buf + [''])
00196 else:
00197 return ''
00198
00199 def dict(self):
00200 return self._dict(self)
00201
00202 def _dict(self, v):
00203 if isinstance(v, tuple):
00204 return tuple([self._dict(x) for x in v])
00205 elif isinstance(v, list):
00206 return [self._dict(x) for x in v]
00207 elif isinstance(v, (dict, DictNode)):
00208 d = {}
00209 for key, value in v.items():
00210 d[key] = self._dict(value)
00211 return d
00212 else:
00213 return v
00214
00215 def read(self, inifile=None, encoding=None):
00216 if inifile is None:
00217 inifile = self._inifile
00218
00219 if isinstance(inifile, (str, unicode)):
00220 try:
00221 f = file(inifile, 'r')
00222 except:
00223 return
00224 elif isinstance(inifile, file):
00225 f = inifile
00226 else:
00227 f = inifile
00228
00229 if not f:
00230 f = sys.stdin
00231
00232 if encoding is None:
00233 encoding = self._encoding
00234
00235 comments = []
00236 section = ''
00237 for line in f.readlines():
00238 line = line.strip()
00239 if not line: continue
00240 if line.startswith(self._commentdelimeter):
00241 comments.append(line[1:].lstrip())
00242 continue
00243 if line.startswith('['):
00244 section = line[1:-1]
00245
00246 if comments:
00247 self.comment(section, '\n'.join(comments))
00248 comments = []
00249 continue
00250 key, value = line.split('=', 1)
00251 key = key.strip()
00252 value = process_value(value.strip(), encoding)
00253 if section:
00254 self.__setitem__(section + section_delimeter + key, value)
00255
00256 if comments:
00257 self.__getitem__(section).comment(key, '\n'.join(comments))
00258 comments = []
00259 else:
00260 self.__setitem__(key, value)
00261
00262 if comments:
00263 self.comment(key, '\n'.join(comments))
00264 comments = []
00265 if isinstance(inifile, (str, unicode)):
00266 f.close()
00267
00268 def setorder(self, key):
00269 if not self._orders.has_key(key):
00270 self._orders[key] = self._ID
00271 self._ID += 1
00272
00273 def _getorderitems(self, values):
00274 s = []
00275 for key, value in values:
00276 s.append((self._orders.get(key, 99999), key, value))
00277 s.sort()
00278 return [(x, y) for z, x, y in s]
00279
00280 def process_value(value, encoding=None):
00281 length = len(value)
00282 t = value
00283 i = 0
00284 r = []
00285 buf = []
00286 listflag = False
00287 while i < length:
00288 if t[i] == '"':
00289 buf.append(t[i])
00290 i += 1
00291 while t[i] != '"' or (t[i] == '"' and t[i-1] == '\\'):
00292 buf.append(t[i])
00293 i += 1
00294 buf.append(t[i])
00295 i += 1
00296 elif t[i] == ',':
00297 r.append(''.join(buf))
00298 buf = []
00299 i += 1
00300 listflag = True
00301 elif t[i] == 'u':
00302 buf.append(t[i])
00303 i += 1
00304 else:
00305 buf.append(t[i])
00306 i += 1
00307 while i < length and t[i] != ',':
00308 buf.append(t[i])
00309 i += 1
00310 if buf:
00311 r.append(''.join(buf))
00312 result = []
00313 for i in r:
00314 if i.isdigit():
00315 result.append(int(i))
00316 elif i and i.startswith('u"'):
00317 result.append(unicode(unescstr(i[1:]), encoding))
00318 else:
00319 b = r_float.match(i)
00320 if b:
00321 result.append(float(b.group()))
00322 else:
00323 result.append(unescstr(i))
00324
00325 if listflag:
00326 return result
00327 elif result:
00328 return result[0]
00329 else:
00330 return ''
00331
00332 unescapechars = {'"':'"', 't':'\t', 'r':'\r', 'n':'\n', '\\':'\\'}
00333 def unescstr(value):
00334 if value.startswith('"') and value.endswith('"'):
00335 s = []
00336 i = 1
00337 end = len(value) - 1
00338 while i < end:
00339 if value[i] == '\\' and unescapechars.has_key(value[i+1]):
00340 s.append(unescapechars[value[i+1]])
00341 i += 2
00342 else:
00343 s.append(value[i])
00344 i += 1
00345 value = ''.join(s)
00346 return value
00347
00348 escapechars = {'"':r'\"', '\t':r'\t', '\r':r'\r', '\n':r'\n', '\\':r'\\'}
00349 def escstr(value):
00350 s = []
00351 for c in value:
00352 if escapechars.has_key(c):
00353 s.append(escapechars[c])
00354 else:
00355 s.append(c)
00356 return ''.join(s)
00357
00358 def getdefaultencoding(encoding):
00359 if not encoding:
00360 encoding = locale.getdefaultlocale()[1]
00361 if not encoding:
00362 encoding = sys.getfilesystemencoding()
00363 if not encoding:
00364 encoding = 'utf-8'
00365 return encoding
00366
00367 def uni_prt(a, encoding=None):
00368 s = []
00369 if isinstance(a, (list, tuple)):
00370 for i, k in enumerate(a):
00371 s.append(uni_prt(k, encoding))
00372 s.append(',')
00373 elif isinstance(a, str):
00374 t = escstr(a)
00375 if ' ' in t or ',' in t or t.isdigit():
00376 s.append('"%s"' % t)
00377 else:
00378 s.append("%s" % t)
00379 elif isinstance(a, unicode):
00380 t = escstr(a)
00381 s.append('u"%s"' % t.encode(encoding))
00382 else:
00383 s.append(str(a))
00384 return ''.join(s)
00385
00386 if __name__ == '__main__':
00387 d = DictIni('t.ini')
00388 d._comment = 'Test\nTest2'
00389 d.a = 'b'
00390 d.b = 1
00391 d['b'] = 3
00392 d.c.d = (1,2,'b asf aaa')
00393 d['s']['t'] = u'中国'
00394 d['s'].a = 1
00395 d['m/m'] = 'testing'
00396 d.t.m.p = '3'
00397 d.save()
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425