Fix three PyChecker-detected gotchas.
[python/dscho.git] / Mac / Contrib / Tabcleaner / Tabcleaner.py
blobc6d32237dc5de2665511a03919bf4b7121676797
1 #!/usr/bin/python
3 import tokenize
4 import string
6 TABSONLY = 'TABSONLY'
7 SPACESONLY = 'SPACESONLY'
8 MIXED = 'MIXED'
10 class PyText:
11 def __init__(self, fnm, optdict):
12 self.optdict = optdict
13 self.fnm = fnm
14 self.txt = open(self.fnm, 'r').readlines()
15 self.indents = [(0, 0, )]
16 self.lnndx = 0
17 self.indentndx = 0
18 def getline(self):
19 if self.lnndx < len(self.txt):
20 txt = self.txt[self.lnndx]
21 self.lnndx = self.lnndx + 1
22 else:
23 txt = ''
24 return txt
25 def tokeneater(self, type, token, start, end, line):
26 if type == tokenize.INDENT:
27 (lvl, s) = self.indents[-1]
28 self.indents[-1] = (lvl, s, start[0]-1)
29 self.indents.append((lvl+1, start[0]-1,))
30 elif type == tokenize.DEDENT:
31 (lvl, s) = self.indents[-1]
32 self.indents[-1] = (lvl, s, start[0]-1)
33 self.indents.append((lvl-1, start[0]-1,))
34 elif type == tokenize.ENDMARKER:
35 (lvl, s) = self.indents[-1]
36 self.indents[-1] = (lvl, s, len(self.txt))
37 def split(self, ln):
38 content = string.lstrip(ln)
39 if not content:
40 return ('', '\n')
41 lead = ln[:len(ln) - len(content)]
42 lead = string.expandtabs(lead)
43 return (lead, content)
45 def process(self):
46 style = self.optdict.get('style', TABSONLY)
47 indent = string.atoi(self.optdict.get('indent', '4'))
48 tabsz = string.atoi(self.optdict.get('tabs', '8'))
49 print 'file %s -> style %s, tabsize %d, indent %d' % (self.fnm, style, tabsz, indent)
50 tokenize.tokenize(self.getline, self.tokeneater)
51 #import pprint
52 #pprint.pprint(self.indents)
53 new = []
54 for (lvl, s, e) in self.indents:
55 if s >= len(self.txt):
56 break
57 if s == e:
58 continue
59 oldlead, content = self.split(self.txt[s])
60 #print "oldlead", len(oldlead), `oldlead`
61 if style == TABSONLY:
62 newlead = '\t'*lvl
63 elif style == SPACESONLY:
64 newlead = ' '*(indent*lvl)
65 else:
66 sz = indent*lvl
67 t,spcs = divmod(sz, tabsz)
68 newlead = '\t'*t + ' '*spcs
69 new.append(newlead + content)
70 for ln in self.txt[s+1:e]:
71 lead, content = self.split(ln)
72 #print "lead:", len(lead)
73 new.append(newlead + lead[len(oldlead):] + content)
74 self.save(new)
75 #print "---", self.fnm
76 #for ln in new:
77 # print ln,
78 #print
80 def save(self, txt):
81 bakname = os.path.splitext(self.fnm)[0]+'.bak'
82 print "backing up", self.fnm, "to", bakname
83 #print os.getcwd()
84 try:
85 os.rename(self.fnm, bakname)
86 except os.error:
87 os.remove(bakname)
88 os.rename(self.fnm, bakname)
89 open(self.fnm, 'w').writelines(txt)
91 def test():
92 tc = PyText('test1.py')
93 tc.process()
94 tc = PyText('test1.py')
95 tc.process(style=TABSONLY)
96 tc = PyText('test1.py')
97 tc.process(style=MIXED, indent=4, tabs=8)
98 tc = PyText('test1.py')
99 tc.process(style=MIXED, indent=2, tabs=8)
101 def cleanfile(fnm, d):
102 if os.path.isdir(fnm) and not os.path.islink(fnm):
103 names = os.listdir(fnm)
104 for name in names:
105 fullnm = os.path.join(fnm, name)
106 if (os.path.isdir(fullnm) and not os.path.islink(fullnm)) or \
107 os.path.normcase(fullnm[-3:]) == ".py":
108 cleanfile(fullnm, d)
109 return
110 tc = PyText(fnm, d)
111 tc.process()
113 usage="""\
114 %s [options] [path...]
115 options
116 -T : reformat to TABS ONLY
117 -S : reformat to SPACES ONLY ( -i option is important)
118 -M : reformat to MIXED SPACES / TABS ( -t and -i options important)
119 -t<n> : tab is worth <n> characters
120 -i<n> : indents should be <n> characters
121 -h : print this text
122 path is file or directory
124 if __name__ == '__main__':
125 import sys, getopt, os
126 opts, args = getopt.getopt(sys.argv[1:], "TSMht:i:")
127 d = {}
128 print `opts`
129 for opt in opts:
130 if opt[0] == '-T':
131 d['style'] = TABSONLY
132 elif opt[0] == '-S':
133 d['style'] = SPACESONLY
134 elif opt[0] == '-M':
135 d['style'] = MIXED
136 elif opt[0] == '-t':
137 d['tabs'] = opt[1]
138 elif opt[0] == '-i':
139 d['indent'] = opt[1]
140 elif opt[0] == '-h':
141 print usage % sys.argv[0]
142 sys.exit(0)
143 if not args:
144 print usage % sys.argv[0]
145 for arg in args:
146 cleanfile(arg, d)