#!/usr/bin/python import tokenize import string TABSONLY = 'TABSONLY' SPACESONLY = 'SPACESONLY' MIXED = 'MIXED' class PyText: def __init__(self, fnm, optdict): self.optdict = optdict self.fnm = fnm self.txt = open(self.fnm, 'r').readlines() self.indents = [(0, 0, )] self.lnndx = 0 self.indentndx = 0 def getline(self): if self.lnndx < len(self.txt): txt = self.txt[self.lnndx] self.lnndx = self.lnndx + 1 else: txt = '' return txt def tokeneater(self, type, token, start, end, line): if type == tokenize.INDENT: (lvl, s) = self.indents[-1] self.indents[-1] = (lvl, s, start[0]-1) self.indents.append((lvl+1, start[0]-1,)) elif type == tokenize.DEDENT: (lvl, s) = self.indents[-1] self.indents[-1] = (lvl, s, start[0]-1) self.indents.append((lvl-1, start[0]-1,)) elif type == tokenize.ENDMARKER: (lvl, s) = self.indents[-1] self.indents[-1] = (lvl, s, len(self.txt)) def split(self, ln): content = string.lstrip(ln) if not content: return ('', '\n') lead = ln[:len(ln) - len(content)] lead = string.expandtabs(lead) return (lead, content) def process(self): style = self.optdict.get('style', TABSONLY) indent = string.atoi(self.optdict.get('indent', '4')) tabsz = string.atoi(self.optdict.get('tabs', '8')) print 'file %s -> style %s, tabsize %d, indent %d' % (self.fnm, style, tabsz, indent) tokenize.tokenize(self.getline, self.tokeneater) #import pprint #pprint.pprint(self.indents) new = [] for (lvl, s, e) in self.indents: if s >= len(self.txt): break if s == e: continue oldlead, content = self.split(self.txt[s]) #print "oldlead", len(oldlead), `oldlead` if style == TABSONLY: newlead = '\t'*lvl elif style == SPACESONLY: newlead = ' '*(indent*lvl) else: sz = indent*lvl t,spcs = divmod(sz, tabsz) newlead = '\t'*t + ' '*spcs new.append(newlead + content) for ln in self.txt[s+1:e]: lead, content = self.split(ln) #print "lead:", len(lead) new.append(newlead + lead[len(oldlead):] + content) self.save(new) #print "---", self.fnm #for ln in new: # print ln, #print def save(self, txt): bakname = os.path.splitext(self.fnm)[0]+'.bak' print "backing up", self.fnm, "to", bakname #print os.getcwd() try: os.rename(self.fnm, bakname) except os.error: os.remove(bakname) os.rename(self.fnm, bakname) open(self.fnm, 'w').writelines(txt) def test(): tc = PyText('test1.py') tc.process() tc = PyText('test1.py') tc.process(style=TABSONLY) tc = PyText('test1.py') tc.process(style=MIXED, indent=4, tabs=8) tc = PyText('test1.py') tc.process(style=MIXED, indent=2, tabs=8) def cleanfile(fnm, d): if os.path.isdir(fnm) and not os.path.islink(fnm): names = os.listdir(fnm) for name in names: fullnm = os.path.join(fnm, name) if (os.path.isdir(fullnm) and not os.path.islink(fullnm)) or \ os.path.normcase(fullnm[-3:]) == ".py": cleanfile(fullnm, d) return tc = PyText(fnm, d) tc.process() usage="""\ %s [options] [path...] options -T : reformat to TABS ONLY -S : reformat to SPACES ONLY ( -i option is important) -M : reformat to MIXED SPACES / TABS ( -t and -i options important) -t : tab is worth characters -i : indents should be characters -h : print this text path is file or directory """ if __name__ == '__main__': import sys, getopt, os opts, args = getopt.getopt(sys.argv[1:], "TSMht:i:") d = {} print `opts` for opt in opts: if opt[0] == '-T': d['style'] = TABSONLY elif opt[0] == '-S': d['style'] = SPACESONLY elif opt[0] == '-M': d['style'] = MIXED elif opt[0] == '-t': d['tabs'] = opt[1] elif opt[0] == '-i': d['indent'] = opt[1] elif opt[0] == '-h': print usage % sys.argv[0] sys.exit(0) if not args: print usage % sys.argv[0] for arg in args: cleanfile(arg, d)