Attachment 'dict4ini.py'
Download 1 #coding=utf-8
2 # dump python dict to ini format file
3 # Author: limodou ([email protected])
4 # Copyleft BSD
5 # $Revision: 2096 $
6 # you can see http://wiki.woodpecker.org.cn/moin/Dict4Ini for more details
7 #
8 # Updates:
9 # 0.9.1-----------------------
10 # 2007/06/26
11 # Fix float convert bug
12 # 0.9-------------------------
13 # 2007/06/13
14 # Thanks for Victor Stinner giving a output format patch, so you can define your own
15 # output format "%s = %s" to anything you want, for example "%s=%s" or "%s:%s". And
16 # Dict4Ini will auto remove '%s' from the fromat string, and the strip() the rest of
17 # the string, then use it to split the key and value in Ini file. For example, if you
18 # using "%s:%s", Dict4Ini will get "%s:%s".replace('%s', '').strip(), then use ':' to
19 # split the line.
20 # 0.8-------------------------
21 # 2007/04/20
22 # Add exception process when parsing file
23 # Add BOM detect
24 # 0.7-------------------------
25 # 2007/04/19
26 # Fix '\' escape
27 # 0.6-------------------------
28 # 2006/01/18
29 # Fix ordereditems bug.
30 # 0.5-------------------------
31 # 2006/01/04
32 # Add ordereditems() method, so you can get the items according the ini file definition
33 # 2005/12/30
34 # Support onelevel parameter, you can set it True, than only one level can be used, so that
35 # the key can include section delimeter char in it.
36 # Support sectiondelimeter parmeter, you can set the section delimeter to which you want
37 # 0.4-------------------------
38 # 2005/12/12
39 # Fixed "\" bug in option's value
40 # 2005/12/09
41 # Adding dict() method, then you can change the DictIni object to dict type
42 # 2005/10/16
43 # Saving the order of the items
44 # Adding float format
45 #
46
47 __version__ = '0.9'
48
49 import sys
50 import locale
51 import os.path
52 import re
53
54 r_float = re.compile('\d*\.\d+')
55 section_delimeter = '/'
56
57 class DictNode(object):
58 def __init__(self, values, encoding=None, root=None, section=[], orders=[], sectiondelimeter=section_delimeter, onelevel=False, format="%s = %s"):
59 self._items = values
60 self._orders = orders
61 self._encoding = encoding
62 self._root = root
63 self._section = section
64 self._section_delimeter = sectiondelimeter
65 self._onelevel = onelevel
66 self._format = format
67
68 def __getitem__(self, name):
69 if self._items.has_key(name):
70 value = self._items[name]
71 if isinstance(value, dict):
72 return DictNode(value, self._encoding, self._root, self._section + [name], sectiondelimeter=self._section_delimeter, onelevel=self._onelevel, format=self._format)
73 else:
74 return value
75 else:
76 self._items[name] = {}
77 self._root.setorder(self.get_full_keyname(name))
78 return DictNode(self._items[name], self._encoding, self._root, self._section + [name], sectiondelimeter=self._section_delimeter, onelevel=self._onelevel, format=self._format)
79
80 def __setitem__(self, name, value):
81 if self._section_delimeter and self._section_delimeter in name:
82 if self._onelevel:
83 sec = name.split(self._section_delimeter, 1)
84 else:
85 sec = name.split(self._section_delimeter)
86 obj = self._items
87
88 _s = self._section[:]
89 for i in sec[:-1]:
90 _s.append(i)
91 if obj.has_key(i):
92 if isinstance(obj[i], dict):
93 obj = obj[i]
94 else:
95 obj[i] = {} #may lost some data
96 obj = obj[i]
97 else:
98 obj[i] = {}
99 self._root.setorder(self._section_delimeter.join(_s))
100 obj = obj[i]
101 obj[sec[-1]] = value
102 self._root.setorder(self._section_delimeter.join(_s + [sec[-1]]))
103 else:
104 self._items[name] = value
105 self._root.setorder(self.get_full_keyname(name))
106
107 def __delitem__(self, name):
108 if self._items.has_key(name):
109 del self._items[name]
110
111 def __repr__(self):
112 return repr(self._items)
113
114 def __getattr__(self, name):
115 return self.__getitem__(name)
116
117 def __setattr__(self, name, value):
118 if name.startswith('_'):
119 if name == '_comment':
120 self._root._comments[self._section_delimeter.join(self._section)] = value
121 else:
122 self.__dict__[name] = value
123 else:
124 self.__setitem__(name, value)
125
126 def comment(self, name, comment):
127 if name:
128 self._root._comments[self._section_delimeter.join(self._section + [name])] = comment
129 else:
130 self._root._comments[self._section_delimeter.join(self._section)] = comment
131
132 def __delattr__(self, name):
133 if self._items.has_key(name):
134 del self._items[name]
135
136 def __str__(self):
137 return repr(self._items)
138
139 def __len__(self):
140 return len(self._items)
141
142 def has_key(self, name):
143 return self._items.has_key(name)
144
145 def items(self):
146 return self._items.items()
147
148 def setdefault(self, name, value):
149 return self._items.setdefault(name, value)
150
151 def get(self, name, default=None):
152 return self._items.get(name, default)
153
154 def keys(self):
155 return self._items.keys()
156
157 def values(self):
158 return self._items.values()
159
160 def get_full_keyname(self, key):
161 return self._section_delimeter.join(self._section + [key])
162
163 def ordereditems(self, values, sec=[]):
164 s = []
165 for key, value in values.items():
166 s.append((self._root._orders.get(self._section_delimeter.join(sec + [key]), 99999), key, value))
167 s.sort()
168 return [(x, y) for z, x, y in s]
169
170
171 class DictIni(DictNode):
172 def __init__(self, inifile=None, values=None, encoding=None, commentdelimeter='#', sectiondelimeter=section_delimeter, onelevel=False, format="%s = %s"):
173 self._items = {}
174 self._inifile = inifile
175 self._root = self
176 self._section = []
177 self._commentdelimeter = commentdelimeter
178 self._comments = {}
179 self._orders = {}
180 self._ID = 1
181 self._encoding = getdefaultencoding(encoding)
182 self._section_delimeter = sectiondelimeter
183 self._format = format
184 assert not self
185 if not self._section_delimeter:
186 raise Exception, "section_delimeter cann't be empty!"
187 self._onelevel = onelevel
188 if values is not None:
189 self._items = values
190
191 if self._inifile and os.path.exists(self._inifile):
192 self.read(self._inifile, self._encoding)
193
194 def setfilename(self, filename):
195 self._inifile = filename
196
197 def getfilename(self):
198 return self._inifile
199
200 def save(self, inifile=None, encoding=None):
201 if inifile is None:
202 inifile = self._inifile
203
204 if isinstance(inifile, (str, unicode)):
205 f = file(inifile, 'w')
206 elif isinstance(inifile, file):
207 f = inifile
208 else:
209 f = inifile
210
211 if not f:
212 f = sys.stdout
213
214 if encoding is None:
215 encoding = self._encoding
216
217 f.write(self._savedict([], self._items, encoding))
218 if isinstance(inifile, (str, unicode)):
219 f.close()
220
221 def _savedict(self, section, values, encoding):
222 if values:
223 buf = []
224 default = []
225 for key, value in self.ordereditems(values, sec=section):
226 if isinstance(value, dict):
227 sec = section[:]
228 sec.append(key)
229 buf.append(self._savedict(sec, value, encoding))
230 else:
231 c = self._comments.get(self._section_delimeter.join(section + [key]), '')
232 if c:
233 lines = c.splitlines()
234 default.append('\n'.join(['%s %s' % (self._commentdelimeter, x) for x in lines]))
235
236 default.append(self._format % (key, uni_prt(value, encoding)))
237 if default:
238 buf.insert(0, '\n'.join(default))
239 buf.insert(0, '[%s]' % self._section_delimeter.join(section))
240 c = self._comments.get(self._section_delimeter.join(section), '')
241 if c:
242 lines = c.splitlines()
243 buf.insert(0, '\n'.join(['%s %s' % (self._commentdelimeter, x) for x in lines]))
244 return '\n'.join(buf + [''])
245 else:
246 return ''
247
248 def dict(self):
249 return self._dict(self)
250
251 def _dict(self, v):
252 if isinstance(v, tuple):
253 return tuple([self._dict(x) for x in v])
254 elif isinstance(v, list):
255 return [self._dict(x) for x in v]
256 elif isinstance(v, (dict, DictNode)):
257 d = {}
258 for key, value in v.items():
259 d[key] = self._dict(value)
260 return d
261 else:
262 return v
263
264 def read(self, inifile=None, encoding=None):
265 if inifile is None:
266 inifile = self._inifile
267
268 if isinstance(inifile, (str, unicode)):
269 try:
270 f = file(inifile, 'r')
271 except:
272 return #may raise Exception is better
273 elif isinstance(inifile, file):
274 f = inifile
275 else:
276 f = inifile
277
278 if not f:
279 f = sys.stdin
280
281 if encoding is None:
282 encoding = self._encoding
283
284 comments = []
285 section = ''
286 for lineno, line in enumerate(f.readlines()):
287 try:
288 if lineno == 0:
289 if line.startswith('\xEF\xBB\xBF'):
290 line = line[3:]
291 encoding = 'utf-8'
292 line = line.strip()
293 if not line: continue
294 if line.startswith(self._commentdelimeter):
295 comments.append(line[1:].lstrip())
296 continue
297 if line.startswith('['): #section
298 section = line[1:-1]
299 #if comment then set it
300 if comments:
301 self.comment(section, '\n'.join(comments))
302 comments = []
303 continue
304 key, value = line.split(self._format.replace('%s', '').strip(), 1)
305 key = key.strip()
306 value = process_value(value.strip(), encoding)
307 if section:
308 self.__setitem__(section + self._section_delimeter + key, value)
309 #if comment then set it
310 if comments:
311 self.__getitem__(section).comment(key, '\n'.join(comments))
312 comments = []
313 else:
314 self.__setitem__(key, value)
315 #if comment then set it
316 if comments:
317 self.comment(key, '\n'.join(comments))
318 comments = []
319 except Exception, err:
320 import traceback
321 traceback.print_exc()
322 print 'Error in [line %d]%s:' % (lineno, line)
323 print line
324 if isinstance(inifile, (str, unicode)):
325 f.close()
326
327 def setorder(self, key):
328 if not self._orders.has_key(key):
329 self._orders[key] = self._ID
330 self._ID += 1
331
332 def process_value(value, encoding=None):
333 length = len(value)
334 t = value
335 i = 0
336 r = []
337 buf = []
338 listflag = False
339 while i < length:
340 if t[i] == '"': #string quote
341 buf.append(t[i])
342 i += 1
343 while t[i] != '"' or (t[i] == '"' and t[i-1] == '\\'):
344 buf.append(t[i])
345 i += 1
346 buf.append(t[i])
347 i += 1
348 elif t[i] == ',':
349 r.append(''.join(buf).strip())
350 buf = []
351 i += 1
352 listflag = True
353 elif t[i] == 'u':
354 buf.append(t[i])
355 i += 1
356 else:
357 buf.append(t[i])
358 i += 1
359 while i < length and t[i] != ',':
360 buf.append(t[i])
361 i += 1
362 if buf:
363 r.append(''.join(buf).strip())
364 result = []
365 for i in r:
366 if i.isdigit():
367 result.append(int(i))
368 elif i and i.startswith('u"'):
369 result.append(unicode(unescstr(i[1:]), encoding))
370 else:
371 try:
372 b = float(i)
373 result.append(b)
374 except:
375 result.append(unescstr(i))
376
377 if listflag:
378 return result
379 elif result:
380 return result[0]
381 else:
382 return ''
383
384 unescapechars = {'"':'"', 't':'\t', 'r':'\r', 'n':'\n', '\\':'\\', 'a':'\a', 'f':'\f',
385 "'":"'", 'b':'\b', 'v':'\v'}
386 def unescstr(value):
387 if value.startswith('"') and value.endswith('"') or value.startswith("'") and value.endswith("'"):
388 s = []
389 i = 1
390 end = len(value) - 1
391 while i < end:
392 if value[i] == '\\' and unescapechars.has_key(value[i+1]):
393 s.append(unescapechars[value[i+1]])
394 i += 2
395 else:
396 s.append(value[i])
397 i += 1
398 value = ''.join(s)
399 return value
400
401 #escapechars = {'"':r'\"', '\t':r'\t', '\r':r'\r', '\n':r'\n', '\\':r'\\'}
402 def escstr(value):
403 # if value:
404 # v = repr(value).replace('"', r'\"')
405 # if v[0] == "'":
406 # return v[1:-1]
407 # else:
408 # return v[2:-1]
409 # else:
410 # return ''
411 s = []
412 for c in value:
413 if c == "'":
414 s.append(c)
415 continue
416 v = repr(c).replace('"', r'\"')
417 if '\\' in v and ord(c)<128:
418 if isinstance(c, str):
419 s.append(v[1:-1])
420 else:
421 s.append(v[2:-1])
422 else:
423 s.append(c)
424 return ''.join(s)
425
426 def getdefaultencoding(encoding):
427 if not encoding:
428 encoding = locale.getdefaultlocale()[1]
429 if not encoding:
430 encoding = sys.getfilesystemencoding()
431 if not encoding:
432 encoding = 'utf-8'
433 return encoding
434
435 def uni_prt(a, encoding=None):
436 s = []
437 if isinstance(a, (list, tuple)):
438 for i, k in enumerate(a):
439 s.append(uni_prt(k, encoding))
440 s.append(',')
441 elif isinstance(a, str):
442 t = escstr(a)
443 if (' ' in t) or (',' in t) or ('"' in t) or t.isdigit() or ('\\' in t):
444 s.append('"%s"' % t)
445 else:
446 s.append("%s" % t)
447 elif isinstance(a, unicode):
448 t = escstr(a)
449 s.append('u"%s"' % t.encode(encoding))
450 else:
451 s.append(str(a))
452 return ''.join(s)
453
454 if __name__ == '__main__':
455 d = DictIni('t.ini', format="%s:%s")
456 print d.p
457 print repr(d.s.t)
458 d._comment = 'Test\nTest2'
459 d.a = 'b'
460 d.b = 1
461 d['b'] = 3
462 d.c.d = (1,2,'b asf "aaa')
463 d['s']['t'] = u'中\n国'
464 d['s'].a = 1
465 d['m/m'] = 'testing'
466 d['p'] = r'\?'
467 d.t.m.p = '3'
468 d.save()
469
470 # p = DictIni()
471 # p.x = 1
472 #
473 # d.q = [p, p]
474 #
475 # x = d.dict()
476 # print type(x), x
477 # print type(x['t']['m']), x['t']['m']
478 # d.setfilename('test.ini')
479 # d.t.m.comment('p', 'PTesting')
480 # print d.getfilename()
481 # print '---------------------'
482 #
483 # d.save('test.ini')
484 #
485 # a = process_value('1,abc,"aa cc",," ,\\"sdf",u"aaa"', 'ascii')
486 # print a
487 # print uni_prt(a, 'utf-8')
488 #
489 # t = DictIni(inifile='test.ini')
490 # print t
491 #
492 # t.setfilename('test1.ini')
493 # t.save()
494
495 # t.setfilename('test2.ini')
496 # t.save()
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.