1#!/usr/bin/env python 2 3"""\ 4py.cleanup [PATH] ... 5 6Delete typical python development related files recursively under the specified PATH (which defaults to the current working directory). Don't follow links and don't recurse into directories with a dot. Optionally remove setup.py related files and empty 7directories. 8 9""" 10import py 11import sys, subprocess 12 13def main(): 14 parser = py.std.optparse.OptionParser(usage=__doc__) 15 parser.add_option("-e", metavar="ENDING", 16 dest="endings", default=[".pyc", "$py.class"], action="append", 17 help=("(multi) recursively remove files with the given ending." 18 " '.pyc' and '$py.class' are in the default list.")) 19 parser.add_option("-d", action="store_true", dest="removedir", 20 help="remove empty directories.") 21 parser.add_option("-p", action="store_true", dest="pycache", 22 help="remove __pycache__ directories.") 23 parser.add_option("-s", action="store_true", dest="setup", 24 help="remove 'build' and 'dist' directories next to setup.py files") 25 parser.add_option("-a", action="store_true", dest="all", 26 help="synonym for '-p -S -d -e pip-log.txt'") 27 parser.add_option("-q", "--quiet", action="store_true", dest="quiet", 28 help="don't print each removed filename on output") 29 parser.add_option("-n", "--dryrun", dest="dryrun", default=False, 30 action="store_true", 31 help="don't actually delete but display would-be-removed filenames.") 32 (options, args) = parser.parse_args() 33 34 Cleanup(options, args).main() 35 36class Cleanup: 37 def __init__(self, options, args): 38 if not args: 39 args = ["."] 40 self.options = options 41 self.args = [py.path.local(x) for x in args] 42 if options.all: 43 options.setup = True 44 options.removedir = True 45 options.pycache = True 46 options.endings.append("pip-log.txt") 47 48 def main(self): 49 if self.options.setup: 50 for arg in self.args: 51 self.setupclean(arg) 52 53 for path in self.args: 54 py.builtin.print_("cleaning path", path, 55 "of extensions", self.options.endings) 56 for x in path.visit(self.shouldremove, self.recursedir): 57 self.remove(x) 58 if self.options.removedir: 59 for x in path.visit(lambda x: x.check(dir=1), self.recursedir): 60 if not x.listdir(): 61 self.remove(x) 62 63 def shouldremove(self, p): 64 if p.check(file=1): 65 for ending in self.options.endings: 66 if p.basename.endswith(ending): 67 return True 68 return False 69 if self.options.pycache and p.basename == "__pycache__": 70 return True 71 72 def recursedir(self, path): 73 return path.check(dotfile=0, link=0) 74 75 def remove(self, path): 76 if not path.check(): 77 return 78 if self.options.dryrun: 79 py.builtin.print_("would remove", path) 80 else: 81 if not self.options.quiet: 82 py.builtin.print_("removing", path) 83 path.remove() 84 85 def XXXcallsetup(self, setup, *args): 86 old = setup.dirpath().chdir() 87 try: 88 subprocess.call([sys.executable, str(setup)] + list(args)) 89 finally: 90 old.chdir() 91 92 def setupclean(self, path): 93 for x in path.visit("setup.py", self.recursedir): 94 basepath = x.dirpath() 95 self.remove(basepath / "build") 96 self.remove(basepath / "dist") 97