refined usage and options for "py.cleanup":
py.cleanup     # remove "*.pyc" and "*$py.class" (jython) files
    py.cleanup -e .swp -e .cache # also remove files with these extensions
    py.cleanup -s  # remove "build" and "dist" directory next to setup.py files
    py.cleanup -d  # also remove empty directories
    py.cleanup -a  # synonym for "-s -d -e 'pip-log.txt'"
    py.cleanup -n  # dry run, only show what would be removed
--HG--
branch : trunk
			
			
This commit is contained in:
		
							parent
							
								
									3234e6e978
								
							
						
					
					
						commit
						a3d15b2c60
					
				
							
								
								
									
										21
									
								
								CHANGELOG
								
								
								
								
							
							
						
						
									
										21
									
								
								CHANGELOG
								
								
								
								
							|  | @ -1,22 +1,37 @@ | ||||||
| Changes between 1.2.1 and 1.2.0 | Changes between 1.2.1 and 1.2.0 | ||||||
| ===================================== | ===================================== | ||||||
| 
 | 
 | ||||||
| - add a new option "--funcargs" that shows available funcargs  | - refined usage and options for "py.cleanup": | ||||||
|   and their help strings (docstrings on the factory) for a |     py.cleanup     # remove "*.pyc" and "*$py.class" (jython) files | ||||||
|   given test path |     py.cleanup -e .swp -e .cache # also remove files with these extensions | ||||||
|  |     py.cleanup -s  # remove "build" and "dist" directory next to setup.py files | ||||||
|  |     py.cleanup -d  # also remove empty directories  | ||||||
|  |     py.cleanup -a  # synonym for "-s -d -e 'pip-log.txt'" | ||||||
|  |     py.cleanup -n  # dry run, only show what would be removed | ||||||
|  | 
 | ||||||
|  | - add a new option "py.test --funcargs" which shows available funcargs  | ||||||
|  |   and their help strings (docstrings on their respective factory function)  | ||||||
|  |   for a given test path | ||||||
|  | 
 | ||||||
| - display a short and concise traceback if a funcarg lookup fails  | - display a short and concise traceback if a funcarg lookup fails  | ||||||
|  | 
 | ||||||
| - early-load "test*/conftest.py" files, i.e. conftest.py files in | - early-load "test*/conftest.py" files, i.e. conftest.py files in | ||||||
|   directories starting with 'test'.  allows to conveniently keep and access |   directories starting with 'test'.  allows to conveniently keep and access | ||||||
|   test-related options without having to put a conftest.py into the package root dir. |   test-related options without having to put a conftest.py into the package root dir. | ||||||
|  | 
 | ||||||
| - fix issue67: new super-short traceback-printing option: "--tb=line" will print a single line for each failing (python) test indicating its filename, lineno and the failure value | - fix issue67: new super-short traceback-printing option: "--tb=line" will print a single line for each failing (python) test indicating its filename, lineno and the failure value | ||||||
|  | 
 | ||||||
| - fix issue78: always call python-level teardown functions even if the | - fix issue78: always call python-level teardown functions even if the | ||||||
|   according setup failed.  This includes refinements for calling setup_module/class functions  |   according setup failed.  This includes refinements for calling setup_module/class functions  | ||||||
|   which will now only be called once instead of the previous behaviour where they'd be called |   which will now only be called once instead of the previous behaviour where they'd be called | ||||||
|   multiple times if they raise an exception (including a Skipped exception).  Any exception |   multiple times if they raise an exception (including a Skipped exception).  Any exception | ||||||
|   will be re-corded and associated with all tests in the according module/class scope. |   will be re-corded and associated with all tests in the according module/class scope. | ||||||
|  | 
 | ||||||
| - fix issue63: assume <40 columns to be a bogus terminal width, default to 80 | - fix issue63: assume <40 columns to be a bogus terminal width, default to 80 | ||||||
|  | 
 | ||||||
| - update apipkg.py to fix an issue where recursive imports might | - update apipkg.py to fix an issue where recursive imports might | ||||||
|   unnecessarily break importing  |   unnecessarily break importing  | ||||||
|  | 
 | ||||||
| - fix plugin links  | - fix plugin links  | ||||||
| 
 | 
 | ||||||
| Changes between 1.2 and 1.1.1 | Changes between 1.2 and 1.1.1 | ||||||
|  |  | ||||||
|  | @ -1,47 +1,86 @@ | ||||||
| #!/usr/bin/env python  | #!/usr/bin/env python  | ||||||
| 
 | 
 | ||||||
| """\ | """\ | ||||||
| py.cleanup [PATH] | py.cleanup [PATH] ... | ||||||
|  | 
 | ||||||
|  | Delete 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 | ||||||
|  | directories.  | ||||||
| 
 | 
 | ||||||
