|
| 1 | +""" |
| 2 | +This package installs a pth file that enables the coveragepy process_startup |
| 3 | +feature in this python prefix/virtualenv in subsequent runs. |
| 4 | +
|
| 5 | +See: http://nedbatchelder.com/code/coverage/subprocess.html |
| 6 | +
|
| 7 | +
|
| 8 | +Demo:: |
| 9 | +
|
| 10 | + $ virtualenv tmpenv |
| 11 | + $ . tmpenv/bin/activate |
| 12 | + $ pip install coverage-enable-subprocess |
| 13 | + $ touch .coveragerc |
| 14 | + $ export COVERAGE_PROCESS_START=$PWD/.coveragerc |
| 15 | + $ echo 'print("oh, hi!")' > ohhi.py |
| 16 | + $ python ohhi.py |
| 17 | + oh, hi! |
| 18 | +
|
| 19 | + $ coverage report |
| 20 | + Name Stmts Miss Cover |
| 21 | + ----------------------------------------------------- |
| 22 | + /etc/python2.6/sitecustomize.py 5 1 80% |
| 23 | + ohhi.py 1 0 100% |
| 24 | + tmpenv/lib/python2.6/site.py 433 392 9% |
| 25 | + ----------------------------------------------------- |
| 26 | + TOTAL 439 393 10% |
| 27 | +
|
| 28 | +
|
| 29 | +For projects that need to cd during their test runs, and run many processes in parallel, |
| 30 | +I ensure a ``$TOP`` variable is exported, and I use this .coveragerc:: |
| 31 | +
|
| 32 | + [run] |
| 33 | + parallel = True |
| 34 | + branch = True |
| 35 | + data_file = $TOP/.coverage |
| 36 | +
|
| 37 | + [report] |
| 38 | + exclude_lines = |
| 39 | + # Have to re-enable the standard pragma |
| 40 | + \\#.*pragma:\\s*no.?cover |
| 41 | +
|
| 42 | + # we can't get coverage for functions that don't return: |
| 43 | + \\#.*never returns |
| 44 | + \\#.*doesn't return |
| 45 | +
|
| 46 | + # Don't complain if tests don't hit defensive assertion code: |
| 47 | + ^\\s*raise Impossible\\b |
| 48 | + ^\\s*raise AssertionError\\b |
| 49 | + ^\\s*raise NotImplementedError\\b |
| 50 | + ^\\s*return NotImplemented\\b |
| 51 | +
|
| 52 | + # Don't complain if tests don't hit re-raise of unexpected errors: |
| 53 | + ^\\s*raise$ |
| 54 | +
|
| 55 | + # if main is covered, we're good: |
| 56 | + ^\\s*exit\\(main\\(\\)\\)$ |
| 57 | + show_missing = True |
| 58 | +
|
| 59 | + [html] |
| 60 | + directory = $TOP/coverage-html |
| 61 | +
|
| 62 | + # vim:ft=dosini |
| 63 | +""" |
| 64 | +from __future__ import absolute_import |
| 65 | +from __future__ import print_function |
| 66 | +from __future__ import unicode_literals |
| 67 | + |
| 68 | +from distutils import log |
| 69 | + |
| 70 | +from setuptools import setup |
| 71 | +from setuptools.command.install import install as orig_install |
| 72 | + |
| 73 | +PTH = '''\ |
| 74 | +try: |
| 75 | + import coverage |
| 76 | +except ImportError: |
| 77 | + pass |
| 78 | +else: |
| 79 | + coverage.process_startup() |
| 80 | +''' |
| 81 | + |
| 82 | +DOC = __doc__ |
| 83 | + |
| 84 | + |
| 85 | +class Install(orig_install): |
| 86 | + """ |
| 87 | + default semantics for install.extra_path cause all installed modules to go |
| 88 | + into a directory whose name is equal to the contents of the .pth file. |
| 89 | +
|
| 90 | + All that was necessary was to remove that one behavior to get what you'd |
| 91 | + generally want. |
| 92 | + """ |
| 93 | + # pylint:disable=no-member,attribute-defined-outside-init,access-member-before-definition |
| 94 | + |
| 95 | + def initialize_options(self): |
| 96 | + orig_install.initialize_options(self) |
| 97 | + name = self.distribution.metadata.name |
| 98 | + |
| 99 | + contents = 'import sys; exec(%r)\n' % PTH |
| 100 | + self.extra_path = (name, contents) |
| 101 | + |
| 102 | + def finalize_options(self): |
| 103 | + orig_install.finalize_options(self) |
| 104 | + |
| 105 | + from os.path import relpath, join |
| 106 | + install_suffix = relpath(self.install_lib, self.install_libbase) |
| 107 | + if install_suffix == '.': |
| 108 | + log.info('skipping install of .pth during easy-install') |
| 109 | + elif install_suffix == self.extra_path[1]: |
| 110 | + self.install_lib = self.install_libbase |
| 111 | + log.info( |
| 112 | + "will install .pth to '%s.pth'", |
| 113 | + join(self.install_lib, self.extra_path[0]), |
| 114 | + ) |
| 115 | + else: |
| 116 | + raise AssertionError( |
| 117 | + 'unexpected install_suffix', |
| 118 | + self.install_lib, self.install_libbase, install_suffix, |
| 119 | + ) |
| 120 | + |
| 121 | + |
| 122 | +def main(): |
| 123 | + """the entry point""" |
| 124 | + setup( |
| 125 | + name=str('coverage_enable_subprocess'), |
| 126 | + version='0', |
| 127 | + url="https://github.com/bukzor/python-coverage-enable-subprocess", |
| 128 | + license="MIT", |
| 129 | + author="Buck Evan", |
| 130 | + author_email="buck.2019@gmail.com", |
| 131 | + description="enable python coverage for subprocesses", |
| 132 | + long_description=DOC, |
| 133 | + zip_safe=False, |
| 134 | + classifiers=[ |
| 135 | + 'Programming Language :: Python :: 2.6', |
| 136 | + 'Programming Language :: Python :: 2.7', |
| 137 | + 'Programming Language :: Python :: 3.3', |
| 138 | + 'Programming Language :: Python :: 3.4', |
| 139 | + 'License :: OSI Approved :: MIT License', |
| 140 | + ], |
| 141 | + install_requires=[ |
| 142 | + 'coverage', |
| 143 | + ], |
| 144 | + cmdclass={ |
| 145 | + 'install': Install, |
| 146 | + }, |
| 147 | + options={ |
| 148 | + 'bdist_wheel': { |
| 149 | + 'universal': 1, |
| 150 | + }, |
| 151 | + }, |
| 152 | + ) |
| 153 | + |
| 154 | + |
| 155 | +if __name__ == '__main__': |
| 156 | + exit(main()) |
0 commit comments