OLD | NEW |
(Empty) | |
| 1 """Helper for building, testing, and linting coverage.py. |
| 2 |
| 3 To get portability, all these operations are written in Python here instead |
| 4 of in shell scripts, batch files, or Makefiles. |
| 5 |
| 6 """ |
| 7 |
| 8 import fnmatch |
| 9 import glob |
| 10 import inspect |
| 11 import os |
| 12 import platform |
| 13 import socket |
| 14 import sys |
| 15 import zipfile |
| 16 |
| 17 |
| 18 # Functions named do_* are executable from the command line: do_blah is run |
| 19 # by "python igor.py blah". |
| 20 |
| 21 |
| 22 def do_remove_extension(): |
| 23 """Remove the compiled C extension, no matter what its name.""" |
| 24 |
| 25 so_patterns = """ |
| 26 tracer.so |
| 27 tracer.*.so |
| 28 tracer.pyd |
| 29 """.split() |
| 30 |
| 31 for pattern in so_patterns: |
| 32 pattern = os.path.join("coverage", pattern) |
| 33 for filename in glob.glob(pattern): |
| 34 try: |
| 35 os.remove(filename) |
| 36 except OSError: |
| 37 pass |
| 38 |
| 39 def run_tests(tracer, *nose_args): |
| 40 """The actual running of tests.""" |
| 41 import nose.core |
| 42 if tracer == "py": |
| 43 label = "with Python tracer" |
| 44 else: |
| 45 label = "with C tracer" |
| 46 if os.environ.get("COVERAGE_NO_EXTENSION"): |
| 47 print("Skipping tests, no C extension in this environment") |
| 48 return |
| 49 print_banner(label) |
| 50 os.environ["COVERAGE_TEST_TRACER"] = tracer |
| 51 nose_args = ["nosetests"] + list(nose_args) |
| 52 nose.core.main(argv=nose_args) |
| 53 |
| 54 def run_tests_with_coverage(tracer, *nose_args): |
| 55 """Run tests, but with coverage.""" |
| 56 import coverage |
| 57 |
| 58 os.environ['COVERAGE_PROCESS_START'] = os.path.abspath('metacov.ini') |
| 59 os.environ['COVERAGE_HOME'] = os.getcwd() |
| 60 |
| 61 # Create the .pth file that will let us measure coverage in sub-processes. |
| 62 import nose |
| 63 pth_dir = os.path.dirname(os.path.dirname(nose.__file__)) |
| 64 pth_path = os.path.join(pth_dir, "covcov.pth") |
| 65 pth_file = open(pth_path, "w") |
| 66 try: |
| 67 pth_file.write("import coverage; coverage.process_startup()\n") |
| 68 finally: |
| 69 pth_file.close() |
| 70 |
| 71 version = "%s%s" % sys.version_info[:2] |
| 72 suffix = "%s_%s_%s" % (version, tracer, socket.gethostname()) |
| 73 |
| 74 cov = coverage.coverage(config_file="metacov.ini", data_suffix=suffix) |
| 75 # Cheap trick: the coverage code itself is excluded from measurement, but |
| 76 # if we clobber the cover_prefix in the coverage object, we can defeat the |
| 77 # self-detection. |
| 78 cov.cover_prefix = "Please measure coverage.py!" |
| 79 cov.erase() |
| 80 cov.start() |
| 81 |
| 82 try: |
| 83 # Re-import coverage to get it coverage tested! I don't understand all |
| 84 # the mechanics here, but if I don't carry over the imported modules |
| 85 # (in covmods), then things go haywire (os == None, eventually). |
| 86 covmods = {} |
| 87 covdir = os.path.split(coverage.__file__)[0] |
| 88 # We have to make a list since we'll be deleting in the loop. |
| 89 modules = list(sys.modules.items()) |
| 90 for name, mod in modules: |
| 91 if name.startswith('coverage'): |
| 92 if getattr(mod, '__file__', "??").startswith(covdir): |
| 93 covmods[name] = mod |
| 94 del sys.modules[name] |
| 95 import coverage # don't warn about re-import: pylint: disable=W0404 |
| 96 sys.modules.update(covmods) |
| 97 |
| 98 # Run nosetests, with the arguments from our command line. |
| 99 try: |
| 100 run_tests(tracer, *nose_args) |
| 101 except SystemExit: |
| 102 # nose3 seems to raise SystemExit, not sure why? |
| 103 pass |
| 104 finally: |
| 105 cov.stop() |
| 106 os.remove(pth_path) |
| 107 |
| 108 cov.save() |
| 109 |
| 110 def do_combine_html(): |
| 111 """Combine data from a meta-coverage run, and make the HTML report.""" |
| 112 import coverage |
| 113 os.environ['COVERAGE_HOME'] = os.getcwd() |
| 114 cov = coverage.coverage(config_file="metacov.ini") |
| 115 cov.load() |
| 116 cov.combine() |
| 117 cov.save() |
| 118 cov.html_report() |
| 119 |
| 120 def do_test_with_tracer(tracer, *noseargs): |
| 121 """Run nosetests with a particular tracer.""" |
| 122 if os.environ.get("COVERAGE_COVERAGE", ""): |
| 123 return run_tests_with_coverage(tracer, *noseargs) |
| 124 else: |
| 125 return run_tests(tracer, *noseargs) |
| 126 |
| 127 def do_zip_mods(): |
| 128 """Build the zipmods.zip file.""" |
| 129 zf = zipfile.ZipFile("tests/zipmods.zip", "w") |
| 130 zf.write("tests/covmodzip1.py", "covmodzip1.py") |
| 131 zf.close() |
| 132 |
| 133 def do_install_egg(): |
| 134 """Install the egg1 egg for tests.""" |
| 135 # I am pretty certain there are easier ways to install eggs... |
| 136 # pylint: disable=F0401,E0611,E1101 |
| 137 import distutils.core |
| 138 cur_dir = os.getcwd() |
| 139 os.chdir("tests/eggsrc") |
| 140 distutils.core.run_setup("setup.py", ["--quiet", "bdist_egg"]) |
| 141 egg = glob.glob("dist/*.egg")[0] |
| 142 distutils.core.run_setup( |
| 143 "setup.py", ["--quiet", "easy_install", "--no-deps", "--zip-ok", egg] |
| 144 ) |
| 145 os.chdir(cur_dir) |
| 146 |
| 147 def do_check_eol(): |
| 148 """Check files for incorrect newlines and trailing whitespace.""" |
| 149 |
| 150 ignore_dirs = [ |
| 151 '.svn', '.hg', '.tox', '.tox_kits', 'coverage.egg-info', |
| 152 '_build', 'covtestegg1.egg-info', |
| 153 ] |
| 154 checked = set([]) |
| 155 |
| 156 def check_file(fname, crlf=True, trail_white=True): |
| 157 """Check a single file for whitespace abuse.""" |
| 158 fname = os.path.relpath(fname) |
| 159 if fname in checked: |
| 160 return |
| 161 checked.add(fname) |
| 162 |
| 163 line = None |
| 164 for n, line in enumerate(open(fname, "rb")): |
| 165 if crlf: |
| 166 if "\r" in line: |
| 167 print("%s@%d: CR found" % (fname, n+1)) |
| 168 return |
| 169 if trail_white: |
| 170 line = line[:-1] |
| 171 if not crlf: |
| 172 line = line.rstrip('\r') |
| 173 if line.rstrip() != line: |
| 174 print("%s@%d: trailing whitespace found" % (fname, n+1)) |
| 175 return |
| 176 |
| 177 if line is not None and not line.strip(): |
| 178 print("%s: final blank line" % (fname,)) |
| 179 |
| 180 def check_files(root, patterns, **kwargs): |
| 181 """Check a number of files for whitespace abuse.""" |
| 182 for root, dirs, files in os.walk(root): |
| 183 for f in files: |
| 184 fname = os.path.join(root, f) |
| 185 for p in patterns: |
| 186 if fnmatch.fnmatch(fname, p): |
| 187 check_file(fname, **kwargs) |
| 188 break |
| 189 for dir_name in ignore_dirs: |
| 190 if dir_name in dirs: |
| 191 dirs.remove(dir_name) |
| 192 |
| 193 check_files("coverage", ["*.py", "*.c"]) |
| 194 check_files("coverage/htmlfiles", ["*.html", "*.css", "*.js"]) |
| 195 check_file("tests/farm/html/src/bom.py", crlf=False) |
| 196 check_files("tests", ["*.py"]) |
| 197 check_files("tests", ["*,cover"], trail_white=False) |
| 198 check_files("tests/js", ["*.js", "*.html"]) |
| 199 check_file("setup.py") |
| 200 check_file("igor.py") |
| 201 check_file("Makefile") |
| 202 check_file(".hgignore") |
| 203 check_file(".travis.yml") |
| 204 check_files("doc", ["*.rst"]) |
| 205 check_files(".", ["*.txt"]) |
| 206 |
| 207 |
| 208 def print_banner(label): |
| 209 """Print the version of Python.""" |
| 210 try: |
| 211 impl = platform.python_implementation() |
| 212 except AttributeError: |
| 213 impl = "Python" |
| 214 |
| 215 version = platform.python_version() |
| 216 |
| 217 if '__pypy__' in sys.builtin_module_names: |
| 218 pypy_version = sys.pypy_version_info # pylint: disable=E1101 |
| 219 version += " (pypy %s)" % ".".join([str(v) for v in pypy_version]) |
| 220 |
| 221 print('=== %s %s %s (%s) ===' % (impl, version, label, sys.executable)) |
| 222 |
| 223 |
| 224 def do_help(): |
| 225 """List the available commands""" |
| 226 items = list(globals().items()) |
| 227 items.sort() |
| 228 for name, value in items: |
| 229 if name.startswith('do_'): |
| 230 print("%-20s%s" % (name[3:], value.__doc__)) |
| 231 |
| 232 |
| 233 def main(args): |
| 234 """Main command-line execution for igor. |
| 235 |
| 236 Verbs are taken from the command line, and extra words taken as directed |
| 237 by the arguments needed by the handler. |
| 238 |
| 239 """ |
| 240 while args: |
| 241 verb = args.pop(0) |
| 242 handler = globals().get('do_'+verb) |
| 243 if handler is None: |
| 244 print("*** No handler for %r" % verb) |
| 245 return 1 |
| 246 argspec = inspect.getargspec(handler) |
| 247 if argspec[1]: |
| 248 # Handler has *args, give it all the rest of the command line. |
| 249 handler_args = args |
| 250 args = [] |
| 251 else: |
| 252 # Handler has specific arguments, give it only what it needs. |
| 253 num_args = len(argspec[0]) |
| 254 handler_args = args[:num_args] |
| 255 args = args[num_args:] |
| 256 ret = handler(*handler_args) |
| 257 # If a handler returns a failure-like value, stop. |
| 258 if ret: |
| 259 return ret |
| 260 |
| 261 if __name__ == '__main__': |
| 262 sys.exit(main(sys.argv[1:])) |
OLD | NEW |