| Delete pyc file recursively, starting from PATH (which defaults to the current |  | ||||||
| working directory). Don't follow links and don't recurse into directories with |  | ||||||
| a ".". |  | ||||||
| """ | """ | ||||||
| import py | import py | ||||||
|  | import sys, subprocess | ||||||
| 
 | 
 | ||||||
| def main(): | def main(): | ||||||
|     parser = py.std.optparse.OptionParser(usage=__doc__) |     parser = py.std.optparse.OptionParser(usage=__doc__) | ||||||
|     parser.add_option("-e", "--remove", dest="ext", default=".pyc", action="store", |     parser.add_option("-e", metavar="ENDING",  | ||||||
|         help="remove files with the given comma-separated list of extensions" |         dest="endings", default=[".pyc", "$py.class"], action="append",  | ||||||
|     ) |         help=("(multi) recursively remove files with the given ending."  | ||||||
|  |              " '.pyc' and '$py.class' are in the default list.")) | ||||||
|  |     parser.add_option("-d", action="store_true", dest="removedir", | ||||||
|  |                       help="remove empty directories.") | ||||||
|  |     parser.add_option("-s", action="store_true", dest="setup", | ||||||
|  |                       help="remove 'build' and 'dist' directories next to setup.py files") | ||||||
|  |     parser.add_option("-a", action="store_true", dest="all", | ||||||
|  |                       help="synonym for '-S -d -e pip-log.txt'") | ||||||
|     parser.add_option("-n", "--dryrun", dest="dryrun", default=False,  |     parser.add_option("-n", "--dryrun", dest="dryrun", default=False,  | ||||||
|         action="store_true",  |         action="store_true",  | ||||||
|         help="display would-be-removed filenames" |         help="don't actually delete but display would-be-removed filenames.") | ||||||
|     ) |  | ||||||
|     parser.add_option("-d", action="store_true", dest="removedir", |  | ||||||
|                       help="remove empty directories") |  | ||||||
|     (options, args) = parser.parse_args() |     (options, args) = parser.parse_args() | ||||||
|     if not args: |  | ||||||
|         args = ["."] |  | ||||||
|     ext = options.ext.split(",") |  | ||||||
|     def shouldremove(p): |  | ||||||
|         return p.ext in ext |  | ||||||
| 
 | 
 | ||||||
|     for arg in args: |     Cleanup(options, args).main() | ||||||
|         path = py.path.local(arg) |  | ||||||
|         py.builtin.print_("cleaning path", path, "of extensions", ext) |  | ||||||
|         for x in path.visit(shouldremove, lambda x: x.check(dotfile=0, link=0)): |  | ||||||
|             remove(x, options) |  | ||||||
|     if options.removedir: |  | ||||||
|         for x in path.visit(lambda x: x.check(dir=1),  |  | ||||||
|                             lambda x: x.check(dotfile=0, link=0)): |  | ||||||
|             if not x.listdir(): |  | ||||||
|                 remove(x, options) |  | ||||||
| 
 | 
 | ||||||
| def remove(path, options): | class Cleanup: | ||||||
|     if options.dryrun: |     def __init__(self, options, args): | ||||||
|         py.builtin.print_("would remove", path) |         if not args: | ||||||
|     else: |             args = ["."] | ||||||
|         py.builtin.print_("removing", path) |         self.options = options | ||||||
|         path.remove() |         self.args = [py.path.local(x) for x in args] | ||||||
|  |         if options.all: | ||||||
|  |             options.setup = True | ||||||
|  |             options.removedir = True | ||||||
|  |             options.endings.append("pip-log.txt") | ||||||
| 
 | 
 | ||||||
|  |     def main(self): | ||||||
|  |         if self.options.setup: | ||||||
|  |             for arg in self.args: | ||||||
|  |                 self.setupclean(arg) | ||||||
|  |          | ||||||
|  |         for path in self.args: | ||||||
|  |             py.builtin.print_("cleaning path", path,  | ||||||
|  |                 "of extensions", self.options.endings) | ||||||
|  |             for x in path.visit(self.shouldremove, self.recursedir): | ||||||
|  |                 self.remove(x) | ||||||
|  |         if self.options.removedir: | ||||||
|  |             for x in path.visit(lambda x: x.check(dir=1), self.recursedir): | ||||||
|  |                 if not x.listdir(): | ||||||
|  |                     self.remove(x) | ||||||
|  | 
 | ||||||
|  |     def shouldremove(self, p): | ||||||
|  |         for ending in self.options.endings: | ||||||
|  |             if p.basename.endswith(ending): | ||||||
|  |                 return True | ||||||
|  | 
 | ||||||
|  |     def recursedir(self, path): | ||||||
|  |         return path.check(dotfile=0, link=0) | ||||||
|  | 
 | ||||||
