3 """The Tab Nanny despises ambiguous indentation. She knows no mercy."""
5 # Released to the public domain, by Tim Peters, 15 April 1998.
7 # XXX Note: this is now a standard library module.
8 # XXX The API needs to undergo changes however; the current code is too
9 # XXX script-like. This will be addressed later.
25 sys
.stderr
.write(sep
+ str(arg
))
27 sys
.stderr
.write("\n")
30 global verbose
, filename_only
32 opts
, args
= getopt
.getopt(sys
.argv
[1:], "qv")
33 except getopt
.error
, msg
:
38 filename_only
= filename_only
+ 1
42 errprint("Usage:", sys
.argv
[0], "[-v] file_or_directory ...")
48 def __init__(self
, lineno
, msg
, line
):
49 self
.lineno
, self
.msg
, self
.line
= lineno
, msg
, line
58 if os
.path
.isdir(file) and not os
.path
.islink(file):
60 print "%s: listing directory" % `
file`
61 names
= os
.listdir(file)
63 fullname
= os
.path
.join(file, name
)
64 if (os
.path
.isdir(fullname
) and
65 not os
.path
.islink(fullname
) or
66 os
.path
.normcase(name
[-3:]) == ".py"):
73 errprint("%s: I/O Error: %s" % (`
file`
, str(msg
)))
77 print "checking", `
file`
, "..."
81 tokenize
.tokenize(f
.readline
, tokeneater
)
83 except tokenize
.TokenError
, msg
:
84 errprint("%s: Token Error: %s" % (`
file`
, str(msg
)))
88 badline
= nag
.get_lineno()
91 print "%s: *** Line %d: trouble in tab city! ***" % (
93 print "offending line:", `line`
96 if ' ' in file: file = '"' + file + '"'
97 if filename_only
: print file
98 else: print file, badline
, `line`
102 print "%s: Clean bill of health." % `
file`
105 # the characters used for space and tab
110 # the original string
112 # the number of leading whitespace characters in raw
114 # the number of tabs in raw[:n]
116 # the normal form as a pair (count, trailing), where:
118 # a tuple such that raw[:n] contains count[i]
119 # instances of S * i + T
121 # the number of trailing spaces in raw[:n]
122 # It's A Theorem that m.indent_level(t) ==
123 # n.indent_level(t) for all t >= 1 iff m.norm == n.norm.
125 # true iff raw[:n] is of the form (T*)(S*)
127 def __init__(self
, ws
):
129 S
, T
= Whitespace
.S
, Whitespace
.T
140 count
= count
+ [0] * (b
- len(count
) + 1)
141 count
[b
] = count
[b
] + 1
147 self
.norm
= tuple(count
), b
148 self
.is_simple
= len(count
) <= 1
150 # return length of longest contiguous run of spaces (whether or not
152 def longest_run_of_spaces(self
):
153 count
, trailing
= self
.norm
154 return max(len(count
)-1, trailing
)
156 def indent_level(self
, tabsize
):
157 # count, il = self.norm
158 # for i in range(len(count)):
160 # il = il + (i/tabsize + 1)*tabsize * count[i]
164 # il = trailing + sum (i/ts + 1)*ts*count[i] =
165 # trailing + ts * sum (i/ts + 1)*count[i] =
166 # trailing + ts * sum i/ts*count[i] + count[i] =
167 # trailing + ts * [(sum i/ts*count[i]) + (sum count[i])] =
168 # trailing + ts * [(sum i/ts*count[i]) + num_tabs]
169 # and note that i/ts*count[i] is 0 when i < ts
171 count
, trailing
= self
.norm
173 for i
in range(tabsize
, len(count
)):
174 il
= il
+ i
/tabsize
* count
[i
]
175 return trailing
+ tabsize
* (il
+ self
.nt
)
177 # return true iff self.indent_level(t) == other.indent_level(t)
179 def equal(self
, other
):
180 return self
.norm
== other
.norm
182 # return a list of tuples (ts, i1, i2) such that
183 # i1 == self.indent_level(ts) != other.indent_level(ts) == i2.
184 # Intended to be used after not self.equal(other) is known, in which
185 # case it will return at least one witnessing tab size.
186 def not_equal_witness(self
, other
):
187 n
= max(self
.longest_run_of_spaces(),
188 other
.longest_run_of_spaces()) + 1
190 for ts
in range(1, n
+1):
191 if self
.indent_level(ts
) != other
.indent_level(ts
):
193 self
.indent_level(ts
),
194 other
.indent_level(ts
)) )
197 # Return true iff self.indent_level(t) < other.indent_level(t)
199 # The algorithm is due to Vincent Broman.
200 # Easy to prove it's correct.
202 # Trivial to prove n is sharp (consider T vs ST).
203 # Unknown whether there's a faster general way. I suspected so at
204 # first, but no longer.
205 # For the special (but common!) case where M and N are both of the
206 # form (T*)(S*), M.less(N) iff M.len() < N.len() and
207 # M.num_tabs() <= N.num_tabs(). Proof is easy but kinda long-winded.
209 # Note that M is of the form (T*)(S*) iff len(M.norm[0]) <= 1.
210 def less(self
, other
):
211 if self
.n
>= other
.n
:
213 if self
.is_simple
and other
.is_simple
:
214 return self
.nt
<= other
.nt
215 n
= max(self
.longest_run_of_spaces(),
216 other
.longest_run_of_spaces()) + 1
217 # the self.n >= other.n test already did it for ts=1
218 for ts
in range(2, n
+1):
219 if self
.indent_level(ts
) >= other
.indent_level(ts
):
223 # return a list of tuples (ts, i1, i2) such that
224 # i1 == self.indent_level(ts) >= other.indent_level(ts) == i2.
225 # Intended to be used after not self.less(other) is known, in which
226 # case it will return at least one witnessing tab size.
227 def not_less_witness(self
, other
):
228 n
= max(self
.longest_run_of_spaces(),
229 other
.longest_run_of_spaces()) + 1
231 for ts
in range(1, n
+1):
232 if self
.indent_level(ts
) >= other
.indent_level(ts
):
234 self
.indent_level(ts
),
235 other
.indent_level(ts
)) )
238 def format_witnesses(w
):
240 firsts
= map(lambda tup
: str(tup
[0]), w
)
241 prefix
= "at tab size"
243 prefix
= prefix
+ "s"
244 return prefix
+ " " + string
.join(firsts
, ', ')
246 # The collection of globals, the reset_globals() function, and the
247 # tokeneater() function, depend on which version of tokenize is
250 if hasattr(tokenize
, 'NL'):
251 # take advantage of Guido's patch!
257 global indents
, check_equal
259 indents
= [Whitespace("")]
261 def tokeneater(type, token
, start
, end
, line
,
262 INDENT
=tokenize
.INDENT
,
263 DEDENT
=tokenize
.DEDENT
,
264 NEWLINE
=tokenize
.NEWLINE
,
265 JUNK
=(tokenize
.COMMENT
, tokenize
.NL
) ):
266 global indents
, check_equal
269 # a program statement, or ENDMARKER, will eventually follow,
270 # after some (possibly empty) run of tokens of the form
271 # (NL | COMMENT)* (INDENT | DEDENT+)?
272 # If an INDENT appears, setting check_equal is wrong, and will
273 # be undone when we see the INDENT.
278 thisguy
= Whitespace(token
)
279 if not indents
[-1].less(thisguy
):
280 witness
= indents
[-1].not_less_witness(thisguy
)
281 msg
= "indent not greater e.g. " + format_witnesses(witness
)
282 raise NannyNag(start
[0], msg
, line
)
283 indents
.append(thisguy
)
286 # there's nothing we need to check here! what's important is
287 # that when the run of DEDENTs ends, the indentation of the
288 # program statement (or ENDMARKER) that triggered the run is
289 # equal to what's left at the top of the indents stack
291 # Ouch! This assert triggers if the last line of the source
292 # is indented *and* lacks a newline -- then DEDENTs pop out
294 # assert check_equal # else no earlier NEWLINE, or an earlier INDENT
299 elif check_equal
and type not in JUNK
:
300 # this is the first "real token" following a NEWLINE, so it
301 # must be the first token of the next program statement, or an
302 # ENDMARKER; the "line" argument exposes the leading whitespace
303 # for this statement; in the case of ENDMARKER, line is an empty
304 # string, so will properly match the empty string with which the
305 # "indents" stack was seeded
307 thisguy
= Whitespace(line
)
308 if not indents
[-1].equal(thisguy
):
309 witness
= indents
[-1].not_equal_witness(thisguy
)
310 msg
= "indent not equal e.g. " + format_witnesses(witness
)
311 raise NannyNag(start
[0], msg
, line
)
314 # unpatched version of tokenize
321 global nesting_level
, indents
, check_equal
322 nesting_level
= check_equal
= 0
323 indents
= [Whitespace("")]
325 def tokeneater(type, token
, start
, end
, line
,
326 INDENT
=tokenize
.INDENT
,
327 DEDENT
=tokenize
.DEDENT
,
328 NEWLINE
=tokenize
.NEWLINE
,
329 COMMENT
=tokenize
.COMMENT
,
331 global nesting_level
, indents
, check_equal
335 thisguy
= Whitespace(token
)
336 if not indents
[-1].less(thisguy
):
337 witness
= indents
[-1].not_less_witness(thisguy
)
338 msg
= "indent not greater e.g. " + format_witnesses(witness
)
339 raise NannyNag(start
[0], msg
, line
)
340 indents
.append(thisguy
)
345 elif type == NEWLINE
:
346 if nesting_level
== 0:
349 elif type == COMMENT
:
354 thisguy
= Whitespace(line
)
355 if not indents
[-1].equal(thisguy
):
356 witness
= indents
[-1].not_equal_witness(thisguy
)
357 msg
= "indent not equal e.g. " + format_witnesses(witness
)
358 raise NannyNag(start
[0], msg
, line
)
360 if type == OP
and token
in ('{', '[', '('):
361 nesting_level
= nesting_level
+ 1
363 elif type == OP
and token
in ('}', ']', ')'):
364 if nesting_level
== 0:
365 raise NannyNag(start
[0],
366 "unbalanced bracket '" + token
+ "'",
368 nesting_level
= nesting_level
- 1
370 if __name__
== '__main__':