#! /usr/bin/env python3"""cleanfuture [-d][-r][-v] path ...-d Dry run. Analyze, but don't make any changes to, files.-r Recurse. Search for all .py files in subdirectories too.-v Verbose. Print informative msgs.Search Python (.py) files for future statements, and remove the featuresfrom such statements that are already mandatory in the version of Pythonyou're using.Pass one or more file and/or directory paths. When a directory path, all.py files within the directory will be examined, and, if the -r option isgiven, likewise recursively for subdirectories.Overwrites files in place, renaming the originals with a .bak extension. Ifcleanfuture finds nothing to change, the file is left alone. If cleanfuturedoes change a file, the changed file is a fixed-point (i.e., runningcleanfuture on the resulting .py file won't change it again, at least notuntil you try it again with a later Python release).Limitations: You can do these things, but this tool won't help you then:+ A future statement cannot be mixed with any other statement on the samephysical line (separated by semicolon).+ A future statement cannot contain an "as" clause.Example: Assuming you're using Python 2.2, if a file containingfrom __future__ import nested_scopes, generatorsis analyzed by cleanfuture, the line is rewritten tofrom __future__ import generatorsbecause nested_scopes is no longer optional in 2.2 but generators is."""import __future__import tokenizeimport osimport sysdryrun = 0recurse = 0verbose = 0def errprint(*args):strings = map(str, args)msg = ' '.join(strings)if msg[-1:] != '\n':msg += '\n'sys.stderr.write(msg)def main():import getoptglobal verbose, recurse, dryruntry:opts, args = getopt.getopt(sys.argv[1:], "drv")except getopt.error as msg:errprint(msg)returnfor o, a in opts:if o == '-d':dryrun += 1elif o == '-r':recurse += 1elif o == '-v':verbose += 1if not args:errprint("Usage:", __doc__)returnfor arg in args:check(arg)def check(file):if os.path.isdir(file) and not os.path.islink(file):if verbose:print("listing directory", file)names = os.listdir(file)for name in names:fullname = os.path.join(file, name)if ((recurse and os.path.isdir(fullname) andnot os.path.islink(fullname))or name.lower().endswith(".py")):check(fullname)returnif verbose:print("checking", file, "...", end=' ')try:f = open(file)except IOError as msg:errprint("%r: I/O Error: %s" % (file, str(msg)))returnff = FutureFinder(f, file)changed = ff.run()if changed:ff.gettherest()f.close()if changed:if verbose:print("changed.")if dryrun:print("But this is a dry run, so leaving it alone.")for s, e, line in changed:print("%r lines %d-%d" % (file, s+1, e+1))for i in range(s, e+1):print(ff.lines[i], end=' ')if line is None:print("-- deleted")else:print("-- change to:")print(line, end=' ')if not dryrun:bak = file + ".bak"if os.path.exists(bak):os.remove(bak)os.rename(file, bak)if verbose:print("renamed", file, "to", bak)g = open(file, "w")ff.write(g)g.close()if verbose:print("wrote new", file)else:if verbose:print("unchanged.")class FutureFinder:def __init__(self, f, fname):self.f = fself.fname = fnameself.ateof = 0self.lines = [] # raw file lines# List of (start_index, end_index, new_line) triples.self.changed = []# Line-getter for tokenize.def getline(self):if self.ateof:return ""line = self.f.readline()if line == "":self.ateof = 1else:self.lines.append(line)return linedef run(self):STRING = tokenize.STRINGNL = tokenize.NLNEWLINE = tokenize.NEWLINECOMMENT = tokenize.COMMENTNAME = tokenize.NAMEOP = tokenize.OPchanged = self.changedget = tokenize.generate_tokens(self.getline).__next__type, token, (srow, scol), (erow, ecol), line = get()# Chew up initial comments and blank lines (if any).while type in (COMMENT, NL, NEWLINE):type, token, (srow, scol), (erow, ecol), line = get()# Chew up docstring (if any -- and it may be implicitly catenated!).while type is STRING:type, token, (srow, scol), (erow, ecol), line = get()# Analyze the future stmts.while 1:# Chew up comments and blank lines (if any).while type in (COMMENT, NL, NEWLINE):type, token, (srow, scol), (erow, ecol), line = get()if not (type is NAME and token == "from"):breakstartline = srow - 1 # tokenize is one-basedtype, token, (srow, scol), (erow, ecol), line = get()if not (type is NAME and token == "__future__"):breaktype, token, (srow, scol), (erow, ecol), line = get()if not (type is NAME and token == "import"):breaktype, token, (srow, scol), (erow, ecol), line = get()# Get the list of features.features = []while type is NAME:features.append(token)type, token, (srow, scol), (erow, ecol), line = get()if not (type is OP and token == ','):breaktype, token, (srow, scol), (erow, ecol), line = get()# A trailing comment?comment = Noneif type is COMMENT:comment = tokentype, token, (srow, scol), (erow, ecol), line = get()if type is not NEWLINE:errprint("Skipping file %r; can't parse line %d:\n%s" %(self.fname, srow, line))return []endline = srow - 1# Check for obsolete features.okfeatures = []for f in features:object = getattr(__future__, f, None)if object is None:# A feature we don't know about yet -- leave it in.# They'll get a compile-time error when they compile# this program, but that's not our job to sort out.okfeatures.append(f)else:released = object.getMandatoryRelease()if released is None or released <= sys.version_info:# Withdrawn or obsolete.passelse:okfeatures.append(f)# Rewrite the line if at least one future-feature is obsolete.if len(okfeatures) < len(features):if len(okfeatures) == 0:line = Noneelse:line = "from __future__ import "line += ', '.join(okfeatures)if comment is not None:line += ' ' + commentline += '\n'changed.append((startline, endline, line))# Loop back for more future statements.return changeddef gettherest(self):if self.ateof:self.therest = ''else:self.therest = self.f.read()def write(self, f):changed = self.changedassert changed# Prevent calling this again.self.changed = []# Apply changes in reverse order.changed.reverse()for s, e, line in changed:if line is None:# pure deletiondel self.lines[s:e+1]else:self.lines[s:e+1] = [line]f.writelines(self.lines)# Copy over the remainder of the file.if self.therest:f.write(self.therest)if __name__ == '__main__':main()
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。