1import os 2import sys 3import shutil 4import signal 5import tarfile 6import importlib 7import contextlib 8from concurrent import futures 9import re 10from zipfile import ZipFile 11 12import pytest 13from jaraco import path 14 15from .textwrap import DALS 16 17SETUP_SCRIPT_STUB = "__import__('setuptools').setup()" 18 19 20TIMEOUT = int(os.getenv("TIMEOUT_BACKEND_TEST", "180")) # in seconds 21IS_PYPY = '__pypy__' in sys.builtin_module_names 22 23 24pytestmark = pytest.mark.skipif( 25 sys.platform == "win32" and IS_PYPY, 26 reason="The combination of PyPy + Windows + pytest-xdist + ProcessPoolExecutor " 27 "is flaky and problematic" 28) 29 30 31class BuildBackendBase: 32 def __init__(self, cwd='.', env={}, backend_name='setuptools.build_meta'): 33 self.cwd = cwd 34 self.env = env 35 self.backend_name = backend_name 36 37 38class BuildBackend(BuildBackendBase): 39 """PEP 517 Build Backend""" 40 41 def __init__(self, *args, **kwargs): 42 super(BuildBackend, self).__init__(*args, **kwargs) 43 self.pool = futures.ProcessPoolExecutor(max_workers=1) 44 45 def __getattr__(self, name): 46 """Handles aribrary function invocations on the build backend.""" 47 48 def method(*args, **kw): 49 root = os.path.abspath(self.cwd) 50 caller = BuildBackendCaller(root, self.env, self.backend_name) 51 pid = None 52 try: 53 pid = self.pool.submit(os.getpid).result(TIMEOUT) 54 return self.pool.submit(caller, name, *args, **kw).result(TIMEOUT) 55 except futures.TimeoutError: 56 self.pool.shutdown(wait=False) # doesn't stop already running processes 57 self._kill(pid) 58 pytest.xfail(f"Backend did not respond before timeout ({TIMEOUT} s)") 59 except (futures.process.BrokenProcessPool, MemoryError, OSError): 60 if IS_PYPY: 61 pytest.xfail("PyPy frequently fails tests with ProcessPoolExector") 62 raise 63 64 return method 65 66 def _kill(self, pid): 67 if pid is None: 68 return 69 with contextlib.suppress(ProcessLookupError, OSError): 70 os.kill(pid, signal.SIGTERM if os.name == "nt" else signal.SIGKILL) 71 72 73class BuildBackendCaller(BuildBackendBase): 74 def __init__(self, *args, **kwargs): 75 super(BuildBackendCaller, self).__init__(*args, **kwargs) 76 77 (self.backend_name, _, 78 self.backend_obj) = self.backend_name.partition(':') 79 80 def __call__(self, name, *args, **kw): 81 """Handles aribrary function invocations on the build backend.""" 82 os.chdir(self.cwd) 83 os.environ.update(self.env) 84 mod = importlib.import_module(self.backend_name) 85 86 if self.backend_obj: 87 backend = getattr(mod, self.backend_obj) 88 else: 89 backend = mod 90 91 return getattr(backend, name)(*args, **kw) 92 93 94defns = [ 95 { # simple setup.py script 96 'setup.py': DALS(""" 97 __import__('setuptools').setup( 98 name='foo', 99 version='0.0.0', 100 py_modules=['hello'], 101 setup_requires=['six'], 102 ) 103 """), 104 'hello.py': DALS(""" 105 def run(): 106 print('hello') 107 """), 108 }, 109 { # setup.py that relies on __name__ 110 'setup.py': DALS(""" 111 assert __name__ == '__main__' 112 __import__('setuptools').setup( 113 name='foo', 114 version='0.0.0', 115 py_modules=['hello'], 116 setup_requires=['six'], 117 ) 118 """), 119 'hello.py': DALS(""" 120 def run(): 121 print('hello') 122 """), 123 }, 124 { # setup.py script that runs arbitrary code 125 'setup.py': DALS(""" 126 variable = True 127 def function(): 128 return variable 129 assert variable 130 __import__('setuptools').setup( 131 name='foo', 132 version='0.0.0', 133 py_modules=['hello'], 134 setup_requires=['six'], 135 ) 136 """), 137 'hello.py': DALS(""" 138 def run(): 139 print('hello') 140 """), 141 }, 142 { # setup.py script that constructs temp files to be included in the distribution 143 'setup.py': DALS(""" 144 # Some packages construct files on the fly, include them in the package, 145 # and immediately remove them after `setup()` (e.g. pybind11==2.9.1). 146 # Therefore, we cannot use `distutils.core.run_setup(..., stop_after=...)` 147 # to obtain a distribution object first, and then run the distutils 148 # commands later, because these files will be removed in the meantime. 149 150 with open('world.py', 'w') as f: 151 f.write('x = 42') 152 153 try: 154 __import__('setuptools').setup( 155 name='foo', 156 version='0.0.0', 157 py_modules=['world'], 158 setup_requires=['six'], 159 ) 160 finally: 161 # Some packages will clean temporary files 162 __import__('os').unlink('world.py') 163 """), 164 }, 165 { # setup.cfg only 166 'setup.cfg': DALS(""" 167 [metadata] 168 name = foo 169 version = 0.0.0 170 171 [options] 172 py_modules=hello 173 setup_requires=six 174 """), 175 'hello.py': DALS(""" 176 def run(): 177 print('hello') 178 """) 179 }, 180 { # setup.cfg and setup.py 181 'setup.cfg': DALS(""" 182 [metadata] 183 name = foo 184 version = 0.0.0 185 186 [options] 187 py_modules=hello 188 setup_requires=six 189 """), 190 'setup.py': "__import__('setuptools').setup()", 191 'hello.py': DALS(""" 192 def run(): 193 print('hello') 194 """) 195 }, 196] 197 198 199class TestBuildMetaBackend: 200 backend_name = 'setuptools.build_meta' 201 202 def get_build_backend(self): 203 return BuildBackend(backend_name=self.backend_name) 204 205 @pytest.fixture(params=defns) 206 def build_backend(self, tmpdir, request): 207 path.build(request.param, prefix=str(tmpdir)) 208 with tmpdir.as_cwd(): 209 yield self.get_build_backend() 210 211 def test_get_requires_for_build_wheel(self, build_backend): 212 actual = build_backend.get_requires_for_build_wheel() 213 expected = ['six', 'wheel'] 214 assert sorted(actual) == sorted(expected) 215 216 def test_get_requires_for_build_sdist(self, build_backend): 217 actual = build_backend.get_requires_for_build_sdist() 218 expected = ['six'] 219 assert sorted(actual) == sorted(expected) 220 221 def test_build_wheel(self, build_backend): 222 dist_dir = os.path.abspath('pip-wheel') 223 os.makedirs(dist_dir) 224 wheel_name = build_backend.build_wheel(dist_dir) 225 226 wheel_file = os.path.join(dist_dir, wheel_name) 227 assert os.path.isfile(wheel_file) 228 229 # Temporary files should be removed 230 assert not os.path.isfile('world.py') 231 232 with ZipFile(wheel_file) as zipfile: 233 wheel_contents = set(zipfile.namelist()) 234 235 # Each one of the examples have a single module 236 # that should be included in the distribution 237 python_scripts = (f for f in wheel_contents if f.endswith('.py')) 238 modules = [f for f in python_scripts if not f.endswith('setup.py')] 239 assert len(modules) == 1 240 241 @pytest.mark.parametrize('build_type', ('wheel', 'sdist')) 242 def test_build_with_existing_file_present(self, build_type, tmpdir_cwd): 243 # Building a sdist/wheel should still succeed if there's 244 # already a sdist/wheel in the destination directory. 245 files = { 246 'setup.py': "from setuptools import setup\nsetup()", 247 'VERSION': "0.0.1", 248 'setup.cfg': DALS(""" 249 [metadata] 250 name = foo 251 version = file: VERSION 252 """), 253 'pyproject.toml': DALS(""" 254 [build-system] 255 requires = ["setuptools", "wheel"] 256 build-backend = "setuptools.build_meta" 257 """), 258 } 259 260 path.build(files) 261 262 dist_dir = os.path.abspath('preexisting-' + build_type) 263 264 build_backend = self.get_build_backend() 265 build_method = getattr(build_backend, 'build_' + build_type) 266 267 # Build a first sdist/wheel. 268 # Note: this also check the destination directory is 269 # successfully created if it does not exist already. 270 first_result = build_method(dist_dir) 271 272 # Change version. 273 with open("VERSION", "wt") as version_file: 274 version_file.write("0.0.2") 275 276 # Build a *second* sdist/wheel. 277 second_result = build_method(dist_dir) 278 279 assert os.path.isfile(os.path.join(dist_dir, first_result)) 280 assert first_result != second_result 281 282 # And if rebuilding the exact same sdist/wheel? 283 open(os.path.join(dist_dir, second_result), 'w').close() 284 third_result = build_method(dist_dir) 285 assert third_result == second_result 286 assert os.path.getsize(os.path.join(dist_dir, third_result)) > 0 287 288 @pytest.mark.parametrize("setup_script", [None, SETUP_SCRIPT_STUB]) 289 def test_build_with_pyproject_config(self, tmpdir, setup_script): 290 files = { 291 'pyproject.toml': DALS(""" 292 [build-system] 293 requires = ["setuptools", "wheel"] 294 build-backend = "setuptools.build_meta" 295 296 [project] 297 name = "foo" 298 license = {text = "MIT"} 299 description = "This is a Python package" 300 dynamic = ["version", "readme"] 301 classifiers = [ 302 "Development Status :: 5 - Production/Stable", 303 "Intended Audience :: Developers" 304 ] 305 urls = {Homepage = "http://github.com"} 306 dependencies = [ 307 "appdirs", 308 ] 309 310 [project.optional-dependencies] 311 all = [ 312 "tomli>=1", 313 "pyscaffold>=4,<5", 314 'importlib; python_version == "2.6"', 315 ] 316 317 [project.scripts] 318 foo = "foo.cli:main" 319 320 [tool.setuptools] 321 zip-safe = false 322 package-dir = {"" = "src"} 323 packages = {find = {where = ["src"]}} 324 license-files = ["LICENSE*"] 325 326 [tool.setuptools.dynamic] 327 version = {attr = "foo.__version__"} 328 readme = {file = "README.rst"} 329 330 [tool.distutils.sdist] 331 formats = "gztar" 332 333 [tool.distutils.bdist_wheel] 334 universal = true 335 """), 336 "MANIFEST.in": DALS(""" 337 global-include *.py *.txt 338 global-exclude *.py[cod] 339 """), 340 "README.rst": "This is a ``README``", 341 "LICENSE.txt": "---- placeholder MIT license ----", 342 "src": { 343 "foo": { 344 "__init__.py": "__version__ = '0.1'", 345 "cli.py": "def main(): print('hello world')", 346 "data.txt": "def main(): print('hello world')", 347 } 348 } 349 } 350 if setup_script: 351 files["setup.py"] = setup_script 352 353 build_backend = self.get_build_backend() 354 with tmpdir.as_cwd(): 355 path.build(files) 356 sdist_path = build_backend.build_sdist("temp") 357 wheel_file = build_backend.build_wheel("temp") 358 359 with tarfile.open(os.path.join(tmpdir, "temp", sdist_path)) as tar: 360 sdist_contents = set(tar.getnames()) 361 362 with ZipFile(os.path.join(tmpdir, "temp", wheel_file)) as zipfile: 363 wheel_contents = set(zipfile.namelist()) 364 metadata = str(zipfile.read("foo-0.1.dist-info/METADATA"), "utf-8") 365 license = str(zipfile.read("foo-0.1.dist-info/LICENSE.txt"), "utf-8") 366 epoints = str(zipfile.read("foo-0.1.dist-info/entry_points.txt"), "utf-8") 367 368 assert sdist_contents - {"foo-0.1/setup.py"} == { 369 'foo-0.1', 370 'foo-0.1/LICENSE.txt', 371 'foo-0.1/MANIFEST.in', 372 'foo-0.1/PKG-INFO', 373 'foo-0.1/README.rst', 374 'foo-0.1/pyproject.toml', 375 'foo-0.1/setup.cfg', 376 'foo-0.1/src', 377 'foo-0.1/src/foo', 378 'foo-0.1/src/foo/__init__.py', 379 'foo-0.1/src/foo/cli.py', 380 'foo-0.1/src/foo/data.txt', 381 'foo-0.1/src/foo.egg-info', 382 'foo-0.1/src/foo.egg-info/PKG-INFO', 383 'foo-0.1/src/foo.egg-info/SOURCES.txt', 384 'foo-0.1/src/foo.egg-info/dependency_links.txt', 385 'foo-0.1/src/foo.egg-info/entry_points.txt', 386 'foo-0.1/src/foo.egg-info/requires.txt', 387 'foo-0.1/src/foo.egg-info/top_level.txt', 388 'foo-0.1/src/foo.egg-info/not-zip-safe', 389 } 390 assert wheel_contents == { 391 "foo/__init__.py", 392 "foo/cli.py", 393 "foo/data.txt", # include_package_data defaults to True 394 "foo-0.1.dist-info/LICENSE.txt", 395 "foo-0.1.dist-info/METADATA", 396 "foo-0.1.dist-info/WHEEL", 397 "foo-0.1.dist-info/entry_points.txt", 398 "foo-0.1.dist-info/top_level.txt", 399 "foo-0.1.dist-info/RECORD", 400 } 401 assert license == "---- placeholder MIT license ----" 402 for line in ( 403 "Summary: This is a Python package", 404 "License: MIT", 405 "Classifier: Intended Audience :: Developers", 406 "Requires-Dist: appdirs", 407 "Requires-Dist: tomli (>=1) ; extra == 'all'", 408 "Requires-Dist: importlib ; (python_version == \"2.6\") and extra == 'all'" 409 ): 410 assert line in metadata 411 412 assert metadata.strip().endswith("This is a ``README``") 413 assert epoints.strip() == "[console_scripts]\nfoo = foo.cli:main" 414 415 def test_static_metadata_in_pyproject_config(self, tmpdir): 416 # Make sure static metadata in pyproject.toml is not overwritten by setup.py 417 # as required by PEP 621 418 files = { 419 'pyproject.toml': DALS(""" 420 [build-system] 421 requires = ["setuptools", "wheel"] 422 build-backend = "setuptools.build_meta" 423 424 [project] 425 name = "foo" 426 description = "This is a Python package" 427 version = "42" 428 dependencies = ["six"] 429 """), 430 'hello.py': DALS(""" 431 def run(): 432 print('hello') 433 """), 434 'setup.py': DALS(""" 435 __import__('setuptools').setup( 436 name='bar', 437 version='13', 438 ) 439 """), 440 } 441 build_backend = self.get_build_backend() 442 with tmpdir.as_cwd(): 443 path.build(files) 444 sdist_path = build_backend.build_sdist("temp") 445 wheel_file = build_backend.build_wheel("temp") 446 447 assert (tmpdir / "temp/foo-42.tar.gz").exists() 448 assert (tmpdir / "temp/foo-42-py3-none-any.whl").exists() 449 assert not (tmpdir / "temp/bar-13.tar.gz").exists() 450 assert not (tmpdir / "temp/bar-42.tar.gz").exists() 451 assert not (tmpdir / "temp/foo-13.tar.gz").exists() 452 assert not (tmpdir / "temp/bar-13-py3-none-any.whl").exists() 453 assert not (tmpdir / "temp/bar-42-py3-none-any.whl").exists() 454 assert not (tmpdir / "temp/foo-13-py3-none-any.whl").exists() 455 456 with tarfile.open(os.path.join(tmpdir, "temp", sdist_path)) as tar: 457 pkg_info = str(tar.extractfile('foo-42/PKG-INFO').read(), "utf-8") 458 members = tar.getnames() 459 assert "bar-13/PKG-INFO" not in members 460 461 with ZipFile(os.path.join(tmpdir, "temp", wheel_file)) as zipfile: 462 metadata = str(zipfile.read("foo-42.dist-info/METADATA"), "utf-8") 463 members = zipfile.namelist() 464 assert "bar-13.dist-info/METADATA" not in members 465 466 for file in pkg_info, metadata: 467 for line in ("Name: foo", "Version: 42"): 468 assert line in file 469 for line in ("Name: bar", "Version: 13"): 470 assert line not in file 471 472 def test_build_sdist(self, build_backend): 473 dist_dir = os.path.abspath('pip-sdist') 474 os.makedirs(dist_dir) 475 sdist_name = build_backend.build_sdist(dist_dir) 476 477 assert os.path.isfile(os.path.join(dist_dir, sdist_name)) 478 479 def test_prepare_metadata_for_build_wheel(self, build_backend): 480 dist_dir = os.path.abspath('pip-dist-info') 481 os.makedirs(dist_dir) 482 483 dist_info = build_backend.prepare_metadata_for_build_wheel(dist_dir) 484 485 assert os.path.isfile(os.path.join(dist_dir, dist_info, 'METADATA')) 486 487 def test_build_sdist_explicit_dist(self, build_backend): 488 # explicitly specifying the dist folder should work 489 # the folder sdist_directory and the ``--dist-dir`` can be the same 490 dist_dir = os.path.abspath('dist') 491 sdist_name = build_backend.build_sdist(dist_dir) 492 assert os.path.isfile(os.path.join(dist_dir, sdist_name)) 493 494 def test_build_sdist_version_change(self, build_backend): 495 sdist_into_directory = os.path.abspath("out_sdist") 496 os.makedirs(sdist_into_directory) 497 498 sdist_name = build_backend.build_sdist(sdist_into_directory) 499 assert os.path.isfile(os.path.join(sdist_into_directory, sdist_name)) 500 501 # if the setup.py changes subsequent call of the build meta 502 # should still succeed, given the 503 # sdist_directory the frontend specifies is empty 504 setup_loc = os.path.abspath("setup.py") 505 if not os.path.exists(setup_loc): 506 setup_loc = os.path.abspath("setup.cfg") 507 508 with open(setup_loc, 'rt') as file_handler: 509 content = file_handler.read() 510 with open(setup_loc, 'wt') as file_handler: 511 file_handler.write( 512 content.replace("version='0.0.0'", "version='0.0.1'")) 513 514 shutil.rmtree(sdist_into_directory) 515 os.makedirs(sdist_into_directory) 516 517 sdist_name = build_backend.build_sdist("out_sdist") 518 assert os.path.isfile( 519 os.path.join(os.path.abspath("out_sdist"), sdist_name)) 520 521 def test_build_sdist_pyproject_toml_exists(self, tmpdir_cwd): 522 files = { 523 'setup.py': DALS(""" 524 __import__('setuptools').setup( 525 name='foo', 526 version='0.0.0', 527 py_modules=['hello'] 528 )"""), 529 'hello.py': '', 530 'pyproject.toml': DALS(""" 531 [build-system] 532 requires = ["setuptools", "wheel"] 533 build-backend = "setuptools.build_meta" 534 """), 535 } 536 path.build(files) 537 build_backend = self.get_build_backend() 538 targz_path = build_backend.build_sdist("temp") 539 with tarfile.open(os.path.join("temp", targz_path)) as tar: 540 assert any('pyproject.toml' in name for name in tar.getnames()) 541 542 def test_build_sdist_setup_py_exists(self, tmpdir_cwd): 543 # If build_sdist is called from a script other than setup.py, 544 # ensure setup.py is included 545 path.build(defns[0]) 546 547 build_backend = self.get_build_backend() 548 targz_path = build_backend.build_sdist("temp") 549 with tarfile.open(os.path.join("temp", targz_path)) as tar: 550 assert any('setup.py' in name for name in tar.getnames()) 551 552 def test_build_sdist_setup_py_manifest_excluded(self, tmpdir_cwd): 553 # Ensure that MANIFEST.in can exclude setup.py 554 files = { 555 'setup.py': DALS(""" 556 __import__('setuptools').setup( 557 name='foo', 558 version='0.0.0', 559 py_modules=['hello'] 560 )"""), 561 'hello.py': '', 562 'MANIFEST.in': DALS(""" 563 exclude setup.py 564 """) 565 } 566 567 path.build(files) 568 569 build_backend = self.get_build_backend() 570 targz_path = build_backend.build_sdist("temp") 571 with tarfile.open(os.path.join("temp", targz_path)) as tar: 572 assert not any('setup.py' in name for name in tar.getnames()) 573 574 def test_build_sdist_builds_targz_even_if_zip_indicated(self, tmpdir_cwd): 575 files = { 576 'setup.py': DALS(""" 577 __import__('setuptools').setup( 578 name='foo', 579 version='0.0.0', 580 py_modules=['hello'] 581 )"""), 582 'hello.py': '', 583 'setup.cfg': DALS(""" 584 [sdist] 585 formats=zip 586 """) 587 } 588 589 path.build(files) 590 591 build_backend = self.get_build_backend() 592 build_backend.build_sdist("temp") 593 594 _relative_path_import_files = { 595 'setup.py': DALS(""" 596 __import__('setuptools').setup( 597 name='foo', 598 version=__import__('hello').__version__, 599 py_modules=['hello'] 600 )"""), 601 'hello.py': '__version__ = "0.0.0"', 602 'setup.cfg': DALS(""" 603 [sdist] 604 formats=zip 605 """) 606 } 607 608 def test_build_sdist_relative_path_import(self, tmpdir_cwd): 609 path.build(self._relative_path_import_files) 610 build_backend = self.get_build_backend() 611 with pytest.raises(ImportError, match="^No module named 'hello'$"): 612 build_backend.build_sdist("temp") 613 614 @pytest.mark.parametrize('setup_literal, requirements', [ 615 ("'foo'", ['foo']), 616 ("['foo']", ['foo']), 617 (r"'foo\n'", ['foo']), 618 (r"'foo\n\n'", ['foo']), 619 ("['foo', 'bar']", ['foo', 'bar']), 620 (r"'# Has a comment line\nfoo'", ['foo']), 621 (r"'foo # Has an inline comment'", ['foo']), 622 (r"'foo \\\n >=3.0'", ['foo>=3.0']), 623 (r"'foo\nbar'", ['foo', 'bar']), 624 (r"'foo\nbar\n'", ['foo', 'bar']), 625 (r"['foo\n', 'bar\n']", ['foo', 'bar']), 626 ]) 627 @pytest.mark.parametrize('use_wheel', [True, False]) 628 def test_setup_requires(self, setup_literal, requirements, use_wheel, 629 tmpdir_cwd): 630 631 files = { 632 'setup.py': DALS(""" 633 from setuptools import setup 634 635 setup( 636 name="qux", 637 version="0.0.0", 638 py_modules=["hello"], 639 setup_requires={setup_literal}, 640 ) 641 """).format(setup_literal=setup_literal), 642 'hello.py': DALS(""" 643 def run(): 644 print('hello') 645 """), 646 } 647 648 path.build(files) 649 650 build_backend = self.get_build_backend() 651 652 if use_wheel: 653 base_requirements = ['wheel'] 654 get_requires = build_backend.get_requires_for_build_wheel 655 else: 656 base_requirements = [] 657 get_requires = build_backend.get_requires_for_build_sdist 658 659 # Ensure that the build requirements are properly parsed 660 expected = sorted(base_requirements + requirements) 661 actual = get_requires() 662 663 assert expected == sorted(actual) 664 665 def test_setup_requires_with_auto_discovery(self, tmpdir_cwd): 666 # Make sure patches introduced to retrieve setup_requires don't accidentally 667 # activate auto-discovery and cause problems due to the incomplete set of 668 # attributes passed to MinimalDistribution 669 files = { 670 'pyproject.toml': DALS(""" 671 [project] 672 name = "proj" 673 version = "42" 674 """), 675 "setup.py": DALS(""" 676 __import__('setuptools').setup( 677 setup_requires=["foo"], 678 py_modules = ["hello", "world"] 679 ) 680 """), 681 'hello.py': "'hello'", 682 'world.py': "'world'", 683 } 684 path.build(files) 685 build_backend = self.get_build_backend() 686 setup_requires = build_backend.get_requires_for_build_wheel() 687 assert setup_requires == ["wheel", "foo"] 688 689 def test_dont_install_setup_requires(self, tmpdir_cwd): 690 files = { 691 'setup.py': DALS(""" 692 from setuptools import setup 693 694 setup( 695 name="qux", 696 version="0.0.0", 697 py_modules=["hello"], 698 setup_requires=["does-not-exist >99"], 699 ) 700 """), 701 'hello.py': DALS(""" 702 def run(): 703 print('hello') 704 """), 705 } 706 707 path.build(files) 708 709 build_backend = self.get_build_backend() 710 711 dist_dir = os.path.abspath('pip-dist-info') 712 os.makedirs(dist_dir) 713 714 # does-not-exist can't be satisfied, so if it attempts to install 715 # setup_requires, it will fail. 716 build_backend.prepare_metadata_for_build_wheel(dist_dir) 717 718 _sys_argv_0_passthrough = { 719 'setup.py': DALS(""" 720 import os 721 import sys 722 723 __import__('setuptools').setup( 724 name='foo', 725 version='0.0.0', 726 ) 727 728 sys_argv = os.path.abspath(sys.argv[0]) 729 file_path = os.path.abspath('setup.py') 730 assert sys_argv == file_path 731 """) 732 } 733 734 def test_sys_argv_passthrough(self, tmpdir_cwd): 735 path.build(self._sys_argv_0_passthrough) 736 build_backend = self.get_build_backend() 737 with pytest.raises(AssertionError): 738 build_backend.build_sdist("temp") 739 740 @pytest.mark.parametrize('build_hook', ('build_sdist', 'build_wheel')) 741 def test_build_with_empty_setuppy(self, build_backend, build_hook): 742 files = {'setup.py': ''} 743 path.build(files) 744 745 with pytest.raises( 746 ValueError, 747 match=re.escape('No distribution was found.')): 748 getattr(build_backend, build_hook)("temp") 749 750 751class TestBuildMetaLegacyBackend(TestBuildMetaBackend): 752 backend_name = 'setuptools.build_meta:__legacy__' 753 754 # build_meta_legacy-specific tests 755 def test_build_sdist_relative_path_import(self, tmpdir_cwd): 756 # This must fail in build_meta, but must pass in build_meta_legacy 757 path.build(self._relative_path_import_files) 758 759 build_backend = self.get_build_backend() 760 build_backend.build_sdist("temp") 761 762 def test_sys_argv_passthrough(self, tmpdir_cwd): 763 path.build(self._sys_argv_0_passthrough) 764 765 build_backend = self.get_build_backend() 766 build_backend.build_sdist("temp") 767