1*cda5da8dSAndroid Build Coastguard Worker#! /usr/bin/env python3 2*cda5da8dSAndroid Build Coastguard Worker 3*cda5da8dSAndroid Build Coastguard Worker"""The Tab Nanny despises ambiguous indentation. She knows no mercy. 4*cda5da8dSAndroid Build Coastguard Worker 5*cda5da8dSAndroid Build Coastguard Workertabnanny -- Detection of ambiguous indentation 6*cda5da8dSAndroid Build Coastguard Worker 7*cda5da8dSAndroid Build Coastguard WorkerFor the time being this module is intended to be called as a script. 8*cda5da8dSAndroid Build Coastguard WorkerHowever it is possible to import it into an IDE and use the function 9*cda5da8dSAndroid Build Coastguard Workercheck() described below. 10*cda5da8dSAndroid Build Coastguard Worker 11*cda5da8dSAndroid Build Coastguard WorkerWarning: The API provided by this module is likely to change in future 12*cda5da8dSAndroid Build Coastguard Workerreleases; such changes may not be backward compatible. 13*cda5da8dSAndroid Build Coastguard Worker""" 14*cda5da8dSAndroid Build Coastguard Worker 15*cda5da8dSAndroid Build Coastguard Worker# Released to the public domain, by Tim Peters, 15 April 1998. 16*cda5da8dSAndroid Build Coastguard Worker 17*cda5da8dSAndroid Build Coastguard Worker# XXX Note: this is now a standard library module. 18*cda5da8dSAndroid Build Coastguard Worker# XXX The API needs to undergo changes however; the current code is too 19*cda5da8dSAndroid Build Coastguard Worker# XXX script-like. This will be addressed later. 20*cda5da8dSAndroid Build Coastguard Worker 21*cda5da8dSAndroid Build Coastguard Worker__version__ = "6" 22*cda5da8dSAndroid Build Coastguard Worker 23*cda5da8dSAndroid Build Coastguard Workerimport os 24*cda5da8dSAndroid Build Coastguard Workerimport sys 25*cda5da8dSAndroid Build Coastguard Workerimport tokenize 26*cda5da8dSAndroid Build Coastguard Worker 27*cda5da8dSAndroid Build Coastguard Worker__all__ = ["check", "NannyNag", "process_tokens"] 28*cda5da8dSAndroid Build Coastguard Worker 29*cda5da8dSAndroid Build Coastguard Workerverbose = 0 30*cda5da8dSAndroid Build Coastguard Workerfilename_only = 0 31*cda5da8dSAndroid Build Coastguard Worker 32*cda5da8dSAndroid Build Coastguard Workerdef errprint(*args): 33*cda5da8dSAndroid Build Coastguard Worker sep = "" 34*cda5da8dSAndroid Build Coastguard Worker for arg in args: 35*cda5da8dSAndroid Build Coastguard Worker sys.stderr.write(sep + str(arg)) 36*cda5da8dSAndroid Build Coastguard Worker sep = " " 37*cda5da8dSAndroid Build Coastguard Worker sys.stderr.write("\n") 38*cda5da8dSAndroid Build Coastguard Worker 39*cda5da8dSAndroid Build Coastguard Workerdef main(): 40*cda5da8dSAndroid Build Coastguard Worker import getopt 41*cda5da8dSAndroid Build Coastguard Worker 42*cda5da8dSAndroid Build Coastguard Worker global verbose, filename_only 43*cda5da8dSAndroid Build Coastguard Worker try: 44*cda5da8dSAndroid Build Coastguard Worker opts, args = getopt.getopt(sys.argv[1:], "qv") 45*cda5da8dSAndroid Build Coastguard Worker except getopt.error as msg: 46*cda5da8dSAndroid Build Coastguard Worker errprint(msg) 47*cda5da8dSAndroid Build Coastguard Worker return 48*cda5da8dSAndroid Build Coastguard Worker for o, a in opts: 49*cda5da8dSAndroid Build Coastguard Worker if o == '-q': 50*cda5da8dSAndroid Build Coastguard Worker filename_only = filename_only + 1 51*cda5da8dSAndroid Build Coastguard Worker if o == '-v': 52*cda5da8dSAndroid Build Coastguard Worker verbose = verbose + 1 53*cda5da8dSAndroid Build Coastguard Worker if not args: 54*cda5da8dSAndroid Build Coastguard Worker errprint("Usage:", sys.argv[0], "[-v] file_or_directory ...") 55*cda5da8dSAndroid Build Coastguard Worker return 56*cda5da8dSAndroid Build Coastguard Worker for arg in args: 57*cda5da8dSAndroid Build Coastguard Worker check(arg) 58*cda5da8dSAndroid Build Coastguard Worker 59*cda5da8dSAndroid Build Coastguard Workerclass NannyNag(Exception): 60*cda5da8dSAndroid Build Coastguard Worker """ 61*cda5da8dSAndroid Build Coastguard Worker Raised by process_tokens() if detecting an ambiguous indent. 62*cda5da8dSAndroid Build Coastguard Worker Captured and handled in check(). 63*cda5da8dSAndroid Build Coastguard Worker """ 64*cda5da8dSAndroid Build Coastguard Worker def __init__(self, lineno, msg, line): 65*cda5da8dSAndroid Build Coastguard Worker self.lineno, self.msg, self.line = lineno, msg, line 66*cda5da8dSAndroid Build Coastguard Worker def get_lineno(self): 67*cda5da8dSAndroid Build Coastguard Worker return self.lineno 68*cda5da8dSAndroid Build Coastguard Worker def get_msg(self): 69*cda5da8dSAndroid Build Coastguard Worker return self.msg 70*cda5da8dSAndroid Build Coastguard Worker def get_line(self): 71*cda5da8dSAndroid Build Coastguard Worker return self.line 72*cda5da8dSAndroid Build Coastguard Worker 73*cda5da8dSAndroid Build Coastguard Workerdef check(file): 74*cda5da8dSAndroid Build Coastguard Worker """check(file_or_dir) 75*cda5da8dSAndroid Build Coastguard Worker 76*cda5da8dSAndroid Build Coastguard Worker If file_or_dir is a directory and not a symbolic link, then recursively 77*cda5da8dSAndroid Build Coastguard Worker descend the directory tree named by file_or_dir, checking all .py files 78*cda5da8dSAndroid Build Coastguard Worker along the way. If file_or_dir is an ordinary Python source file, it is 79*cda5da8dSAndroid Build Coastguard Worker checked for whitespace related problems. The diagnostic messages are 80*cda5da8dSAndroid Build Coastguard Worker written to standard output using the print statement. 81*cda5da8dSAndroid Build Coastguard Worker """ 82*cda5da8dSAndroid Build Coastguard Worker 83*cda5da8dSAndroid Build Coastguard Worker if os.path.isdir(file) and not os.path.islink(file): 84*cda5da8dSAndroid Build Coastguard Worker if verbose: 85*cda5da8dSAndroid Build Coastguard Worker print("%r: listing directory" % (file,)) 86*cda5da8dSAndroid Build Coastguard Worker names = os.listdir(file) 87*cda5da8dSAndroid Build Coastguard Worker for name in names: 88*cda5da8dSAndroid Build Coastguard Worker fullname = os.path.join(file, name) 89*cda5da8dSAndroid Build Coastguard Worker if (os.path.isdir(fullname) and 90*cda5da8dSAndroid Build Coastguard Worker not os.path.islink(fullname) or 91*cda5da8dSAndroid Build Coastguard Worker os.path.normcase(name[-3:]) == ".py"): 92*cda5da8dSAndroid Build Coastguard Worker check(fullname) 93*cda5da8dSAndroid Build Coastguard Worker return 94*cda5da8dSAndroid Build Coastguard Worker 95*cda5da8dSAndroid Build Coastguard Worker try: 96*cda5da8dSAndroid Build Coastguard Worker f = tokenize.open(file) 97*cda5da8dSAndroid Build Coastguard Worker except OSError as msg: 98*cda5da8dSAndroid Build Coastguard Worker errprint("%r: I/O Error: %s" % (file, msg)) 99*cda5da8dSAndroid Build Coastguard Worker return 100*cda5da8dSAndroid Build Coastguard Worker 101*cda5da8dSAndroid Build Coastguard Worker if verbose > 1: 102*cda5da8dSAndroid Build Coastguard Worker print("checking %r ..." % file) 103*cda5da8dSAndroid Build Coastguard Worker 104*cda5da8dSAndroid Build Coastguard Worker try: 105*cda5da8dSAndroid Build Coastguard Worker process_tokens(tokenize.generate_tokens(f.readline)) 106*cda5da8dSAndroid Build Coastguard Worker 107*cda5da8dSAndroid Build Coastguard Worker except tokenize.TokenError as msg: 108*cda5da8dSAndroid Build Coastguard Worker errprint("%r: Token Error: %s" % (file, msg)) 109*cda5da8dSAndroid Build Coastguard Worker return 110*cda5da8dSAndroid Build Coastguard Worker 111*cda5da8dSAndroid Build Coastguard Worker except IndentationError as msg: 112*cda5da8dSAndroid Build Coastguard Worker errprint("%r: Indentation Error: %s" % (file, msg)) 113*cda5da8dSAndroid Build Coastguard Worker return 114*cda5da8dSAndroid Build Coastguard Worker 115*cda5da8dSAndroid Build Coastguard Worker except NannyNag as nag: 116*cda5da8dSAndroid Build Coastguard Worker badline = nag.get_lineno() 117*cda5da8dSAndroid Build Coastguard Worker line = nag.get_line() 118*cda5da8dSAndroid Build Coastguard Worker if verbose: 119*cda5da8dSAndroid Build Coastguard Worker print("%r: *** Line %d: trouble in tab city! ***" % (file, badline)) 120*cda5da8dSAndroid Build Coastguard Worker print("offending line: %r" % (line,)) 121*cda5da8dSAndroid Build Coastguard Worker print(nag.get_msg()) 122*cda5da8dSAndroid Build Coastguard Worker else: 123*cda5da8dSAndroid Build Coastguard Worker if ' ' in file: file = '"' + file + '"' 124*cda5da8dSAndroid Build Coastguard Worker if filename_only: print(file) 125*cda5da8dSAndroid Build Coastguard Worker else: print(file, badline, repr(line)) 126*cda5da8dSAndroid Build Coastguard Worker return 127*cda5da8dSAndroid Build Coastguard Worker 128*cda5da8dSAndroid Build Coastguard Worker finally: 129*cda5da8dSAndroid Build Coastguard Worker f.close() 130*cda5da8dSAndroid Build Coastguard Worker 131*cda5da8dSAndroid Build Coastguard Worker if verbose: 132*cda5da8dSAndroid Build Coastguard Worker print("%r: Clean bill of health." % (file,)) 133*cda5da8dSAndroid Build Coastguard Worker 134*cda5da8dSAndroid Build Coastguard Workerclass Whitespace: 135*cda5da8dSAndroid Build Coastguard Worker # the characters used for space and tab 136*cda5da8dSAndroid Build Coastguard Worker S, T = ' \t' 137*cda5da8dSAndroid Build Coastguard Worker 138*cda5da8dSAndroid Build Coastguard Worker # members: 139*cda5da8dSAndroid Build Coastguard Worker # raw 140*cda5da8dSAndroid Build Coastguard Worker # the original string 141*cda5da8dSAndroid Build Coastguard Worker # n 142*cda5da8dSAndroid Build Coastguard Worker # the number of leading whitespace characters in raw 143*cda5da8dSAndroid Build Coastguard Worker # nt 144*cda5da8dSAndroid Build Coastguard Worker # the number of tabs in raw[:n] 145*cda5da8dSAndroid Build Coastguard Worker # norm 146*cda5da8dSAndroid Build Coastguard Worker # the normal form as a pair (count, trailing), where: 147*cda5da8dSAndroid Build Coastguard Worker # count 148*cda5da8dSAndroid Build Coastguard Worker # a tuple such that raw[:n] contains count[i] 149*cda5da8dSAndroid Build Coastguard Worker # instances of S * i + T 150*cda5da8dSAndroid Build Coastguard Worker # trailing 151*cda5da8dSAndroid Build Coastguard Worker # the number of trailing spaces in raw[:n] 152*cda5da8dSAndroid Build Coastguard Worker # It's A Theorem that m.indent_level(t) == 153*cda5da8dSAndroid Build Coastguard Worker # n.indent_level(t) for all t >= 1 iff m.norm == n.norm. 154*cda5da8dSAndroid Build Coastguard Worker # is_simple 155*cda5da8dSAndroid Build Coastguard Worker # true iff raw[:n] is of the form (T*)(S*) 156*cda5da8dSAndroid Build Coastguard Worker 157*cda5da8dSAndroid Build Coastguard Worker def __init__(self, ws): 158*cda5da8dSAndroid Build Coastguard Worker self.raw = ws 159*cda5da8dSAndroid Build Coastguard Worker S, T = Whitespace.S, Whitespace.T 160*cda5da8dSAndroid Build Coastguard Worker count = [] 161*cda5da8dSAndroid Build Coastguard Worker b = n = nt = 0 162*cda5da8dSAndroid Build Coastguard Worker for ch in self.raw: 163*cda5da8dSAndroid Build Coastguard Worker if ch == S: 164*cda5da8dSAndroid Build Coastguard Worker n = n + 1 165*cda5da8dSAndroid Build Coastguard Worker b = b + 1 166*cda5da8dSAndroid Build Coastguard Worker elif ch == T: 167*cda5da8dSAndroid Build Coastguard Worker n = n + 1 168*cda5da8dSAndroid Build Coastguard Worker nt = nt + 1 169*cda5da8dSAndroid Build Coastguard Worker if b >= len(count): 170*cda5da8dSAndroid Build Coastguard Worker count = count + [0] * (b - len(count) + 1) 171*cda5da8dSAndroid Build Coastguard Worker count[b] = count[b] + 1 172*cda5da8dSAndroid Build Coastguard Worker b = 0 173*cda5da8dSAndroid Build Coastguard Worker else: 174*cda5da8dSAndroid Build Coastguard Worker break 175*cda5da8dSAndroid Build Coastguard Worker self.n = n 176*cda5da8dSAndroid Build Coastguard Worker self.nt = nt 177*cda5da8dSAndroid Build Coastguard Worker self.norm = tuple(count), b 178*cda5da8dSAndroid Build Coastguard Worker self.is_simple = len(count) <= 1 179*cda5da8dSAndroid Build Coastguard Worker 180*cda5da8dSAndroid Build Coastguard Worker # return length of longest contiguous run of spaces (whether or not 181*cda5da8dSAndroid Build Coastguard Worker # preceding a tab) 182*cda5da8dSAndroid Build Coastguard Worker def longest_run_of_spaces(self): 183*cda5da8dSAndroid Build Coastguard Worker count, trailing = self.norm 184*cda5da8dSAndroid Build Coastguard Worker return max(len(count)-1, trailing) 185*cda5da8dSAndroid Build Coastguard Worker 186*cda5da8dSAndroid Build Coastguard Worker def indent_level(self, tabsize): 187*cda5da8dSAndroid Build Coastguard Worker # count, il = self.norm 188*cda5da8dSAndroid Build Coastguard Worker # for i in range(len(count)): 189*cda5da8dSAndroid Build Coastguard Worker # if count[i]: 190*cda5da8dSAndroid Build Coastguard Worker # il = il + (i//tabsize + 1)*tabsize * count[i] 191*cda5da8dSAndroid Build Coastguard Worker # return il 192*cda5da8dSAndroid Build Coastguard Worker 193*cda5da8dSAndroid Build Coastguard Worker # quicker: 194*cda5da8dSAndroid Build Coastguard Worker # il = trailing + sum (i//ts + 1)*ts*count[i] = 195*cda5da8dSAndroid Build Coastguard Worker # trailing + ts * sum (i//ts + 1)*count[i] = 196*cda5da8dSAndroid Build Coastguard Worker # trailing + ts * sum i//ts*count[i] + count[i] = 197*cda5da8dSAndroid Build Coastguard Worker # trailing + ts * [(sum i//ts*count[i]) + (sum count[i])] = 198*cda5da8dSAndroid Build Coastguard Worker # trailing + ts * [(sum i//ts*count[i]) + num_tabs] 199*cda5da8dSAndroid Build Coastguard Worker # and note that i//ts*count[i] is 0 when i < ts 200*cda5da8dSAndroid Build Coastguard Worker 201*cda5da8dSAndroid Build Coastguard Worker count, trailing = self.norm 202*cda5da8dSAndroid Build Coastguard Worker il = 0 203*cda5da8dSAndroid Build Coastguard Worker for i in range(tabsize, len(count)): 204*cda5da8dSAndroid Build Coastguard Worker il = il + i//tabsize * count[i] 205*cda5da8dSAndroid Build Coastguard Worker return trailing + tabsize * (il + self.nt) 206*cda5da8dSAndroid Build Coastguard Worker 207*cda5da8dSAndroid Build Coastguard Worker # return true iff self.indent_level(t) == other.indent_level(t) 208*cda5da8dSAndroid Build Coastguard Worker # for all t >= 1 209*cda5da8dSAndroid Build Coastguard Worker def equal(self, other): 210*cda5da8dSAndroid Build Coastguard Worker return self.norm == other.norm 211*cda5da8dSAndroid Build Coastguard Worker 212*cda5da8dSAndroid Build Coastguard Worker # return a list of tuples (ts, i1, i2) such that 213*cda5da8dSAndroid Build Coastguard Worker # i1 == self.indent_level(ts) != other.indent_level(ts) == i2. 214*cda5da8dSAndroid Build Coastguard Worker # Intended to be used after not self.equal(other) is known, in which 215*cda5da8dSAndroid Build Coastguard Worker # case it will return at least one witnessing tab size. 216*cda5da8dSAndroid Build Coastguard Worker def not_equal_witness(self, other): 217*cda5da8dSAndroid Build Coastguard Worker n = max(self.longest_run_of_spaces(), 218*cda5da8dSAndroid Build Coastguard Worker other.longest_run_of_spaces()) + 1 219*cda5da8dSAndroid Build Coastguard Worker a = [] 220*cda5da8dSAndroid Build Coastguard Worker for ts in range(1, n+1): 221*cda5da8dSAndroid Build Coastguard Worker if self.indent_level(ts) != other.indent_level(ts): 222*cda5da8dSAndroid Build Coastguard Worker a.append( (ts, 223*cda5da8dSAndroid Build Coastguard Worker self.indent_level(ts), 224*cda5da8dSAndroid Build Coastguard Worker other.indent_level(ts)) ) 225*cda5da8dSAndroid Build Coastguard Worker return a 226*cda5da8dSAndroid Build Coastguard Worker 227*cda5da8dSAndroid Build Coastguard Worker # Return True iff self.indent_level(t) < other.indent_level(t) 228*cda5da8dSAndroid Build Coastguard Worker # for all t >= 1. 229*cda5da8dSAndroid Build Coastguard Worker # The algorithm is due to Vincent Broman. 230*cda5da8dSAndroid Build Coastguard Worker # Easy to prove it's correct. 231*cda5da8dSAndroid Build Coastguard Worker # XXXpost that. 232*cda5da8dSAndroid Build Coastguard Worker # Trivial to prove n is sharp (consider T vs ST). 233*cda5da8dSAndroid Build Coastguard Worker # Unknown whether there's a faster general way. I suspected so at 234*cda5da8dSAndroid Build Coastguard Worker # first, but no longer. 235*cda5da8dSAndroid Build Coastguard Worker # For the special (but common!) case where M and N are both of the 236*cda5da8dSAndroid Build Coastguard Worker # form (T*)(S*), M.less(N) iff M.len() < N.len() and 237*cda5da8dSAndroid Build Coastguard Worker # M.num_tabs() <= N.num_tabs(). Proof is easy but kinda long-winded. 238*cda5da8dSAndroid Build Coastguard Worker # XXXwrite that up. 239*cda5da8dSAndroid Build Coastguard Worker # Note that M is of the form (T*)(S*) iff len(M.norm[0]) <= 1. 240*cda5da8dSAndroid Build Coastguard Worker def less(self, other): 241*cda5da8dSAndroid Build Coastguard Worker if self.n >= other.n: 242*cda5da8dSAndroid Build Coastguard Worker return False 243*cda5da8dSAndroid Build Coastguard Worker if self.is_simple and other.is_simple: 244*cda5da8dSAndroid Build Coastguard Worker return self.nt <= other.nt 245*cda5da8dSAndroid Build Coastguard Worker n = max(self.longest_run_of_spaces(), 246*cda5da8dSAndroid Build Coastguard Worker other.longest_run_of_spaces()) + 1 247*cda5da8dSAndroid Build Coastguard Worker # the self.n >= other.n test already did it for ts=1 248*cda5da8dSAndroid Build Coastguard Worker for ts in range(2, n+1): 249*cda5da8dSAndroid Build Coastguard Worker if self.indent_level(ts) >= other.indent_level(ts): 250*cda5da8dSAndroid Build Coastguard Worker return False 251*cda5da8dSAndroid Build Coastguard Worker return True 252*cda5da8dSAndroid Build Coastguard Worker 253*cda5da8dSAndroid Build Coastguard Worker # return a list of tuples (ts, i1, i2) such that 254*cda5da8dSAndroid Build Coastguard Worker # i1 == self.indent_level(ts) >= other.indent_level(ts) == i2. 255*cda5da8dSAndroid Build Coastguard Worker # Intended to be used after not self.less(other) is known, in which 256*cda5da8dSAndroid Build Coastguard Worker # case it will return at least one witnessing tab size. 257*cda5da8dSAndroid Build Coastguard Worker def not_less_witness(self, other): 258*cda5da8dSAndroid Build Coastguard Worker n = max(self.longest_run_of_spaces(), 259*cda5da8dSAndroid Build Coastguard Worker other.longest_run_of_spaces()) + 1 260*cda5da8dSAndroid Build Coastguard Worker a = [] 261*cda5da8dSAndroid Build Coastguard Worker for ts in range(1, n+1): 262*cda5da8dSAndroid Build Coastguard Worker if self.indent_level(ts) >= other.indent_level(ts): 263*cda5da8dSAndroid Build Coastguard Worker a.append( (ts, 264*cda5da8dSAndroid Build Coastguard Worker self.indent_level(ts), 265*cda5da8dSAndroid Build Coastguard Worker other.indent_level(ts)) ) 266*cda5da8dSAndroid Build Coastguard Worker return a 267*cda5da8dSAndroid Build Coastguard Worker 268*cda5da8dSAndroid Build Coastguard Workerdef format_witnesses(w): 269*cda5da8dSAndroid Build Coastguard Worker firsts = (str(tup[0]) for tup in w) 270*cda5da8dSAndroid Build Coastguard Worker prefix = "at tab size" 271*cda5da8dSAndroid Build Coastguard Worker if len(w) > 1: 272*cda5da8dSAndroid Build Coastguard Worker prefix = prefix + "s" 273*cda5da8dSAndroid Build Coastguard Worker return prefix + " " + ', '.join(firsts) 274*cda5da8dSAndroid Build Coastguard Worker 275*cda5da8dSAndroid Build Coastguard Workerdef process_tokens(tokens): 276*cda5da8dSAndroid Build Coastguard Worker INDENT = tokenize.INDENT 277*cda5da8dSAndroid Build Coastguard Worker DEDENT = tokenize.DEDENT 278*cda5da8dSAndroid Build Coastguard Worker NEWLINE = tokenize.NEWLINE 279*cda5da8dSAndroid Build Coastguard Worker JUNK = tokenize.COMMENT, tokenize.NL 280*cda5da8dSAndroid Build Coastguard Worker indents = [Whitespace("")] 281*cda5da8dSAndroid Build Coastguard Worker check_equal = 0 282*cda5da8dSAndroid Build Coastguard Worker 283*cda5da8dSAndroid Build Coastguard Worker for (type, token, start, end, line) in tokens: 284*cda5da8dSAndroid Build Coastguard Worker if type == NEWLINE: 285*cda5da8dSAndroid Build Coastguard Worker # a program statement, or ENDMARKER, will eventually follow, 286*cda5da8dSAndroid Build Coastguard Worker # after some (possibly empty) run of tokens of the form 287*cda5da8dSAndroid Build Coastguard Worker # (NL | COMMENT)* (INDENT | DEDENT+)? 288*cda5da8dSAndroid Build Coastguard Worker # If an INDENT appears, setting check_equal is wrong, and will 289*cda5da8dSAndroid Build Coastguard Worker # be undone when we see the INDENT. 290*cda5da8dSAndroid Build Coastguard Worker check_equal = 1 291*cda5da8dSAndroid Build Coastguard Worker 292*cda5da8dSAndroid Build Coastguard Worker elif type == INDENT: 293*cda5da8dSAndroid Build Coastguard Worker check_equal = 0 294*cda5da8dSAndroid Build Coastguard Worker thisguy = Whitespace(token) 295*cda5da8dSAndroid Build Coastguard Worker if not indents[-1].less(thisguy): 296*cda5da8dSAndroid Build Coastguard Worker witness = indents[-1].not_less_witness(thisguy) 297*cda5da8dSAndroid Build Coastguard Worker msg = "indent not greater e.g. " + format_witnesses(witness) 298*cda5da8dSAndroid Build Coastguard Worker raise NannyNag(start[0], msg, line) 299*cda5da8dSAndroid Build Coastguard Worker indents.append(thisguy) 300*cda5da8dSAndroid Build Coastguard Worker 301*cda5da8dSAndroid Build Coastguard Worker elif type == DEDENT: 302*cda5da8dSAndroid Build Coastguard Worker # there's nothing we need to check here! what's important is 303*cda5da8dSAndroid Build Coastguard Worker # that when the run of DEDENTs ends, the indentation of the 304*cda5da8dSAndroid Build Coastguard Worker # program statement (or ENDMARKER) that triggered the run is 305*cda5da8dSAndroid Build Coastguard Worker # equal to what's left at the top of the indents stack 306*cda5da8dSAndroid Build Coastguard Worker 307*cda5da8dSAndroid Build Coastguard Worker # Ouch! This assert triggers if the last line of the source 308*cda5da8dSAndroid Build Coastguard Worker # is indented *and* lacks a newline -- then DEDENTs pop out 309*cda5da8dSAndroid Build Coastguard Worker # of thin air. 310*cda5da8dSAndroid Build Coastguard Worker # assert check_equal # else no earlier NEWLINE, or an earlier INDENT 311*cda5da8dSAndroid Build Coastguard Worker check_equal = 1 312*cda5da8dSAndroid Build Coastguard Worker 313*cda5da8dSAndroid Build Coastguard Worker del indents[-1] 314*cda5da8dSAndroid Build Coastguard Worker 315*cda5da8dSAndroid Build Coastguard Worker elif check_equal and type not in JUNK: 316*cda5da8dSAndroid Build Coastguard Worker # this is the first "real token" following a NEWLINE, so it 317*cda5da8dSAndroid Build Coastguard Worker # must be the first token of the next program statement, or an 318*cda5da8dSAndroid Build Coastguard Worker # ENDMARKER; the "line" argument exposes the leading whitespace 319*cda5da8dSAndroid Build Coastguard Worker # for this statement; in the case of ENDMARKER, line is an empty 320*cda5da8dSAndroid Build Coastguard Worker # string, so will properly match the empty string with which the 321*cda5da8dSAndroid Build Coastguard Worker # "indents" stack was seeded 322*cda5da8dSAndroid Build Coastguard Worker check_equal = 0 323*cda5da8dSAndroid Build Coastguard Worker thisguy = Whitespace(line) 324*cda5da8dSAndroid Build Coastguard Worker if not indents[-1].equal(thisguy): 325*cda5da8dSAndroid Build Coastguard Worker witness = indents[-1].not_equal_witness(thisguy) 326*cda5da8dSAndroid Build Coastguard Worker msg = "indent not equal e.g. " + format_witnesses(witness) 327*cda5da8dSAndroid Build Coastguard Worker raise NannyNag(start[0], msg, line) 328*cda5da8dSAndroid Build Coastguard Worker 329*cda5da8dSAndroid Build Coastguard Worker 330*cda5da8dSAndroid Build Coastguard Workerif __name__ == '__main__': 331*cda5da8dSAndroid Build Coastguard Worker main() 332