{{{ # Programmer: limodou # E-mail: limodou@gmail.com # # Copyleft 2006 limodou # # Distributed under the terms of the GPL (GNU Public License) # # NewEdit is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # version 0.1 # This program is used for parsing settings.py. So it has many limits. # It has some features: # * Remaining comments in settings, including comments in tuple. And if the # comments in tuple data type, they should be nearby the first '(' or the # last ')', other comments will be lost. # * You can delete a key, but when you save the settings, the key will be # comment but not be really delete # * You can also get a comment key, and if you set a new value to it, # the key will be uncomment. Or you can also remove the key object's delete flag # to uncomment the key, just like: # # obj = ini.get_obj('KEY') # obj.delete = False # * If the value is value, DjangoIni will read it as list, so you can deal with # the key as a list. But when it's saved, it'll be automaticaly converted to tuple, # just keeping the same type in settings.py # * The instance of DjangoIni acts as a dict object, so you can do like: # from DjangoIni import DjangoIni # ini = DjangoIni('settings.py') # ini['KEY'] if a key has been delete, so it'll complain a KeyError Exception # ini.get('KEY', defaultvalue) if a key has been delete, it'll also can be returned # ini.keys() omit the key deleted # ini.has_key('KEY') omit the key deleted # ini.get_obj('KEY') can also get a delete obj # ini.save(filename or fileobj) saving the result to file # import codecs import copy import re import os import sys r_line = re.compile(r'(\w+)\s+=\s*([^#]+)') class DeleteException(Exception):pass class Node: def __init__(self, parent, key, old_value, lines, span=None, new=False, delete=False): self.parent = parent self.key = key self.value = copy.deepcopy(old_value) self.old_value = copy.deepcopy(old_value) self.lines = lines self.delete = delete self.new = new self.span = span def render(self): if self.lines[0] == -1: #new if isinstance(self.value, (tuple, list)): line = [] line.append('%s = (') for i in self.value: line.append(' '*4 + repr(i) + ',') line.append(')') else: line = ['%s = %r' % (self.key, self.value)] else: if len(self.lines) == 1: line = self.parent.get_lines(self.lines[0]) b, e = self.span line = ["%s = %r" % (self.key, self.value) + line[e:]] else: b, e = self.lines line = [self.parent.get_lines(b)] has_more = False for j in self.parent.get_lines(b+1, e-1): if j.strip().startswith('#'): line.append(j) else: has_more = True break for i in self.value: line.append(' '*4 + repr(i) + ',') line.append(')') if has_more: lines = self.parent.get_lines(b+1, e-1) lines.reverse() for j in lines: if j.strip().startswith('#'): line.insert(len(line)-1, j) else: break if self.delete: if self.new: return None else: return ['#'+x for x in line] return line class DjangoIni(object): def __init__(self, filename='', encoding='utf-8'): self._items = {} self._orders = {} self._id = 0 self._max_id = 99999 self._lines = [] self.filename = filename self.read(self.filename) def _add_order(self, key): self._id += 1 self._orders[key] = self._id def _add_max_order(self, key): self._max_id += 1 self._orders[key] = self._max_id def read(self, filename, encoding='utf-8'): if not filename: return dir = os.path.dirname(filename) sys.path.insert(0, dir) mod = __import__(os.path.splitext(os.path.basename(filename))[0]) sys.path.pop(0) f = codecs.open(filename, encoding=encoding) i = 0 for line in f: line = line.rstrip() self._lines.append(line) #saving all lines if line.startswith('#'): deleteflag = True line = line[1:] else: deleteflag = False b = r_line.search(line) if b: self._lines[-1] = line key, value = b.groups() lines = [i] x, y = b.span(2) if deleteflag: x = x + 1 value = value.strip() obj = Node(self, key, value, lines, span=(0, x+len(value)), delete=deleteflag) self._items[key] = obj self._add_order(key) if value == '(': s = ['('] while 1: line = f.next().rstrip() if line.startswith('#') and deleteflag: line = line[1:] self._lines.append(line) i += 1 if line != ')': t = line.lstrip() if not t.startswith('#'): s.append(line) else: obj.old_value = s obj.value = [x.strip()[:-1] for x in s if not x.strip().startswith('#')] lines.append(i+1) s.append(')') value = ''.join(s) break else: if value and value[0] in ("'", '"'): ch = value[0] pp = [ch] line_iter = iter(line[x+1:]) for j in line_iter: if j == '\\': pp.append(j) j = line_iter.next() pp.append(j) elif j == ch: pp.append(j) break else: pp.append(j) value = ''.join(pp) obj.span = (0, x + len(value)) if hasattr(mod, key): obj.value = getattr(mod, key) else: obj.value = eval(value) if isinstance(obj.value, tuple): obj.value = list(obj.value) i += 1 def get_lines(self, start, end=-1): if end <= start: return self._lines[start] else: return self._lines[start:end] def out(self): a = [(value, key) for key, value in self._orders.items()] a.sort() for i, key in a: print key, self._items[key].value, self._items[key].lines def __setitem__(self, name, value): obj = self._items.get(name, None) if not obj: obj = Node(self, name, value, [-1], new=True) self._items[name] = obj self._add_max_order(name) else: if obj.delete: obj.delete = False obj.value = value def __getitem__(self, name): obj = self._items.get(name, None) if obj: if obj.delete: raise DeleteException, 'The value has been deleted!' else: return obj.value else: raise KeyError, name def __delitem__(self, name): """set an item's delete flag, so the result will be commented in .py file""" obj = self._items.get(name, None) if obj: obj.delete = True else: raise KeyError, name def get(self, name, defaultvalue): """Get an item, but also can get deleted item""" obj = self._items.get(name, None) if obj: return obj.value else: return defaultvalue def get_obj(self, name): """Get an item, but also can get deleted item""" obj = self._items.get(name, None) if obj: return obj else: raise KeyError, name def keys(self): return self._items.keys() def values(self): return [obj.value for obj in self.items.values()] def has_key(self, name): return self._items.has_key(name) def save(self, filename=None, encoding='utf-8'): if not filename: filename = self.filename a = [(value, key) for key, value in self._orders.items()] a.sort() k = 0 s = [] last = [] for i, key in a: obj = self._items[key] b = obj.lines[0] while k < b: s.append(self.get_lines(k)) k += 1 if len(obj.lines) == 1: b = obj.lines[0] if b == -1: last.extend(obj.render()) else: s.extend(obj.render()) k = b + 1 else: b, e = obj.lines s.extend(obj.render()) k = e s.extend(last) if isinstance(filename, (str, unicode)): f = codecs.open(filename, 'w', encoding) f.write('\n'.join(s)) f.close() else: filename.write('\n'.join(s)) if __name__ == '__main__': ini = DjangoIni('settings.py') print ini.keys() print ini['LANGUAGE_CODE'] ini['ADMINS'].append(('limodou', 'limodou@gmail.com')) ini['DATABASE_ENGINE'] = '' ini['DEBUG'] = False ini['NEW'] = 'This is test' # ini.save('t.py') print '-----------------------------------------------' ini.save(sys.stdout) }}}