1"""Tests for the 'setuptools' package""" 2 3import sys 4import os 5import distutils.core 6import distutils.cmd 7from distutils.errors import DistutilsOptionError 8from distutils.errors import DistutilsSetupError 9from distutils.core import Extension 10from zipfile import ZipFile 11 12import pytest 13 14from setuptools.extern.packaging import version 15 16import setuptools 17import setuptools.dist 18import setuptools.depends as dep 19from setuptools.depends import Require 20 21 22@pytest.fixture(autouse=True) 23def isolated_dir(tmpdir_cwd): 24 yield 25 26 27def makeSetup(**args): 28 """Return distribution from 'setup(**args)', without executing commands""" 29 30 distutils.core._setup_stop_after = "commandline" 31 32 # Don't let system command line leak into tests! 33 args.setdefault('script_args', ['install']) 34 35 try: 36 return setuptools.setup(**args) 37 finally: 38 distutils.core._setup_stop_after = None 39 40 41needs_bytecode = pytest.mark.skipif( 42 not hasattr(dep, 'get_module_constant'), 43 reason="bytecode support not available", 44) 45 46 47class TestDepends: 48 def testExtractConst(self): 49 if not hasattr(dep, 'extract_constant'): 50 # skip on non-bytecode platforms 51 return 52 53 def f1(): 54 global x, y, z 55 x = "test" 56 y = z 57 58 fc = f1.__code__ 59 60 # unrecognized name 61 assert dep.extract_constant(fc, 'q', -1) is None 62 63 # constant assigned 64 dep.extract_constant(fc, 'x', -1) == "test" 65 66 # expression assigned 67 dep.extract_constant(fc, 'y', -1) == -1 68 69 # recognized name, not assigned 70 dep.extract_constant(fc, 'z', -1) is None 71 72 def testFindModule(self): 73 with pytest.raises(ImportError): 74 dep.find_module('no-such.-thing') 75 with pytest.raises(ImportError): 76 dep.find_module('setuptools.non-existent') 77 f, p, i = dep.find_module('setuptools.tests') 78 f.close() 79 80 @needs_bytecode 81 def testModuleExtract(self): 82 from json import __version__ 83 assert dep.get_module_constant('json', '__version__') == __version__ 84 assert dep.get_module_constant('sys', 'version') == sys.version 85 assert dep.get_module_constant( 86 'setuptools.tests.test_setuptools', '__doc__') == __doc__ 87 88 @needs_bytecode 89 def testRequire(self): 90 req = Require('Json', '1.0.3', 'json') 91 92 assert req.name == 'Json' 93 assert req.module == 'json' 94 assert req.requested_version == version.Version('1.0.3') 95 assert req.attribute == '__version__' 96 assert req.full_name() == 'Json-1.0.3' 97 98 from json import __version__ 99 assert str(req.get_version()) == __version__ 100 assert req.version_ok('1.0.9') 101 assert not req.version_ok('0.9.1') 102 assert not req.version_ok('unknown') 103 104 assert req.is_present() 105 assert req.is_current() 106 107 req = Require('Do-what-I-mean', '1.0', 'd-w-i-m') 108 assert not req.is_present() 109 assert not req.is_current() 110 111 @needs_bytecode 112 def test_require_present(self): 113 # In #1896, this test was failing for months with the only 114 # complaint coming from test runners (not end users). 115 # TODO: Evaluate if this code is needed at all. 116 req = Require('Tests', None, 'tests', homepage="http://example.com") 117 assert req.format is None 118 assert req.attribute is None 119 assert req.requested_version is None 120 assert req.full_name() == 'Tests' 121 assert req.homepage == 'http://example.com' 122 123 from setuptools.tests import __path__ 124 paths = [os.path.dirname(p) for p in __path__] 125 assert req.is_present(paths) 126 assert req.is_current(paths) 127 128 129class TestDistro: 130 def setup_method(self, method): 131 self.e1 = Extension('bar.ext', ['bar.c']) 132 self.e2 = Extension('c.y', ['y.c']) 133 134 self.dist = makeSetup( 135 packages=['a', 'a.b', 'a.b.c', 'b', 'c'], 136 py_modules=['b.d', 'x'], 137 ext_modules=(self.e1, self.e2), 138 package_dir={}, 139 ) 140 141 def testDistroType(self): 142 assert isinstance(self.dist, setuptools.dist.Distribution) 143 144 def testExcludePackage(self): 145 self.dist.exclude_package('a') 146 assert self.dist.packages == ['b', 'c'] 147 148 self.dist.exclude_package('b') 149 assert self.dist.packages == ['c'] 150 assert self.dist.py_modules == ['x'] 151 assert self.dist.ext_modules == [self.e1, self.e2] 152 153 self.dist.exclude_package('c') 154 assert self.dist.packages == [] 155 assert self.dist.py_modules == ['x'] 156 assert self.dist.ext_modules == [self.e1] 157 158 # test removals from unspecified options 159 makeSetup().exclude_package('x') 160 161 def testIncludeExclude(self): 162 # remove an extension 163 self.dist.exclude(ext_modules=[self.e1]) 164 assert self.dist.ext_modules == [self.e2] 165 166 # add it back in 167 self.dist.include(ext_modules=[self.e1]) 168 assert self.dist.ext_modules == [self.e2, self.e1] 169 170 # should not add duplicate 171 self.dist.include(ext_modules=[self.e1]) 172 assert self.dist.ext_modules == [self.e2, self.e1] 173 174 def testExcludePackages(self): 175 self.dist.exclude(packages=['c', 'b', 'a']) 176 assert self.dist.packages == [] 177 assert self.dist.py_modules == ['x'] 178 assert self.dist.ext_modules == [self.e1] 179 180 def testEmpty(self): 181 dist = makeSetup() 182 dist.include(packages=['a'], py_modules=['b'], ext_modules=[self.e2]) 183 dist = makeSetup() 184 dist.exclude(packages=['a'], py_modules=['b'], ext_modules=[self.e2]) 185 186 def testContents(self): 187 assert self.dist.has_contents_for('a') 188 self.dist.exclude_package('a') 189 assert not self.dist.has_contents_for('a') 190 191 assert self.dist.has_contents_for('b') 192 self.dist.exclude_package('b') 193 assert not self.dist.has_contents_for('b') 194 195 assert self.dist.has_contents_for('c') 196 self.dist.exclude_package('c') 197 assert not self.dist.has_contents_for('c') 198 199 def testInvalidIncludeExclude(self): 200 with pytest.raises(DistutilsSetupError): 201 self.dist.include(nonexistent_option='x') 202 with pytest.raises(DistutilsSetupError): 203 self.dist.exclude(nonexistent_option='x') 204 with pytest.raises(DistutilsSetupError): 205 self.dist.include(packages={'x': 'y'}) 206 with pytest.raises(DistutilsSetupError): 207 self.dist.exclude(packages={'x': 'y'}) 208 with pytest.raises(DistutilsSetupError): 209 self.dist.include(ext_modules={'x': 'y'}) 210 with pytest.raises(DistutilsSetupError): 211 self.dist.exclude(ext_modules={'x': 'y'}) 212 213 with pytest.raises(DistutilsSetupError): 214 self.dist.include(package_dir=['q']) 215 with pytest.raises(DistutilsSetupError): 216 self.dist.exclude(package_dir=['q']) 217 218 219class TestCommandTests: 220 def testTestIsCommand(self): 221 test_cmd = makeSetup().get_command_obj('test') 222 assert (isinstance(test_cmd, distutils.cmd.Command)) 223 224 def testLongOptSuiteWNoDefault(self): 225 ts1 = makeSetup(script_args=['test', '--test-suite=foo.tests.suite']) 226 ts1 = ts1.get_command_obj('test') 227 ts1.ensure_finalized() 228 assert ts1.test_suite == 'foo.tests.suite' 229 230 def testDefaultSuite(self): 231 ts2 = makeSetup(test_suite='bar.tests.suite').get_command_obj('test') 232 ts2.ensure_finalized() 233 assert ts2.test_suite == 'bar.tests.suite' 234 235 def testDefaultWModuleOnCmdLine(self): 236 ts3 = makeSetup( 237 test_suite='bar.tests', 238 script_args=['test', '-m', 'foo.tests'] 239 ).get_command_obj('test') 240 ts3.ensure_finalized() 241 assert ts3.test_module == 'foo.tests' 242 assert ts3.test_suite == 'foo.tests.test_suite' 243 244 def testConflictingOptions(self): 245 ts4 = makeSetup( 246 script_args=['test', '-m', 'bar.tests', '-s', 'foo.tests.suite'] 247 ).get_command_obj('test') 248 with pytest.raises(DistutilsOptionError): 249 ts4.ensure_finalized() 250 251 def testNoSuite(self): 252 ts5 = makeSetup().get_command_obj('test') 253 ts5.ensure_finalized() 254 assert ts5.test_suite is None 255 256 257@pytest.fixture 258def example_source(tmpdir): 259 tmpdir.mkdir('foo') 260 (tmpdir / 'foo/bar.py').write('') 261 (tmpdir / 'readme.txt').write('') 262 return tmpdir 263 264 265def test_findall(example_source): 266 found = list(setuptools.findall(str(example_source))) 267 expected = ['readme.txt', 'foo/bar.py'] 268 expected = [example_source.join(fn) for fn in expected] 269 assert found == expected 270 271 272def test_findall_curdir(example_source): 273 with example_source.as_cwd(): 274 found = list(setuptools.findall()) 275 expected = ['readme.txt', os.path.join('foo', 'bar.py')] 276 assert found == expected 277 278 279@pytest.fixture 280def can_symlink(tmpdir): 281 """ 282 Skip if cannot create a symbolic link 283 """ 284 link_fn = 'link' 285 target_fn = 'target' 286 try: 287 os.symlink(target_fn, link_fn) 288 except (OSError, NotImplementedError, AttributeError): 289 pytest.skip("Cannot create symbolic links") 290 os.remove(link_fn) 291 292 293def test_findall_missing_symlink(tmpdir, can_symlink): 294 with tmpdir.as_cwd(): 295 os.symlink('foo', 'bar') 296 found = list(setuptools.findall()) 297 assert found == [] 298 299 300def test_its_own_wheel_does_not_contain_tests(setuptools_wheel): 301 with ZipFile(setuptools_wheel) as zipfile: 302 contents = [f.replace(os.sep, '/') for f in zipfile.namelist()] 303 304 for member in contents: 305 assert '/tests/' not in member 306 307 308def test_convert_path_deprecated(): 309 with pytest.warns(setuptools.SetuptoolsDeprecationWarning): 310 setuptools.convert_path('setuptools/tests') 311