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.
  • [get | view] (2021-05-11 08:51:53, 15.8 KB) [[attachment:dict4ini.py]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.