1b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters#! /usr/bin/env python 2b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 3b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters"""cleanfuture [-d][-r][-v] path ... 4b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 5b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters-d Dry run. Analyze, but don't make any changes to, files. 6b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters-r Recurse. Search for all .py files in subdirectories too. 7b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters-v Verbose. Print informative msgs. 8b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 9b704238a6c4ee0e2fd907d46a1e867661af5d315Tim PetersSearch Python (.py) files for future statements, and remove the features 10b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Petersfrom such statements that are already mandatory in the version of Python 11b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Petersyou're using. 12b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 13b704238a6c4ee0e2fd907d46a1e867661af5d315Tim PetersPass one or more file and/or directory paths. When a directory path, all 14b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters.py files within the directory will be examined, and, if the -r option is 15b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Petersgiven, likewise recursively for subdirectories. 16b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 17b704238a6c4ee0e2fd907d46a1e867661af5d315Tim PetersOverwrites files in place, renaming the originals with a .bak extension. If 18b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peterscleanfuture finds nothing to change, the file is left alone. If cleanfuture 19b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Petersdoes change a file, the changed file is a fixed-point (i.e., running 20b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peterscleanfuture on the resulting .py file won't change it again, at least not 21ebb7133f4f10755a24797513c508c0f09b29d20cTim Petersuntil you try it again with a later Python release). 22b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 23b704238a6c4ee0e2fd907d46a1e867661af5d315Tim PetersLimitations: You can do these things, but this tool won't help you then: 24b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 25b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters+ A future statement cannot be mixed with any other statement on the same 26b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters physical line (separated by semicolon). 27b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 28b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters+ A future statement cannot contain an "as" clause. 29b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 30b704238a6c4ee0e2fd907d46a1e867661af5d315Tim PetersExample: Assuming you're using Python 2.2, if a file containing 31b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 32b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Petersfrom __future__ import nested_scopes, generators 33b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 34b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Petersis analyzed by cleanfuture, the line is rewritten to 35b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 36b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Petersfrom __future__ import generators 37b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 38b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Petersbecause nested_scopes is no longer optional in 2.2 but generators is. 39b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters""" 40b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 41b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Petersimport __future__ 42b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Petersimport tokenize 43b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Petersimport os 44b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Petersimport sys 45b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 46b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Petersdryrun = 0 47b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Petersrecurse = 0 48b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Petersverbose = 0 49b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 50b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Petersdef errprint(*args): 51b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters strings = map(str, args) 523055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters msg = ' '.join(strings) 533055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters if msg[-1:] != '\n': 543055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters msg += '\n' 553055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters sys.stderr.write(msg) 56b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 57b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Petersdef main(): 58b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters import getopt 59b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters global verbose, recurse, dryrun 60b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters try: 61b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters opts, args = getopt.getopt(sys.argv[1:], "drv") 62b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters except getopt.error, msg: 63b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters errprint(msg) 64b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters return 65b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters for o, a in opts: 66b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if o == '-d': 67b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters dryrun += 1 68b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters elif o == '-r': 69b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters recurse += 1 70b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters elif o == '-v': 71b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters verbose += 1 72b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if not args: 73b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters errprint("Usage:", __doc__) 74b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters return 75b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters for arg in args: 76b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters check(arg) 77b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 78b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Petersdef check(file): 79b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if os.path.isdir(file) and not os.path.islink(file): 80b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if verbose: 81b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters print "listing directory", file 82b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters names = os.listdir(file) 83b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters for name in names: 84b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters fullname = os.path.join(file, name) 85b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if ((recurse and os.path.isdir(fullname) and 86b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters not os.path.islink(fullname)) 87b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters or name.lower().endswith(".py")): 88b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters check(fullname) 89b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters return 90b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 91b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if verbose: 92b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters print "checking", file, "...", 93b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters try: 94b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters f = open(file) 95b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters except IOError, msg: 96b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters errprint("%r: I/O Error: %s" % (file, str(msg))) 97b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters return 98b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 993055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters ff = FutureFinder(f, file) 100b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters changed = ff.run() 101b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if changed: 1023055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters ff.gettherest() 1033055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters f.close() 1043055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters if changed: 105b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if verbose: 106b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters print "changed." 107b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if dryrun: 108b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters print "But this is a dry run, so leaving it alone." 109b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters for s, e, line in changed: 110b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters print "%r lines %d-%d" % (file, s+1, e+1) 111b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters for i in range(s, e+1): 112b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters print ff.lines[i], 113b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if line is None: 114b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters print "-- deleted" 115b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters else: 116b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters print "-- change to:" 117b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters print line, 118b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if not dryrun: 119b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters bak = file + ".bak" 120b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if os.path.exists(bak): 121b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters os.remove(bak) 122b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters os.rename(file, bak) 123b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if verbose: 124b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters print "renamed", file, "to", bak 1253055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters g = open(file, "w") 1263055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters ff.write(g) 1273055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters g.close() 128b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if verbose: 129b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters print "wrote new", file 130b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters else: 131b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if verbose: 132b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters print "unchanged." 133b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 134b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Petersclass FutureFinder: 135b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 1363055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters def __init__(self, f, fname): 1373055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters self.f = f 1383055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters self.fname = fname 1393055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters self.ateof = 0 1403055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters self.lines = [] # raw file lines 141b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 142b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters # List of (start_index, end_index, new_line) triples. 143b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters self.changed = [] 144b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 145b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters # Line-getter for tokenize. 146b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters def getline(self): 1473055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters if self.ateof: 1483055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters return "" 1493055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters line = self.f.readline() 1503055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters if line == "": 1513055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters self.ateof = 1 152b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters else: 1533055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters self.lines.append(line) 154b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters return line 155b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 156b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters def run(self): 157b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters STRING = tokenize.STRING 158b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters NL = tokenize.NL 159b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters NEWLINE = tokenize.NEWLINE 160b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters COMMENT = tokenize.COMMENT 161b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters NAME = tokenize.NAME 162b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters OP = tokenize.OP 163b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 164b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters changed = self.changed 165b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters get = tokenize.generate_tokens(self.getline).next 166b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters type, token, (srow, scol), (erow, ecol), line = get() 167b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 1683055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters # Chew up initial comments and blank lines (if any). 1693055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters while type in (COMMENT, NL, NEWLINE): 1703055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters type, token, (srow, scol), (erow, ecol), line = get() 1713055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters 1723055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters # Chew up docstring (if any -- and it may be implicitly catenated!). 1733055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters while type is STRING: 174b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters type, token, (srow, scol), (erow, ecol), line = get() 175b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 176b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters # Analyze the future stmts. 1773055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters while 1: 1783055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters # Chew up comments and blank lines (if any). 1793055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters while type in (COMMENT, NL, NEWLINE): 1803055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters type, token, (srow, scol), (erow, ecol), line = get() 1813055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters 1823055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters if not (type is NAME and token == "from"): 1833055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters break 184b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters startline = srow - 1 # tokenize is one-based 185b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters type, token, (srow, scol), (erow, ecol), line = get() 186b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 187b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if not (type is NAME and token == "__future__"): 188b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters break 189b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters type, token, (srow, scol), (erow, ecol), line = get() 190b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 191b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if not (type is NAME and token == "import"): 192b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters break 193b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters type, token, (srow, scol), (erow, ecol), line = get() 194b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 195b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters # Get the list of features. 196b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters features = [] 197b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters while type is NAME: 198b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters features.append(token) 199b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters type, token, (srow, scol), (erow, ecol), line = get() 200b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 201b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if not (type is OP and token == ','): 202b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters break 203b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters type, token, (srow, scol), (erow, ecol), line = get() 204b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 205b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters # A trailing comment? 206b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters comment = None 207b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if type is COMMENT: 208b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters comment = token 209b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters type, token, (srow, scol), (erow, ecol), line = get() 210b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 211b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if type is not NEWLINE: 2123055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters errprint("Skipping file %r; can't parse line %d:\n%s" % 2133055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters (self.fname, srow, line)) 214b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters return [] 215b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 216b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters endline = srow - 1 217b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 218b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters # Check for obsolete features. 219b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters okfeatures = [] 220b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters for f in features: 221b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters object = getattr(__future__, f, None) 222b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if object is None: 223b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters # A feature we don't know about yet -- leave it in. 224b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters # They'll get a compile-time error when they compile 225b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters # this program, but that's not our job to sort out. 226b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters okfeatures.append(f) 227b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters else: 228b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters released = object.getMandatoryRelease() 229b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if released is None or released <= sys.version_info: 230b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters # Withdrawn or obsolete. 231b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters pass 232b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters else: 233b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters okfeatures.append(f) 234b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 2353055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters # Rewrite the line if at least one future-feature is obsolete. 236b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if len(okfeatures) < len(features): 237b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if len(okfeatures) == 0: 238b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters line = None 239b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters else: 240b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters line = "from __future__ import " 241b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters line += ', '.join(okfeatures) 242b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if comment is not None: 243b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters line += ' ' + comment 244b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters line += '\n' 245b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters changed.append((startline, endline, line)) 246b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 2473055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters # Loop back for more future statements. 248b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 249b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters return changed 250b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 2513055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters def gettherest(self): 2523055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters if self.ateof: 2533055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters self.therest = '' 2543055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters else: 2553055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters self.therest = self.f.read() 2563055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters 257b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters def write(self, f): 258b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters changed = self.changed 259b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters assert changed 260b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters # Prevent calling this again. 261b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters self.changed = [] 262b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters # Apply changes in reverse order. 263b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters changed.reverse() 264b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters for s, e, line in changed: 265b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters if line is None: 266b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters # pure deletion 267b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters del self.lines[s:e+1] 268b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters else: 269b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters self.lines[s:e+1] = [line] 270b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters f.writelines(self.lines) 2713055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters # Copy over the remainder of the file. 2723055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters if self.therest: 2733055ad234aeaf768f58bdbc45ab25c47770b6f81Tim Peters f.write(self.therest) 274b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters 275b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Petersif __name__ == '__main__': 276b704238a6c4ee0e2fd907d46a1e867661af5d315Tim Peters main() 277