109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh#! /usr/bin/env python
209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh# Released to the public domain, by Tim Peters, 03 October 2000.
409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh"""reindent [-d][-r][-v] [ path ... ]
609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh-d (--dryrun)   Dry run.   Analyze, but don't make any changes to, files.
809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh-r (--recurse)  Recurse.   Search for all .py files in subdirectories too.
909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh-n (--nobackup) No backup. Does not make a ".bak" file before reindenting.
1009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh-v (--verbose)  Verbose.   Print informative msgs; else no output.
1109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh-h (--help)     Help.      Print this usage information and exit.
1209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
1309a025e087fa754b633ebc499d5c96d4e0f844b0mblighChange Python (.py) files to use 4-space indents and no hard tab characters.
1409a025e087fa754b633ebc499d5c96d4e0f844b0mblighAlso trim excess spaces and tabs from ends of lines, and remove empty lines
1509a025e087fa754b633ebc499d5c96d4e0f844b0mblighat the end of files.  Also ensure the last line ends with a newline.
1609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
1709a025e087fa754b633ebc499d5c96d4e0f844b0mblighIf no paths are given on the command line, reindent operates as a filter,
1809a025e087fa754b633ebc499d5c96d4e0f844b0mblighreading a single source file from standard input and writing the transformed
1909a025e087fa754b633ebc499d5c96d4e0f844b0mblighsource to standard output.  In this case, the -d, -r and -v flags are
2009a025e087fa754b633ebc499d5c96d4e0f844b0mblighignored.
2109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
2209a025e087fa754b633ebc499d5c96d4e0f844b0mblighYou can pass one or more file and/or directory paths.  When a directory
2309a025e087fa754b633ebc499d5c96d4e0f844b0mblighpath, all .py files within the directory will be examined, and, if the -r
2409a025e087fa754b633ebc499d5c96d4e0f844b0mblighoption is given, likewise recursively for subdirectories.
2509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
2609a025e087fa754b633ebc499d5c96d4e0f844b0mblighIf output is not to standard output, reindent overwrites files in place,
2709a025e087fa754b633ebc499d5c96d4e0f844b0mblighrenaming the originals with a .bak extension.  If it finds nothing to
2809a025e087fa754b633ebc499d5c96d4e0f844b0mblighchange, the file is left alone.  If reindent does change a file, the changed
2909a025e087fa754b633ebc499d5c96d4e0f844b0mblighfile is a fixed-point for future runs (i.e., running reindent on the
3009a025e087fa754b633ebc499d5c96d4e0f844b0mblighresulting .py file won't change it again).
3109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
3209a025e087fa754b633ebc499d5c96d4e0f844b0mblighThe hard part of reindenting is figuring out what to do with comment
3309a025e087fa754b633ebc499d5c96d4e0f844b0mblighlines.  So long as the input files get a clean bill of health from
3409a025e087fa754b633ebc499d5c96d4e0f844b0mblightabnanny.py, reindent should do a good job.
3509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
3609a025e087fa754b633ebc499d5c96d4e0f844b0mblighThe backup file is a copy of the one that is being reindented. The ".bak"
3709a025e087fa754b633ebc499d5c96d4e0f844b0mblighfile is generated with shutil.copy(), but some corner cases regarding
3809a025e087fa754b633ebc499d5c96d4e0f844b0mblighuser/group and permissions could leave the backup file more readable that
3909a025e087fa754b633ebc499d5c96d4e0f844b0mblighyou'd prefer. You can always use the --nobackup option to prevent this.
4009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh"""
4109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
4209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh__version__ = "1"
4309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
4409a025e087fa754b633ebc499d5c96d4e0f844b0mblighimport tokenize
4509a025e087fa754b633ebc499d5c96d4e0f844b0mblighimport os, shutil
4609a025e087fa754b633ebc499d5c96d4e0f844b0mblighimport sys
4709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
4809a025e087fa754b633ebc499d5c96d4e0f844b0mblighverbose    = 0
4909a025e087fa754b633ebc499d5c96d4e0f844b0mblighrecurse    = 0
5009a025e087fa754b633ebc499d5c96d4e0f844b0mblighdryrun     = 0
5109a025e087fa754b633ebc499d5c96d4e0f844b0mblighmakebackup = True
5209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
5309a025e087fa754b633ebc499d5c96d4e0f844b0mblighdef usage(msg=None):
5409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    if msg is not None:
5509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        print >> sys.stderr, msg
5609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    print >> sys.stderr, __doc__
5709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
5809a025e087fa754b633ebc499d5c96d4e0f844b0mblighdef errprint(*args):
5909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    sep = ""
6009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    for arg in args:
6109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        sys.stderr.write(sep + str(arg))
6209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        sep = " "
6309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    sys.stderr.write("\n")
6409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
6509a025e087fa754b633ebc499d5c96d4e0f844b0mblighdef main():
6609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    import getopt
6709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    global verbose, recurse, dryrun, makebackup
6809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    try:
6909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        opts, args = getopt.getopt(sys.argv[1:], "drnvh",
7009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                        ["dryrun", "recurse", "nobackup", "verbose", "help"])
7109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    except getopt.error, msg:
7209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        usage(msg)
7309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        return
7409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    for o, a in opts:
7509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        if o in ('-d', '--dryrun'):
7609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            dryrun += 1
7709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        elif o in ('-r', '--recurse'):
7809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            recurse += 1
7909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        elif o in ('-n', '--nobackup'):
8009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            makebackup = False
8109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        elif o in ('-v', '--verbose'):
8209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            verbose += 1
8309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        elif o in ('-h', '--help'):
8409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            usage()
8509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            return
8609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    if not args:
8709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        r = Reindenter(sys.stdin)
8809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        r.run()
8909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        r.write(sys.stdout)
9009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        return
9109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    for arg in args:
9209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        check(arg)
9309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
9409a025e087fa754b633ebc499d5c96d4e0f844b0mblighdef check(file):
9509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    if os.path.isdir(file) and not os.path.islink(file):
9609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        if verbose:
9709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            print "listing directory", file
9809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        names = os.listdir(file)
9909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        for name in names:
10009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            fullname = os.path.join(file, name)
10109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            if ((recurse and os.path.isdir(fullname) and
10209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                 not os.path.islink(fullname))
10309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                or name.lower().endswith(".py")):
10409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                check(fullname)
10509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        return
10609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
10709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    if verbose:
10809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        print "checking", file, "...",
10909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    try:
11009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        f = open(file)
11109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    except IOError, msg:
11209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        errprint("%s: I/O Error: %s" % (file, str(msg)))
11309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        return
11409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
11509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    r = Reindenter(f)
11609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    f.close()
11709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    if r.run():
11809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        if verbose:
11909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            print "changed."
12009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            if dryrun:
12109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                print "But this is a dry run, so leaving it alone."
12209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        if not dryrun:
12309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            bak = file + ".bak"
12409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            if makebackup:
12509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                shutil.copyfile(file, bak)
12609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                if verbose:
12709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                    print "backed up", file, "to", bak
12809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            f = open(file, "w")
12909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            r.write(f)
13009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            f.close()
13109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            if verbose:
13209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                print "wrote new", file
13309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        return True
13409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    else:
13509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        if verbose:
13609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            print "unchanged."
13709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        return False
13809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
13909a025e087fa754b633ebc499d5c96d4e0f844b0mblighdef _rstrip(line, JUNK='\n \t'):
14009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    """Return line stripped of trailing spaces, tabs, newlines.
14109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
14209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    Note that line.rstrip() instead also strips sundry control characters,
14309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    but at least one known Emacs user expects to keep junk like that, not
14409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    mentioning Barry by name or anything <wink>.
14509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    """
14609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
14709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    i = len(line)
14809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    while i > 0 and line[i-1] in JUNK:
14909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        i -= 1
15009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    return line[:i]
15109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
15209a025e087fa754b633ebc499d5c96d4e0f844b0mblighclass Reindenter:
15309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
15409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    def __init__(self, f):
15509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        self.find_stmt = 1  # next token begins a fresh stmt?
15609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        self.level = 0      # current indent level
15709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
15809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        # Raw file lines.
15909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        self.raw = f.readlines()
16009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
16109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        # File lines, rstripped & tab-expanded.  Dummy at start is so
16209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        # that we can use tokenize's 1-based line numbering easily.
16309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        # Note that a line is all-blank iff it's "\n".
16409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        self.lines = [_rstrip(line).expandtabs() + "\n"
16509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                      for line in self.raw]
16609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        self.lines.insert(0, None)
16709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        self.index = 1  # index into self.lines of next line
16809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
16909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        # List of (lineno, indentlevel) pairs, one for each stmt and
17009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        # comment line.  indentlevel is -1 for comment lines, as a
17109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        # signal that tokenize doesn't know what to do about them;
17209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        # indeed, they're our headache!
17309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        self.stats = []
17409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
17509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    def run(self):
17609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        tokenize.tokenize(self.getline, self.tokeneater)
17709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        # Remove trailing empty lines.
17809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        lines = self.lines
17909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        while lines and lines[-1] == "\n":
18009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            lines.pop()
18109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        # Sentinel.
18209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        stats = self.stats
18309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        stats.append((len(lines), 0))
18409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        # Map count of leading spaces to # we want.
18509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        have2want = {}
18609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        # Program after transformation.
18709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        after = self.after = []
18809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        # Copy over initial empty lines -- there's nothing to do until
18909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        # we see a line with *something* on it.
19009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        i = stats[0][0]
19109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        after.extend(lines[1:i])
19209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        for i in range(len(stats)-1):
19309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            thisstmt, thislevel = stats[i]
19409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            nextstmt = stats[i+1][0]
19509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            have = getlspace(lines[thisstmt])
19609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            want = thislevel * 4
19709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            if want < 0:
19809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                # A comment line.
19909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                if have:
20009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                    # An indented comment line.  If we saw the same
20109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                    # indentation before, reuse what it most recently
20209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                    # mapped to.
20309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                    want = have2want.get(have, -1)
20409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                    if want < 0:
20509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                        # Then it probably belongs to the next real stmt.
20609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                        for j in xrange(i+1, len(stats)-1):
20709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                            jline, jlevel = stats[j]
20809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                            if jlevel >= 0:
20909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                                if have == getlspace(lines[jline]):
21009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                                    want = jlevel * 4
21109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                                break
21209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                    if want < 0:           # Maybe it's a hanging
21309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                                           # comment like this one,
21409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                        # in which case we should shift it like its base
21509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                        # line got shifted.
21609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                        for j in xrange(i-1, -1, -1):
21709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                            jline, jlevel = stats[j]
21809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                            if jlevel >= 0:
21909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                                want = have + getlspace(after[jline-1]) - \
22009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                                       getlspace(lines[jline])
22109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                                break
22209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                    if want < 0:
22309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                        # Still no luck -- leave it alone.
22409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                        want = have
22509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                else:
22609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                    want = 0
22709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            assert want >= 0
22809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            have2want[have] = want
22909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            diff = want - have
23009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            if diff == 0 or have == 0:
23109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                after.extend(lines[thisstmt:nextstmt])
23209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            else:
23309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                for line in lines[thisstmt:nextstmt]:
23409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                    if diff > 0:
23509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                        if line == "\n":
23609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                            after.append(line)
23709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                        else:
23809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                            after.append(" " * diff + line)
23909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                    else:
24009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                        remove = min(getlspace(line), -diff)
24109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                        after.append(line[remove:])
24209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        return self.raw != self.after
24309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
24409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    def write(self, f):
24509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        f.writelines(self.after)
24609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
24709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    # Line-getter for tokenize.
24809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    def getline(self):
24909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        if self.index >= len(self.lines):
25009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            line = ""
25109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        else:
25209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            line = self.lines[self.index]
25309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            self.index += 1
25409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        return line
25509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
25609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    # Line-eater for tokenize.
25709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    def tokeneater(self, type, token, (sline, scol), end, line,
25809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                   INDENT=tokenize.INDENT,
25909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                   DEDENT=tokenize.DEDENT,
26009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                   NEWLINE=tokenize.NEWLINE,
26109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                   COMMENT=tokenize.COMMENT,
26209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                   NL=tokenize.NL):
26309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
26409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        if type == NEWLINE:
26509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            # A program statement, or ENDMARKER, will eventually follow,
26609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            # after some (possibly empty) run of tokens of the form
26709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            #     (NL | COMMENT)* (INDENT | DEDENT+)?
26809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            self.find_stmt = 1
26909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
27009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        elif type == INDENT:
27109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            self.find_stmt = 1
27209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            self.level += 1
27309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
27409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        elif type == DEDENT:
27509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            self.find_stmt = 1
27609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            self.level -= 1
27709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
27809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        elif type == COMMENT:
27909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            if self.find_stmt:
28009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                self.stats.append((sline, -1))
28109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                # but we're still looking for a new stmt, so leave
28209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                # find_stmt alone
28309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
28409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        elif type == NL:
28509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            pass
28609a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
28709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        elif self.find_stmt:
28809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            # This is the first "real token" following a NEWLINE, so it
28909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            # must be the first token of the next program statement, or an
29009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            # ENDMARKER.
29109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            self.find_stmt = 0
29209a025e087fa754b633ebc499d5c96d4e0f844b0mbligh            if line:   # not endmarker
29309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh                self.stats.append((sline, self.level))
29409a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
29509a025e087fa754b633ebc499d5c96d4e0f844b0mbligh# Count number of leading blanks.
29609a025e087fa754b633ebc499d5c96d4e0f844b0mblighdef getlspace(line):
29709a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    i, n = 0, len(line)
29809a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    while i < n and line[i] == " ":
29909a025e087fa754b633ebc499d5c96d4e0f844b0mbligh        i += 1
30009a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    return i
30109a025e087fa754b633ebc499d5c96d4e0f844b0mbligh
30209a025e087fa754b633ebc499d5c96d4e0f844b0mblighif __name__ == '__main__':
30309a025e087fa754b633ebc499d5c96d4e0f844b0mbligh    main()
304