1# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 2# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt 3 4"""Code coverage measurement for Python""" 5 6# Distutils setup for coverage.py 7# This file is used unchanged under all versions of Python, 2.x and 3.x. 8 9import os 10import sys 11 12# Setuptools has to be imported before distutils, or things break. 13from setuptools import setup 14from distutils.core import Extension # pylint: disable=wrong-import-order 15from distutils.command.build_ext import build_ext # pylint: disable=wrong-import-order 16from distutils import errors # pylint: disable=wrong-import-order 17 18 19# Get or massage our metadata. We exec coverage/version.py so we can avoid 20# importing the product code into setup.py. 21 22classifiers = """\ 23Environment :: Console 24Intended Audience :: Developers 25License :: OSI Approved :: Apache Software License 26Operating System :: OS Independent 27Programming Language :: Python 28Programming Language :: Python :: 2 29Programming Language :: Python :: 2.7 30Programming Language :: Python :: 3 31Programming Language :: Python :: 3.5 32Programming Language :: Python :: 3.6 33Programming Language :: Python :: 3.7 34Programming Language :: Python :: 3.8 35Programming Language :: Python :: 3.9 36Programming Language :: Python :: Implementation :: CPython 37Programming Language :: Python :: Implementation :: PyPy 38Topic :: Software Development :: Quality Assurance 39Topic :: Software Development :: Testing 40""" 41 42cov_ver_py = os.path.join(os.path.split(__file__)[0], "coverage/version.py") 43with open(cov_ver_py) as version_file: 44 # __doc__ will be overwritten by version.py. 45 doc = __doc__ 46 # Keep pylint happy. 47 __version__ = __url__ = version_info = "" 48 # Execute the code in version.py. 49 exec(compile(version_file.read(), cov_ver_py, 'exec')) 50 51with open("README.rst") as readme: 52 long_description = readme.read().replace("https://coverage.readthedocs.io", __url__) 53 54with open("CONTRIBUTORS.txt", "rb") as contributors: 55 paras = contributors.read().split(b"\n\n") 56 num_others = len(paras[-1].splitlines()) 57 num_others += 1 # Count Gareth Rees, who is mentioned in the top paragraph. 58 59classifier_list = classifiers.splitlines() 60 61if version_info[3] == 'alpha': 62 devstat = "3 - Alpha" 63elif version_info[3] in ['beta', 'candidate']: 64 devstat = "4 - Beta" 65else: 66 assert version_info[3] == 'final' 67 devstat = "5 - Production/Stable" 68classifier_list.append("Development Status :: " + devstat) 69 70# Create the keyword arguments for setup() 71 72setup_args = dict( 73 name='coverage', 74 version=__version__, 75 76 packages=[ 77 'coverage', 78 ], 79 80 package_data={ 81 'coverage': [ 82 'htmlfiles/*.*', 83 'fullcoverage/*.*', 84 ] 85 }, 86 87 entry_points={ 88 # Install a script as "coverage", and as "coverage[23]", and as 89 # "coverage-2.7" (or whatever). 90 'console_scripts': [ 91 'coverage = coverage.cmdline:main', 92 'coverage%d = coverage.cmdline:main' % sys.version_info[:1], 93 'coverage-%d.%d = coverage.cmdline:main' % sys.version_info[:2], 94 ], 95 }, 96 97 extras_require={ 98 # Enable pyproject.toml support. 99 'toml': ['toml'], 100 }, 101 102 # We need to get HTML assets from our htmlfiles directory. 103 zip_safe=False, 104 105 author='Ned Batchelder and {} others'.format(num_others), 106 author_email='ned@nedbatchelder.com', 107 description=doc, 108 long_description=long_description, 109 long_description_content_type='text/x-rst', 110 keywords='code coverage testing', 111 license='Apache 2.0', 112 classifiers=classifier_list, 113 url="https://github.com/nedbat/coveragepy", 114 project_urls={ 115 'Documentation': __url__, 116 'Funding': ( 117 'https://tidelift.com/subscription/pkg/pypi-coverage' 118 '?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=pypi' 119 ), 120 'Issues': 'https://github.com/nedbat/coveragepy/issues', 121 }, 122 python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4", 123) 124 125# A replacement for the build_ext command which raises a single exception 126# if the build fails, so we can fallback nicely. 127 128ext_errors = ( 129 errors.CCompilerError, 130 errors.DistutilsExecError, 131 errors.DistutilsPlatformError, 132) 133if sys.platform == 'win32': 134 # distutils.msvc9compiler can raise an IOError when failing to 135 # find the compiler 136 ext_errors += (IOError,) 137 138 139class BuildFailed(Exception): 140 """Raise this to indicate the C extension wouldn't build.""" 141 def __init__(self): 142 Exception.__init__(self) 143 self.cause = sys.exc_info()[1] # work around py 2/3 different syntax 144 145 146class ve_build_ext(build_ext): 147 """Build C extensions, but fail with a straightforward exception.""" 148 149 def run(self): 150 """Wrap `run` with `BuildFailed`.""" 151 try: 152 build_ext.run(self) 153 except errors.DistutilsPlatformError: 154 raise BuildFailed() 155 156 def build_extension(self, ext): 157 """Wrap `build_extension` with `BuildFailed`.""" 158 try: 159 # Uncomment to test compile failure handling: 160 # raise errors.CCompilerError("OOPS") 161 build_ext.build_extension(self, ext) 162 except ext_errors: 163 raise BuildFailed() 164 except ValueError as err: 165 # this can happen on Windows 64 bit, see Python issue 7511 166 if "'path'" in str(err): # works with both py 2/3 167 raise BuildFailed() 168 raise 169 170# There are a few reasons we might not be able to compile the C extension. 171# Figure out if we should attempt the C extension or not. 172 173compile_extension = True 174 175if sys.platform.startswith('java'): 176 # Jython can't compile C extensions 177 compile_extension = False 178 179if '__pypy__' in sys.builtin_module_names: 180 # Pypy can't compile C extensions 181 compile_extension = False 182 183if compile_extension: 184 setup_args.update(dict( 185 ext_modules=[ 186 Extension( 187 "coverage.tracer", 188 sources=[ 189 "coverage/ctracer/datastack.c", 190 "coverage/ctracer/filedisp.c", 191 "coverage/ctracer/module.c", 192 "coverage/ctracer/tracer.c", 193 ], 194 ), 195 ], 196 cmdclass={ 197 'build_ext': ve_build_ext, 198 }, 199 )) 200 201 202def main(): 203 """Actually invoke setup() with the arguments we built above.""" 204 # For a variety of reasons, it might not be possible to install the C 205 # extension. Try it with, and if it fails, try it without. 206 try: 207 setup(**setup_args) 208 except BuildFailed as exc: 209 msg = "Couldn't install with extension module, trying without it..." 210 exc_msg = "%s: %s" % (exc.__class__.__name__, exc.cause) 211 print("**\n** %s\n** %s\n**" % (msg, exc_msg)) 212 213 del setup_args['ext_modules'] 214 setup(**setup_args) 215 216if __name__ == '__main__': 217 main() 218