#coding=utf-8
|
# dump python dict to ini format file
|
# Author: limodou (limodou@gmail.com)
|
# Copyleft BSD
|
# you can see http://wiki.woodpecker.org.cn/moin/Dict4Ini for more details
|
# and the new source project is in http://code.google.com/p/dict4ini/
|
#
|
# Updates:
|
# 0.9.7-----------------------
|
# 2011/01/18
|
# Fix #8
|
# 0.9.6-----------------------
|
# 2009/11/02
|
# Add update function
|
# 0.9.5-----------------------
|
# 2008/03/14
|
# Fix __init__.py bug, and you can import dict4ini from dict4ini package
|
# 0.9.4-----------------------
|
# 2008/02/16
|
# Fix comment process, if you comment with node._comment, then if the comment
|
# does not begin with '#'(commentdelimeter), it'll automatically add it.
|
# 0.9.3-----------------------
|
# 2007/09/22
|
# Improve the comment process
|
# Improve empty section process
|
# Add normal format support, so each value should be treated string type.
|
# 0.9.2.5-----------------------
|
# 2007/09/19
|
# Save boolean value to 0 or 1
|
# Add iterator support, and you can use for i in ini to iterator (key,value) now
|
# Add 'in' test support, so you can test if a key is in a ini via key in ini,
|
# it's the same with ini.has_key(key)
|
# 0.9.2.4-----------------------
|
# 2007/08/23
|
# Fix the end string is \" bug
|
# 0.9.2.3-----------------------
|
# 2007/08/08
|
# Fix sub section bug, for secretSection argument, dict4ini will encrypt all
|
# subsections of a secretSection, for example, secretSection=['a', 'c'], then
|
# all subsections of 'a' and 'c' will be encrypted.
|
# 0.9.2.2-----------------------
|
# 2007/07/08
|
# Add missing __init__.py file.
|
# 0.9.2.1-----------------------
|
# 2007/07/09 thanks to Mayowa Akinyemi
|
# Add the ability to protect specific sections.
|
# When use with the secretKey or hideData only section names listed in the
|
# secretSections parameter will be encrypted
|
# 0.9.2-----------------------
|
# 2007/07/03
|
# Add clear method
|
# Added encryption code thanks to Mayowa Akinyemi
|
# using the secretKey parameter will encrypt the values using
|
# Paul Rubin's p3.py encryption module
|
# using the hideData parameter will perform base64 enc/dec of the values
|
# 0.9.1-----------------------
|
# 2007/06/26
|
# Fix float convert bug
|
# 0.9-------------------------
|
# 2007/06/13
|
# Thanks for Victor Stinner giving a output format patch, so you can define your own
|
# output format "%s = %s" to anything you want, for example "%s=%s" or "%s:%s". And
|
# Dict4Ini will auto remove '%s' from the fromat string, and the strip() the rest of
|
# the string, then use it to split the key and value in Ini file. For example, if you
|
# using "%s:%s", Dict4Ini will get "%s:%s".replace('%s', '').strip(), then use ':' to
|
# split the line.
|
# 0.8-------------------------
|
# 2007/04/20
|
# Add exception process when parsing file
|
# Add BOM detect
|
# 0.7-------------------------
|
# 2007/04/19
|
# Fix '\' escape
|
# 0.6-------------------------
|
# 2006/01/18
|
# Fix ordereditems bug.
|
# 0.5-------------------------
|
# 2006/01/04
|
# Add ordereditems() method, so you can get the items according the ini file definition
|
# 2005/12/30
|
# Support onelevel parameter, you can set it True, than only one level can be used, so that
|
# the key can include section delimeter char in it.
|
# Support sectiondelimeter parmeter, you can set the section delimeter to which you want
|
# 0.4-------------------------
|
# 2005/12/12
|
# Fixed "\" bug in option's value
|
# 2005/12/09
|
# Adding dict() method, then you can change the DictIni object to dict type
|
# 2005/10/16
|
# Saving the order of the items
|
# Adding float format
|
#
|
|
__version__ = '0.9.6'
|
|
import sys
|
import locale
|
import os.path
|
|
section_delimeter = '/'
|
|
import base64
|
try:
|
import _p3encryption as crypt
|
except:
|
crypt = None
|
|
class DictNode(object):
|
def __init__(self, values, encoding=None, root=None, section=[], orders=[],
|
sectiondelimeter=section_delimeter, onelevel=False, format="%s = %s",
|
normal=False, commentdelimeter='#'):
|
self._items = values
|
self._orders = orders
|
self._encoding = encoding
|
self._root = root
|
self._section = section
|
self._section_delimeter = sectiondelimeter
|
self._onelevel = onelevel
|
self._format = format
|
self._normal = normal
|
self._commentdelimeter = commentdelimeter
|
|
def __getitem__(self, name):
|
if self._items.has_key(name):
|
value = self._items[name]
|
if isinstance(value, dict):
|
return DictNode(value, self._encoding, self._root, self._section + [name],
|
sectiondelimeter=self._section_delimeter, onelevel=self._onelevel,
|
format=self._format, normal=self._normal, commentdelimeter=self._commentdelimeter)
|
else:
|
return value
|
else:
|
self._items[name] = {}
|
self._root.setorder(self.get_full_keyname(name))
|
return DictNode(self._items[name], self._encoding, self._root, self._section + [name],
|
sectiondelimeter=self._section_delimeter, onelevel=self._onelevel,
|
format=self._format, normal=self._normal, commentdelimeter=self._commentdelimeter)
|
|
def __setitem__(self, name, value):
|
if not self._normal and self._section_delimeter and self._section_delimeter in name:
|
if self._onelevel:
|
sec = name.split(self._section_delimeter, 1)
|
else:
|
sec = name.split(self._section_delimeter)
|
obj = self._items
|
|
_s = self._section[:]
|
for i in sec[:-1]:
|
_s.append(i)
|
if obj.has_key(i):
|
if isinstance(obj[i], dict):
|
obj = obj[i]
|
else:
|
obj[i] = {} #may lost some data
|
obj = obj[i]
|
else:
|
obj[i] = {}
|
self._root.setorder(self._section_delimeter.join(_s))
|
obj = obj[i]
|
obj[sec[-1]] = value
|
self._root.setorder(self._section_delimeter.join(_s + [sec[-1]]))
|
else:
|
self._items[name] = value
|
self._root.setorder(self.get_full_keyname(name))
|
|
def __delitem__(self, name):
|
if self._items.has_key(name):
|
del self._items[name]
|
|
def __repr__(self):
|
return repr(self._items)
|
|
def __getattr__(self, name):
|
return self.__getitem__(name)
|
|
def __setattr__(self, name, value):
|
if name.startswith('_'):
|
if name == '_comment':
|
self._root._comments[self._section_delimeter.join(self._section)] = self._get_comment_value(value)
|
else:
|
self.__dict__[name] = value
|
else:
|
self.__setitem__(name, value)
|
|
def _get_comment_value(self, value):
|
lines = value.splitlines()
|
s = []
|
for x in lines:
|
if not x.startswith(self._commentdelimeter):
|
s.append(self._commentdelimeter + x)
|
else:
|
s.append(x)
|
return '\n'.join(s)
|
|
def comment(self, name, comment):
|
comment = self._get_comment_value(comment)
|
if name:
|
self._root._comments[self._section_delimeter.join(self._section + [name])] = comment
|
else:
|
self._root._comments[self._section_delimeter.join(self._section)] = comment
|
|
def __delattr__(self, name):
|
if self._items.has_key(name):
|
del self._items[name]
|
|
def __str__(self):
|
return repr(self._items)
|
|
def __len__(self):
|
return len(self._items)
|
|
#add iterator support
|
def __iter__(self):
|
return self._items.iteritems()
|
|
def has_key(self, name):
|
return self._items.has_key(name)
|
|
#add in test support
|
def __contains__(self, name):
|
return self.has_key(name)
|
|
def items(self):
|
return self._items.items()
|
|
def setdefault(self, name, value):
|
return self._items.setdefault(name, value)
|
|
def get(self, name, default=None):
|
return self._items.get(name, default)
|
|
def keys(self):
|
return self._items.keys()
|
|
def values(self):
|
return self._items.values()
|
|
def get_full_keyname(self, key):
|
return self._section_delimeter.join(self._section + [key])
|
|
def ordereditems(self, values, sec=[]):
|
s = []
|
for key, value in values.items():
|
s.append((self._root._orders.get(self._section_delimeter.join(sec + [key]), 99999), key, value))
|
s.sort()
|
return [(x, y) for z, x, y in s]
|
|
def clear(self):
|
self._items = {}
|
self._orders = []
|
self._section = []
|
|
def update(self, d):
|
for k, v in d.items():
|
self[k] = v
|
|
class DictIni(DictNode):
|
def __init__(self, inifile=None, values=None, encoding=None,
|
commentdelimeter='#', sectiondelimeter=section_delimeter,
|
onelevel=False, format="%s = %s", normal=False,
|
hideData=False, secretKey=None, secretSections=None ):
|
|
self._items = {}
|
self._inifile = inifile
|
self._root = self
|
self._section = []
|
self._commentdelimeter = commentdelimeter
|
self._comments = {}
|
self._orders = {}
|
self._ID = 1
|
self._encoding = getdefaultencoding(encoding)
|
self._section_delimeter = sectiondelimeter
|
self._format = format
|
self._secretKey = secretKey
|
self._hideData = hideData
|
self._secretSections = secretSections
|
self._onelevel = onelevel
|
self._normal = normal
|
if type(secretSections) is str:
|
self._secretSections = secretSections.split(',')
|
assert not self
|
if not self._section_delimeter:
|
raise Exception, "section_delimeter cann't be empty!"
|
if values is not None:
|
self._items = values
|
|
if self._inifile and os.path.exists(self._inifile):
|
self.read(self._inifile, self._encoding)
|
|
def setfilename(self, filename):
|
self._inifile = filename
|
|
def getfilename(self):
|
return self._inifile
|
|
def save(self, inifile=None, encoding=None):
|
if inifile is None:
|
inifile = self._inifile
|
|
if isinstance(inifile, (str, unicode)):
|
f = file(inifile, 'w')
|
elif isinstance(inifile, file):
|
f = inifile
|
else:
|
f = inifile
|
|
if not f:
|
f = sys.stdout
|
|
if encoding is None:
|
encoding = self._encoding
|
|
f.write(self._savedict([], self._items, encoding))
|
if isinstance(inifile, (str, unicode)):
|
f.close()
|
|
def _savedict(self, section, values, encoding):
|
if values:
|
buf = []
|
default = []
|
for key, value in self.ordereditems(values, sec=section):
|
if isinstance(value, dict):
|
sec = section[:]
|
sec.append(key)
|
buf.append(self._savedict(sec, value, encoding))
|
else:
|
c = self._comments.get(self._section_delimeter.join(section + [key]), '')
|
if c:
|
lines = c.splitlines()
|
default.append('\n'.join(lines))
|
default.append(self._format % (uni_prt(key, encoding), self.uni_str(value, encoding, section)))
|
if default:
|
buf.insert(0, '\n'.join(default))
|
if section:
|
buf.insert(0, '[%s]' % self._section_delimeter.join(section))
|
c = self._comments.get(self._section_delimeter.join(section), '')
|
if c:
|
lines = c.splitlines()
|
buf.insert(0, '\n'.join(lines))
|
return '\n'.join(buf + [''])
|
else:
|
buf = []
|
if section:
|
buf.insert(0, '[%s]' % self._section_delimeter.join(section))
|
c = self._comments.get(self._section_delimeter.join(section), '')
|
if c:
|
lines = c.splitlines()
|
buf.insert(0, '\n'.join(['%s' % x for x in lines]))
|
return '\n'.join(buf)
|
return ''
|
|
def dict(self):
|
return self._dict(self)
|
|
def _dict(self, v):
|
if isinstance(v, tuple):
|
return tuple([self._dict(x) for x in v])
|
elif isinstance(v, list):
|
return [self._dict(x) for x in v]
|
elif isinstance(v, (dict, DictNode)):
|
d = {}
|
for key, value in v.items():
|
d[key] = self._dict(value)
|
return d
|
else:
|
return v
|
|
def read(self, inifile=None, encoding=None):
|
if inifile is None:
|
inifile = self._inifile
|
|
if isinstance(inifile, (str, unicode)):
|
try:
|
f = file(inifile, 'r')
|
except:
|
return #may raise Exception is better
|
elif isinstance(inifile, file):
|
f = inifile
|
else:
|
f = inifile
|
|
if not f:
|
f = sys.stdin
|
|
if encoding is None:
|
encoding = self._encoding
|
|
comments = []
|
section = ''
|
for lineno, line in enumerate(f.readlines()):
|
try:
|
if lineno == 0:
|
if line.startswith('\xEF\xBB\xBF'):
|
line = line[3:]
|
encoding = 'utf-8'
|
line = line.strip()
|
if not line:
|
if comments:
|
comments.append('')
|
continue
|
else:
|
continue
|
if line.startswith(self._commentdelimeter):
|
comments.append(line.rstrip())
|
continue
|
if line.startswith('['): #section
|
section = line[1:-1]
|
#if comment then set it
|
if comments:
|
self.comment(section, '\n'.join(comments))
|
comments = []
|
self.__setitem__(section, {})
|
continue
|
key, value = line.split(self._format.replace('%s', '').strip(), 1)
|
key = key.strip()
|
value = self.process_value(value.strip(), encoding, section)
|
if section:
|
if self._normal:
|
self[section][key] = value
|
else:
|
self.__setitem__(section + self._section_delimeter + key, value)
|
#if comment then set it
|
if comments:
|
self[section].comment(key, '\n'.join(comments))
|
# self.__getitem__(section).comment(key, '\n'.join(comments))
|
comments = []
|
else:
|
self[key] = value
|
# self.__setitem__(key, value)
|
#if comment then set it
|
if comments:
|
self.comment(key, '\n'.join(comments))
|
comments = []
|
except Exception, err:
|
import traceback
|
traceback.print_exc()
|
print 'Error in [line %d]: %s' % (lineno, line)
|
print line
|
if isinstance(inifile, (str, unicode)):
|
f.close()
|
|
def setorder(self, key):
|
if not self._orders.has_key(key):
|
self._orders[key] = self._ID
|
self._ID += 1
|
|
def clear(self):
|
self._items = {}
|
self._section = []
|
self._comments = {}
|
self._orders = {}
|
|
def process_value(self, value, encoding=None, section=None):
|
value = self.protect_value(value, 1, section)
|
|
if self._normal:
|
return value
|
|
length = len(value)
|
t = value
|
i = 0
|
r = []
|
buf = []
|
listflag = False
|
while i < length:
|
if t[i] == '"': #string quote
|
buf.append(t[i])
|
i += 1
|
while t[i] != '"':
|
if t[i] == '\\':
|
buf.append(t[i])
|
buf.append(t[i+1])
|
i += 2
|
else:
|
buf.append(t[i])
|
i += 1
|
buf.append(t[i])
|
i += 1
|
elif t[i] == ',':
|
r.append(''.join(buf).strip())
|
buf = []
|
i += 1
|
listflag = True
|
elif t[i] == 'u':
|
buf.append(t[i])
|
i += 1
|
else:
|
buf.append(t[i])
|
i += 1
|
while i < length and t[i] != ',':
|
buf.append(t[i])
|
i += 1
|
if buf:
|
r.append(''.join(buf).strip())
|
result = []
|
for i in r:
|
if i.isdigit():
|
result.append(int(i))
|
elif i and i.startswith('u"'):
|
result.append(unicode(unescstr(i[1:]), encoding))
|
else:
|
try:
|
b = float(i)
|
result.append(b)
|
except:
|
result.append(unescstr(i))
|
|
if listflag:
|
return result
|
elif result:
|
return result[0]
|
else:
|
return ''
|
|
def protect_value(self, value, mode=0, section=None):
|
if self._secretSections is not None and section is not None:
|
for s in self._secretSections:
|
if section.startswith(s):
|
break
|
else:
|
return value
|
|
if mode == 0:
|
# encrypt
|
if crypt != None and self._secretKey != None :
|
cipher = crypt.p3_encrypt(value, self._secretKey)
|
return base64.b64encode(cipher)
|
elif self._hideData is True:
|
return base64.b64encode(value)
|
else:
|
# decrypt
|
if crypt != None and self._secretKey != None :
|
cipher = base64.b64decode(value)
|
return crypt.p3_decrypt(cipher, self._secretKey)
|
elif self._hideData is True:
|
return base64.b64decode(value)
|
|
return value
|
|
def uni_str(self, a, encoding=None, section=None):
|
if section and isinstance(section, list):
|
current_section = section[0]
|
else:
|
current_section = section
|
|
if self._normal:
|
if isinstance(a, (int, float, str)):
|
v = str(a)
|
elif isinstance(a, unicode):
|
v = a.encode(encoding)
|
else:
|
print 'Error to save: does not support this type %r' % type(a)
|
return
|
else:
|
v = uni_prt(a, encoding)
|
return self.protect_value(v, section=self._section_delimeter.join(section))
|
|
|
unescapechars = {'"':'"', 't':'\t', 'r':'\r', 'n':'\n', '\\':'\\', 'a':'\a', 'f':'\f',
|
"'":"'", 'b':'\b', 'v':'\v'}
|
def unescstr(value):
|
if value.startswith('"') and value.endswith('"') or value.startswith("'") and value.endswith("'"):
|
s = []
|
i = 1
|
end = len(value) - 1
|
while i < end:
|
if value[i] == '\\' and unescapechars.has_key(value[i+1]):
|
s.append(unescapechars[value[i+1]])
|
i += 2
|
else:
|
s.append(value[i])
|
i += 1
|
value = ''.join(s)
|
return value
|
|
def escstr(value):
|
s = []
|
for c in value:
|
if c == "'":
|
s.append(c)
|
continue
|
v = repr(c).replace('"', r'\"')
|
if '\\' in v and ord(c)<128:
|
if isinstance(c, str):
|
s.append(v[1:-1])
|
else:
|
s.append(v[2:-1])
|
else:
|
s.append(c)
|
return ''.join(s)
|
|
def uni_prt(a, encoding=None):
|
s = []
|
if isinstance(a, (list, tuple)):
|
for i, k in enumerate(a):
|
s.append(uni_prt(k, encoding))
|
s.append(',')
|
elif isinstance(a, str):
|
t = escstr(a)
|
if (' ' in t) or (',' in t) or ('"' in t) or t.isdigit() or ('\\' in t):
|
s.append('"%s"' % t)
|
else:
|
s.append("%s" % t)
|
elif isinstance(a, unicode):
|
t = escstr(a)
|
s.append('u"%s"' % t.encode(encoding))
|
elif isinstance(a, bool):
|
s.append(str(int(a)))
|
else:
|
s.append(str(a))
|
|
return ''.join(s)
|
|
def getdefaultencoding(encoding):
|
import codecs
|
|
try:
|
if not encoding:
|
encoding = locale.getdefaultlocale()[1]
|
codecs.lookup(encoding)
|
except:
|
encoding = 'utf-8'
|
return encoding
|
|
if __name__ == '__main__':
|
d = DictIni('t.ini', format="%s:%s", secretKey='mayowa')
|
print d
|
d._comment = 'Test\nTest2'
|
d.a = 'b'
|
d.b = 1
|
d['b'] = 3
|
d.c.d = (1,2,'b asf "aaa')
|
d['s']['t'] = u'涓浗'
|
d['s'].a = 1
|
d['m/m'] = 'testing'
|
d['p'] = r'\?'
|
d.t.m.p = 'd:\\'
|
print d
|
d.save()
|
|
d = DictIni('t.ini', format="%s:%s", secretKey='mayowa')
|
print d.p, d.t.m.p
|
|
d = DictIni('t2.ini', format="%s:%s", hideData=True)
|
d.a.a = 'mama'
|
d.a.b = 'lubs me!'
|
|
d.b.a = 'i'
|
d.b.b = 'lub bosunmi!'
|
|
d.c.a = 'dada'
|
d.c.b = 'lubs me too!'
|
d.save()
|
|
d = DictIni('t2.ini', format="%s:%s", hideData=True)
|
print d.a.a, d.a.b
|
print d.b.a, d.b.b
|
print d.c.a, d.c.b
|
|
# secret sections test
|
d = DictIni('t3.ini', format="%s:%s", hideData=True, secretSections=['a', 'c/c'])
|
d.a.a = 'mama'
|
d.a.b = 'lubs me!'
|
|
d.b.a = 'i'
|
d.b.b = 'lub bosunmi!'
|
|
d.c.a = 'dada'
|
d.c.b = 'lubs me too!'
|
d.c.c.a = 'far out!'
|
d.c.c.b.a = 'ppppp'
|
|
d.save()
|
|
d = DictIni('t3.ini', format="%s:%s", hideData=True, secretSections=['a', 'c/c'])
|
print d.a.a, d.a.b
|
print d.b.a, d.b.b
|
print d.c.a, d.c.b
|
print d.c.c.b.a
|