|  |     def remove(self, path): | ||||||
|  |         if not path.check(): | ||||||
|  |             return | ||||||
|  |         if self.options.dryrun: | ||||||
|  |             py.builtin.print_("would remove", path) | ||||||
|  |         else: | ||||||
|  |             py.builtin.print_("removing", path) | ||||||
|  |             path.remove() | ||||||
|  | 
 | ||||||
|  |     def XXXcallsetup(self, setup, *args): | ||||||
|  |         old = setup.dirpath().chdir() | ||||||
|  |         try: | ||||||
|  |             subprocess.call([sys.executable, str(setup)] + list(args)) | ||||||
|  |         finally: | ||||||
|  |             old.chdir() | ||||||
|  |              | ||||||
|  |     def setupclean(self, path): | ||||||
|  |         for x in path.visit("setup.py", self.recursedir): | ||||||
|  |             basepath = x.dirpath() | ||||||
|  |             self.remove(basepath / "build") | ||||||
|  |             self.remove(basepath / "dist") | ||||||
|  |  | ||||||
|  | @ -45,8 +45,10 @@ class TestPyCleanup: | ||||||
|         assert p.check() |         assert p.check() | ||||||
|         pyc = p.new(ext='pyc') |         pyc = p.new(ext='pyc') | ||||||
|         pyc.ensure() |         pyc.ensure() | ||||||
|  |         pyclass = p.new(basename=p.basename + '$py.class') | ||||||
|         result = testdir.runpybin("py.cleanup", tmpdir) |         result = testdir.runpybin("py.cleanup", tmpdir) | ||||||
|         assert not pyc.check() |         assert not pyc.check() | ||||||
|  |         assert not pyclass.check() | ||||||
| 
 | 
 | ||||||
|     def test_dir_remove_simple(self, testdir, tmpdir): |     def test_dir_remove_simple(self, testdir, tmpdir): | ||||||
|         subdir = tmpdir.mkdir("subdir") |         subdir = tmpdir.mkdir("subdir") | ||||||
|  | @ -59,3 +61,43 @@ class TestPyCleanup: | ||||||
|         result = testdir.runpybin("py.cleanup", tmpdir, '-d') |         result = testdir.runpybin("py.cleanup", tmpdir, '-d') | ||||||
|         assert result.ret == 0 |         assert result.ret == 0 | ||||||
|         assert not subdir.check() |         assert not subdir.check() | ||||||
|  | 
 | ||||||
|  |     @py.test.mark.multi(opt=["-s"]) | ||||||
|  |     def test_remove_setup_simple(self, testdir, tmpdir, opt): | ||||||
|  |         subdir = tmpdir.mkdir("subdir") | ||||||
|  |         p = subdir.ensure("setup.py") | ||||||
|  |         subdir.mkdir("build").ensure("hello", "world.py") | ||||||
|  |         egg1 = subdir.mkdir("something.egg-info") | ||||||
|  |         egg1.mkdir("whatever") | ||||||
|  |         okbuild = subdir.mkdir("preserved1").mkdir("build") | ||||||
|  |         egg2 = subdir.mkdir("preserved2").mkdir("other.egg-info") | ||||||
|  |         subdir.mkdir("dist") | ||||||
|  |         result = testdir.runpybin("py.cleanup", opt, subdir) | ||||||
|  |         assert result.ret == 0 | ||||||
|  |         assert okbuild.check()       | ||||||
|  |         assert egg1.check() | ||||||
|  |         assert egg2.check() | ||||||
|  |         assert subdir.join("preserved1").check() | ||||||
|  |         assert subdir.join("preserved2").check() | ||||||
|  |         assert not subdir.join("build").check() | ||||||
|  |         assert not subdir.join("dist").check() | ||||||
|  | 
 | ||||||
|  |     def test_remove_all(self, testdir, tmpdir): | ||||||
|  |         tmpdir.ensure("setup.py") | ||||||
|  |         tmpdir.ensure("build", "xyz.py") | ||||||
|  |         tmpdir.ensure("dist", "abc.py") | ||||||
|  |         piplog = tmpdir.ensure("preserved2", "pip-log.txt") | ||||||
|  |         tmpdir.ensure("hello.egg-info") | ||||||
|  |         setup = tmpdir.ensure("setup.py") | ||||||
|  |         tmpdir.ensure("src/a/b") | ||||||
|  |         x = tmpdir.ensure("src/x.py") | ||||||
|  |         x2 = tmpdir.ensure("src/x.pyc") | ||||||
|  |         x3 = tmpdir.ensure("src/x$py.class") | ||||||
|  |         result = testdir.runpybin("py.cleanup", "-a", tmpdir) | ||||||
|  |         assert result.ret == 0 | ||||||
|  |         assert len(tmpdir.listdir()) == 3 | ||||||
|  |         assert setup.check() | ||||||
|  |         assert x.check() | ||||||
|  |         assert not x2.check() | ||||||
|  |         assert not x3.check() | ||||||
|  |         assert not piplog.check() | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue