Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
test_build_meta.py960 linesDownload Raw Back to tests
1import contextlib2import importlib3import os4import re5import shutil6import signal7import sys8import tarfile9import warnings10from concurrent import futures11from pathlib import Path12from typing import Any, Callable13from zipfile import ZipFile14 15import pytest16from jaraco import path17from packaging.requirements import Requirement18 19from setuptools.warnings import SetuptoolsDeprecationWarning20 21from .textwrap import DALS22 23SETUP_SCRIPT_STUB = "__import__('setuptools').setup()"24 25 26TIMEOUT = int(os.getenv("TIMEOUT_BACKEND_TEST", "180"))  # in seconds27IS_PYPY = '__pypy__' in sys.builtin_module_names28 29 30pytestmark = pytest.mark.skipif(31    sys.platform == "win32" and IS_PYPY,32    reason="The combination of PyPy + Windows + pytest-xdist + ProcessPoolExecutor "33    "is flaky and problematic",34)35 36 37class BuildBackendBase:38    def __init__(self, cwd='.', env=None, backend_name='setuptools.build_meta') -> None:39        self.cwd = cwd40        self.env = env or {}41        self.backend_name = backend_name42 43 44class BuildBackend(BuildBackendBase):45    """PEP 517 Build Backend"""46 47    def __init__(self, *args, **kwargs) -> None:48        super().__init__(*args, **kwargs)49        self.pool = futures.ProcessPoolExecutor(max_workers=1)50 51    def __getattr__(self, name: str) -> Callable[..., Any]:52        """Handles arbitrary function invocations on the build backend."""53 54        def method(*args, **kw):55            root = os.path.abspath(self.cwd)56            caller = BuildBackendCaller(root, self.env, self.backend_name)57            pid = None58            try:59                pid = self.pool.submit(os.getpid).result(TIMEOUT)60                return self.pool.submit(caller, name, *args, **kw).result(TIMEOUT)61            except futures.TimeoutError:62                self.pool.shutdown(wait=False)  # doesn't stop already running processes63                self._kill(pid)64                pytest.xfail(f"Backend did not respond before timeout ({TIMEOUT} s)")65            except (futures.process.BrokenProcessPool, MemoryError, OSError):66                if IS_PYPY:67                    pytest.xfail("PyPy frequently fails tests with ProcessPoolExector")68                raise69 70        return method71 72    def _kill(self, pid):73        if pid is None:74            return75        with contextlib.suppress(ProcessLookupError, OSError):76            os.kill(pid, signal.SIGTERM if os.name == "nt" else signal.SIGKILL)77 78 79class BuildBackendCaller(BuildBackendBase):80    def __init__(self, *args, **kwargs) -> None:81        super().__init__(*args, **kwargs)82 83        (self.backend_name, _, self.backend_obj) = self.backend_name.partition(':')84 85    def __call__(self, name, *args, **kw) -> Any:86        """Handles arbitrary function invocations on the build backend."""87        os.chdir(self.cwd)88        os.environ.update(self.env)89        mod = importlib.import_module(self.backend_name)90 91        if self.backend_obj:92            backend = getattr(mod, self.backend_obj)93        else:94            backend = mod95 96        return getattr(backend, name)(*args, **kw)97 98 99defns = [100    {  # simple setup.py script101        'setup.py': DALS(102            """103            __import__('setuptools').setup(104                name='foo',105                version='0.0.0',106                py_modules=['hello'],107                setup_requires=['six'],108            )109            """110        ),111        'hello.py': DALS(112            """113            def run():114                print('hello')115            """116        ),117    },118    {  # setup.py that relies on __name__119        'setup.py': DALS(120            """121            assert __name__ == '__main__'122            __import__('setuptools').setup(123                name='foo',124                version='0.0.0',125                py_modules=['hello'],126                setup_requires=['six'],127            )128            """129        ),130        'hello.py': DALS(131            """132            def run():133                print('hello')134            """135        ),136    },137    {  # setup.py script that runs arbitrary code138        'setup.py': DALS(139            """140            variable = True141            def function():142                return variable143            assert variable144            __import__('setuptools').setup(145                name='foo',146                version='0.0.0',147                py_modules=['hello'],148                setup_requires=['six'],149            )150            """151        ),152        'hello.py': DALS(153            """154            def run():155                print('hello')156            """157        ),158    },159    {  # setup.py script that constructs temp files to be included in the distribution160        'setup.py': DALS(161            """162            # Some packages construct files on the fly, include them in the package,163            # and immediately remove them after `setup()` (e.g. pybind11==2.9.1).164            # Therefore, we cannot use `distutils.core.run_setup(..., stop_after=...)`165            # to obtain a distribution object first, and then run the distutils166            # commands later, because these files will be removed in the meantime.167 168            with open('world.py', 'w', encoding="utf-8") as f:169                f.write('x = 42')170 171            try:172                __import__('setuptools').setup(173                    name='foo',174                    version='0.0.0',175                    py_modules=['world'],176                    setup_requires=['six'],177                )178            finally:179                # Some packages will clean temporary files180                __import__('os').unlink('world.py')181            """182        ),183    },184    {  # setup.cfg only185        'setup.cfg': DALS(186            """187        [metadata]188        name = foo189        version = 0.0.0190 191        [options]192        py_modules=hello193        setup_requires=six194        """195        ),196        'hello.py': DALS(197            """198        def run():199            print('hello')200        """201        ),202    },203    {  # setup.cfg and setup.py204        'setup.cfg': DALS(205            """206        [metadata]207        name = foo208        version = 0.0.0209 210        [options]211        py_modules=hello212        setup_requires=six213        """214        ),215        'setup.py': "__import__('setuptools').setup()",216        'hello.py': DALS(217            """218        def run():219            print('hello')220        """221        ),222    },223]224 225 226class TestBuildMetaBackend:227    backend_name = 'setuptools.build_meta'228 229    def get_build_backend(self):230        return BuildBackend(backend_name=self.backend_name)231 232    @pytest.fixture(params=defns)233    def build_backend(self, tmpdir, request):234        path.build(request.param, prefix=str(tmpdir))235        with tmpdir.as_cwd():236            yield self.get_build_backend()237 238    def test_get_requires_for_build_wheel(self, build_backend):239        actual = build_backend.get_requires_for_build_wheel()240        expected = ['six']241        assert sorted(actual) == sorted(expected)242 243    def test_get_requires_for_build_sdist(self, build_backend):244        actual = build_backend.get_requires_for_build_sdist()245        expected = ['six']246        assert sorted(actual) == sorted(expected)247 248    def test_build_wheel(self, build_backend):249        dist_dir = os.path.abspath('pip-wheel')250        os.makedirs(dist_dir)251        wheel_name = build_backend.build_wheel(dist_dir)252 253        wheel_file = os.path.join(dist_dir, wheel_name)254        assert os.path.isfile(wheel_file)255 256        # Temporary files should be removed257        assert not os.path.isfile('world.py')258 259        with ZipFile(wheel_file) as zipfile:260            wheel_contents = set(zipfile.namelist())261 262        # Each one of the examples have a single module263        # that should be included in the distribution264        python_scripts = (f for f in wheel_contents if f.endswith('.py'))265        modules = [f for f in python_scripts if not f.endswith('setup.py')]266        assert len(modules) == 1267 268    @pytest.mark.parametrize('build_type', ('wheel', 'sdist'))269    def test_build_with_existing_file_present(self, build_type, tmpdir_cwd):270        # Building a sdist/wheel should still succeed if there's271        # already a sdist/wheel in the destination directory.272        files = {273            'setup.py': "from setuptools import setup\nsetup()",274            'VERSION': "0.0.1",275            'setup.cfg': DALS(276                """277                [metadata]278                name = foo279                version = file: VERSION280                """281            ),282            'pyproject.toml': DALS(283                """284                [build-system]285                requires = ["setuptools", "wheel"]286                build-backend = "setuptools.build_meta"287                """288            ),289        }290 291        path.build(files)292 293        dist_dir = os.path.abspath('preexisting-' + build_type)294 295        build_backend = self.get_build_backend()296        build_method = getattr(build_backend, 'build_' + build_type)297 298        # Build a first sdist/wheel.299        # Note: this also check the destination directory is300        # successfully created if it does not exist already.301        first_result = build_method(dist_dir)302 303        # Change version.304        with open("VERSION", "wt", encoding="utf-8") as version_file:305            version_file.write("0.0.2")306 307        # Build a *second* sdist/wheel.308        second_result = build_method(dist_dir)309 310        assert os.path.isfile(os.path.join(dist_dir, first_result))311        assert first_result != second_result312 313        # And if rebuilding the exact same sdist/wheel?314        open(os.path.join(dist_dir, second_result), 'wb').close()315        third_result = build_method(dist_dir)316        assert third_result == second_result317        assert os.path.getsize(os.path.join(dist_dir, third_result)) > 0318 319    @pytest.mark.parametrize("setup_script", [None, SETUP_SCRIPT_STUB])320    def test_build_with_pyproject_config(self, tmpdir, setup_script):321        files = {322            'pyproject.toml': DALS(323                """324                [build-system]325                requires = ["setuptools", "wheel"]326                build-backend = "setuptools.build_meta"327 328                [project]329                name = "foo"330                license = {text = "MIT"}331                description = "This is a Python package"332                dynamic = ["version", "readme"]333                classifiers = [334                    "Development Status :: 5 - Production/Stable",335                    "Intended Audience :: Developers"336                ]337                urls = {Homepage = "http://github.com"}338                dependencies = [339                    "appdirs",340                ]341 342                [project.optional-dependencies]343                all = [344                    "tomli>=1",345                    "pyscaffold>=4,<5",346                    'importlib; python_version == "2.6"',347                ]348 349                [project.scripts]350                foo = "foo.cli:main"351 352                [tool.setuptools]353                zip-safe = false354                package-dir = {"" = "src"}355                packages = {find = {where = ["src"]}}356                license-files = ["LICENSE*"]357 358                [tool.setuptools.dynamic]359                version = {attr = "foo.__version__"}360                readme = {file = "README.rst"}361 362                [tool.distutils.sdist]363                formats = "gztar"364                """365            ),366            "MANIFEST.in": DALS(367                """368                global-include *.py *.txt369                global-exclude *.py[cod]370                """371            ),372            "README.rst": "This is a ``README``",373            "LICENSE.txt": "---- placeholder MIT license ----",374            "src": {375                "foo": {376                    "__init__.py": "__version__ = '0.1'",377                    "__init__.pyi": "__version__: str",378                    "cli.py": "def main(): print('hello world')",379                    "data.txt": "def main(): print('hello world')",380                    "py.typed": "",381                }382            },383        }384        if setup_script:385            files["setup.py"] = setup_script386 387        build_backend = self.get_build_backend()388        with tmpdir.as_cwd():389            path.build(files)390            msgs = [391                "'tool.setuptools.license-files' is deprecated in favor of 'project.license-files'",392                "`project.license` as a TOML table is deprecated",393            ]394            with warnings.catch_warnings():395                for msg in msgs:396                    warnings.filterwarnings("ignore", msg, SetuptoolsDeprecationWarning)397                sdist_path = build_backend.build_sdist("temp")398                wheel_file = build_backend.build_wheel("temp")399 400        with tarfile.open(os.path.join(tmpdir, "temp", sdist_path)) as tar:401            sdist_contents = set(tar.getnames())402 403        with ZipFile(os.path.join(tmpdir, "temp", wheel_file)) as zipfile:404            wheel_contents = set(zipfile.namelist())405            metadata = str(zipfile.read("foo-0.1.dist-info/METADATA"), "utf-8")406            license = str(407                zipfile.read("foo-0.1.dist-info/licenses/LICENSE.txt"), "utf-8"408            )409            epoints = str(zipfile.read("foo-0.1.dist-info/entry_points.txt"), "utf-8")410 411        assert sdist_contents - {"foo-0.1/setup.py"} == {412            'foo-0.1',413            'foo-0.1/LICENSE.txt',414            'foo-0.1/MANIFEST.in',415            'foo-0.1/PKG-INFO',416            'foo-0.1/README.rst',417            'foo-0.1/pyproject.toml',418            'foo-0.1/setup.cfg',419            'foo-0.1/src',420            'foo-0.1/src/foo',421            'foo-0.1/src/foo/__init__.py',422            'foo-0.1/src/foo/__init__.pyi',423            'foo-0.1/src/foo/cli.py',424            'foo-0.1/src/foo/data.txt',425            'foo-0.1/src/foo/py.typed',426            'foo-0.1/src/foo.egg-info',427            'foo-0.1/src/foo.egg-info/PKG-INFO',428            'foo-0.1/src/foo.egg-info/SOURCES.txt',429            'foo-0.1/src/foo.egg-info/dependency_links.txt',430            'foo-0.1/src/foo.egg-info/entry_points.txt',431            'foo-0.1/src/foo.egg-info/requires.txt',432            'foo-0.1/src/foo.egg-info/top_level.txt',433            'foo-0.1/src/foo.egg-info/not-zip-safe',434        }435        assert wheel_contents == {436            "foo/__init__.py",437            "foo/__init__.pyi",  # include type information by default438            "foo/cli.py",439            "foo/data.txt",  # include_package_data defaults to True440            "foo/py.typed",  # include type information by default441            "foo-0.1.dist-info/licenses/LICENSE.txt",442            "foo-0.1.dist-info/METADATA",443            "foo-0.1.dist-info/WHEEL",444            "foo-0.1.dist-info/entry_points.txt",445            "foo-0.1.dist-info/top_level.txt",446            "foo-0.1.dist-info/RECORD",447        }448        assert license == "---- placeholder MIT license ----"449 450        for line in (451            "Summary: This is a Python package",452            "License: MIT",453            "License-File: LICENSE.txt",454            "Classifier: Intended Audience :: Developers",455            "Requires-Dist: appdirs",456            "Requires-Dist: " + str(Requirement('tomli>=1 ; extra == "all"')),457            "Requires-Dist: "458            + str(Requirement('importlib; python_version=="2.6" and extra =="all"')),459        ):460            assert line in metadata, (line, metadata)461 462        assert metadata.strip().endswith("This is a ``README``")463        assert epoints.strip() == "[console_scripts]\nfoo = foo.cli:main"464 465    def test_static_metadata_in_pyproject_config(self, tmpdir):466        # Make sure static metadata in pyproject.toml is not overwritten by setup.py467        # as required by PEP 621468        files = {469            'pyproject.toml': DALS(470                """471                [build-system]472                requires = ["setuptools", "wheel"]473                build-backend = "setuptools.build_meta"474 475                [project]476                name = "foo"477                description = "This is a Python package"478                version = "42"479                dependencies = ["six"]480                """481            ),482            'hello.py': DALS(483                """484                def run():485                    print('hello')486                """487            ),488            'setup.py': DALS(489                """490                __import__('setuptools').setup(491                    name='bar',492                    version='13',493                )494                """495            ),496        }497        build_backend = self.get_build_backend()498        with tmpdir.as_cwd():499            path.build(files)500            sdist_path = build_backend.build_sdist("temp")501            wheel_file = build_backend.build_wheel("temp")502 503        assert (tmpdir / "temp/foo-42.tar.gz").exists()504        assert (tmpdir / "temp/foo-42-py3-none-any.whl").exists()505        assert not (tmpdir / "temp/bar-13.tar.gz").exists()506        assert not (tmpdir / "temp/bar-42.tar.gz").exists()507        assert not (tmpdir / "temp/foo-13.tar.gz").exists()508        assert not (tmpdir / "temp/bar-13-py3-none-any.whl").exists()509        assert not (tmpdir / "temp/bar-42-py3-none-any.whl").exists()510        assert not (tmpdir / "temp/foo-13-py3-none-any.whl").exists()511 512        with tarfile.open(os.path.join(tmpdir, "temp", sdist_path)) as tar:513            pkg_info = str(tar.extractfile('foo-42/PKG-INFO').read(), "utf-8")514            members = tar.getnames()515            assert "bar-13/PKG-INFO" not in members516 517        with ZipFile(os.path.join(tmpdir, "temp", wheel_file)) as zipfile:518            metadata = str(zipfile.read("foo-42.dist-info/METADATA"), "utf-8")519            members = zipfile.namelist()520            assert "bar-13.dist-info/METADATA" not in members521 522        for file in pkg_info, metadata:523            for line in ("Name: foo", "Version: 42"):524                assert line in file525            for line in ("Name: bar", "Version: 13"):526                assert line not in file527 528    def test_build_sdist(self, build_backend):529        dist_dir = os.path.abspath('pip-sdist')530        os.makedirs(dist_dir)531        sdist_name = build_backend.build_sdist(dist_dir)532 533        assert os.path.isfile(os.path.join(dist_dir, sdist_name))534 535    def test_prepare_metadata_for_build_wheel(self, build_backend):536        dist_dir = os.path.abspath('pip-dist-info')537        os.makedirs(dist_dir)538 539        dist_info = build_backend.prepare_metadata_for_build_wheel(dist_dir)540 541        assert os.path.isfile(os.path.join(dist_dir, dist_info, 'METADATA'))542 543    def test_prepare_metadata_inplace(self, build_backend):544        """545        Some users might pass metadata_directory pre-populated with `.tox` or `.venv`.546        See issue #3523.547        """548        for pre_existing in [549            ".tox/python/lib/python3.10/site-packages/attrs-22.1.0.dist-info",550            ".tox/python/lib/python3.10/site-packages/autocommand-2.2.1.dist-info",551            ".nox/python/lib/python3.10/site-packages/build-0.8.0.dist-info",552            ".venv/python3.10/site-packages/click-8.1.3.dist-info",553            "venv/python3.10/site-packages/distlib-0.3.5.dist-info",554            "env/python3.10/site-packages/docutils-0.19.dist-info",555        ]:556            os.makedirs(pre_existing, exist_ok=True)557        dist_info = build_backend.prepare_metadata_for_build_wheel(".")558        assert os.path.isfile(os.path.join(dist_info, 'METADATA'))559 560    def test_build_sdist_explicit_dist(self, build_backend):561        # explicitly specifying the dist folder should work562        # the folder sdist_directory and the ``--dist-dir`` can be the same563        dist_dir = os.path.abspath('dist')564        sdist_name = build_backend.build_sdist(dist_dir)565        assert os.path.isfile(os.path.join(dist_dir, sdist_name))566 567    def test_build_sdist_version_change(self, build_backend):568        sdist_into_directory = os.path.abspath("out_sdist")569        os.makedirs(sdist_into_directory)570 571        sdist_name = build_backend.build_sdist(sdist_into_directory)572        assert os.path.isfile(os.path.join(sdist_into_directory, sdist_name))573 574        # if the setup.py changes subsequent call of the build meta575        # should still succeed, given the576        # sdist_directory the frontend specifies is empty577        setup_loc = os.path.abspath("setup.py")578        if not os.path.exists(setup_loc):579            setup_loc = os.path.abspath("setup.cfg")580 581        with open(setup_loc, 'rt', encoding="utf-8") as file_handler:582            content = file_handler.read()583        with open(setup_loc, 'wt', encoding="utf-8") as file_handler:584            file_handler.write(content.replace("version='0.0.0'", "version='0.0.1'"))585 586        shutil.rmtree(sdist_into_directory)587        os.makedirs(sdist_into_directory)588 589        sdist_name = build_backend.build_sdist("out_sdist")590        assert os.path.isfile(os.path.join(os.path.abspath("out_sdist"), sdist_name))591 592    def test_build_sdist_pyproject_toml_exists(self, tmpdir_cwd):593        files = {594            'setup.py': DALS(595                """596                __import__('setuptools').setup(597                    name='foo',598                    version='0.0.0',599                    py_modules=['hello']600                )"""601            ),602            'hello.py': '',603            'pyproject.toml': DALS(604                """605                [build-system]606                requires = ["setuptools", "wheel"]607                build-backend = "setuptools.build_meta"608                """609            ),610        }611        path.build(files)612        build_backend = self.get_build_backend()613        targz_path = build_backend.build_sdist("temp")614        with tarfile.open(os.path.join("temp", targz_path)) as tar:615            assert any('pyproject.toml' in name for name in tar.getnames())616 617    def test_build_sdist_setup_py_exists(self, tmpdir_cwd):618        # If build_sdist is called from a script other than setup.py,619        # ensure setup.py is included620        path.build(defns[0])621 622        build_backend = self.get_build_backend()623        targz_path = build_backend.build_sdist("temp")624        with tarfile.open(os.path.join("temp", targz_path)) as tar:625            assert any('setup.py' in name for name in tar.getnames())626 627    def test_build_sdist_setup_py_manifest_excluded(self, tmpdir_cwd):628        # Ensure that MANIFEST.in can exclude setup.py629        files = {630            'setup.py': DALS(631                """632        __import__('setuptools').setup(633            name='foo',634            version='0.0.0',635            py_modules=['hello']636        )"""637            ),638            'hello.py': '',639            'MANIFEST.in': DALS(640                """641        exclude setup.py642        """643            ),644        }645 646        path.build(files)647 648        build_backend = self.get_build_backend()649        targz_path = build_backend.build_sdist("temp")650        with tarfile.open(os.path.join("temp", targz_path)) as tar:651            assert not any('setup.py' in name for name in tar.getnames())652 653    def test_build_sdist_builds_targz_even_if_zip_indicated(self, tmpdir_cwd):654        files = {655            'setup.py': DALS(656                """657                __import__('setuptools').setup(658                    name='foo',659                    version='0.0.0',660                    py_modules=['hello']661                )"""662            ),663            'hello.py': '',664            'setup.cfg': DALS(665                """666                [sdist]667                formats=zip668                """669            ),670        }671 672        path.build(files)673 674        build_backend = self.get_build_backend()675        build_backend.build_sdist("temp")676 677    _relative_path_import_files = {678        'setup.py': DALS(679            """680            __import__('setuptools').setup(681                name='foo',682                version=__import__('hello').__version__,683                py_modules=['hello']684            )"""685        ),686        'hello.py': '__version__ = "0.0.0"',687        'setup.cfg': DALS(688            """689            [sdist]690            formats=zip691            """692        ),693    }694 695    def test_build_sdist_relative_path_import(self, tmpdir_cwd):696        path.build(self._relative_path_import_files)697        build_backend = self.get_build_backend()698        with pytest.raises(ImportError, match="^No module named 'hello'$"):699            build_backend.build_sdist("temp")700 701    _simple_pyproject_example = {702        "pyproject.toml": DALS(703            """704            [project]705            name = "proj"706            version = "42"707            """708        ),709        "src": {"proj": {"__init__.py": ""}},710    }711 712    def _assert_link_tree(self, parent_dir):713        """All files in the directory should be either links or hard links"""714        files = list(Path(parent_dir).glob("**/*"))715        assert files  # Should not be empty716        for file in files:717            assert file.is_symlink() or os.stat(file).st_nlink > 0718 719    def test_editable_without_config_settings(self, tmpdir_cwd):720        """721        Sanity check to ensure tests with --mode=strict are different from the ones722        without --mode.723 724        --mode=strict should create a local directory with a package tree.725        The directory should not get created otherwise.726        """727        path.build(self._simple_pyproject_example)728        build_backend = self.get_build_backend()729        assert not Path("build").exists()730        build_backend.build_editable("temp")731        assert not Path("build").exists()732 733    def test_build_wheel_inplace(self, tmpdir_cwd):734        config_settings = {"--build-option": ["build_ext", "--inplace"]}735        path.build(self._simple_pyproject_example)736        build_backend = self.get_build_backend()737        assert not Path("build").exists()738        Path("build").mkdir()739        build_backend.prepare_metadata_for_build_wheel("build", config_settings)740        build_backend.build_wheel("build", config_settings)741        assert Path("build/proj-42-py3-none-any.whl").exists()742 743    @pytest.mark.parametrize("config_settings", [{"editable-mode": "strict"}])744    def test_editable_with_config_settings(self, tmpdir_cwd, config_settings):745        path.build({**self._simple_pyproject_example, '_meta': {}})746        assert not Path("build").exists()747        build_backend = self.get_build_backend()748        build_backend.prepare_metadata_for_build_editable("_meta", config_settings)749        build_backend.build_editable("temp", config_settings, "_meta")750        self._assert_link_tree(next(Path("build").glob("__editable__.*")))751 752    @pytest.mark.parametrize(753        ("setup_literal", "requirements"),754        [755            ("'foo'", ['foo']),756            ("['foo']", ['foo']),757            (r"'foo\n'", ['foo']),758            (r"'foo\n\n'", ['foo']),759            ("['foo', 'bar']", ['foo', 'bar']),760            (r"'# Has a comment line\nfoo'", ['foo']),761            (r"'foo # Has an inline comment'", ['foo']),762            (r"'foo \\\n >=3.0'", ['foo>=3.0']),763            (r"'foo\nbar'", ['foo', 'bar']),764            (r"'foo\nbar\n'", ['foo', 'bar']),765            (r"['foo\n', 'bar\n']", ['foo', 'bar']),766        ],767    )768    @pytest.mark.parametrize('use_wheel', [True, False])769    def test_setup_requires(self, setup_literal, requirements, use_wheel, tmpdir_cwd):770        files = {771            'setup.py': DALS(772                """773                from setuptools import setup774 775                setup(776                    name="qux",777                    version="0.0.0",778                    py_modules=["hello"],779                    setup_requires={setup_literal},780                )781            """782            ).format(setup_literal=setup_literal),783            'hello.py': DALS(784                """785            def run():786                print('hello')787            """788            ),789        }790 791        path.build(files)792 793        build_backend = self.get_build_backend()794 795        if use_wheel:796            get_requires = build_backend.get_requires_for_build_wheel797        else:798            get_requires = build_backend.get_requires_for_build_sdist799 800        # Ensure that the build requirements are properly parsed801        expected = sorted(requirements)802        actual = get_requires()803 804        assert expected == sorted(actual)805 806    def test_setup_requires_with_auto_discovery(self, tmpdir_cwd):807        # Make sure patches introduced to retrieve setup_requires don't accidentally808        # activate auto-discovery and cause problems due to the incomplete set of809        # attributes passed to MinimalDistribution810        files = {811            'pyproject.toml': DALS(812                """813                [project]814                name = "proj"815                version = "42"816            """817            ),818            "setup.py": DALS(819                """820                __import__('setuptools').setup(821                    setup_requires=["foo"],822                    py_modules = ["hello", "world"]823                )824            """825            ),826            'hello.py': "'hello'",827            'world.py': "'world'",828        }829        path.build(files)830        build_backend = self.get_build_backend()831        setup_requires = build_backend.get_requires_for_build_wheel()832        assert setup_requires == ["foo"]833 834    def test_dont_install_setup_requires(self, tmpdir_cwd):835        files = {836            'setup.py': DALS(837                """838                        from setuptools import setup839 840                        setup(841                            name="qux",842                            version="0.0.0",843                            py_modules=["hello"],844                            setup_requires=["does-not-exist >99"],845                        )846                    """847            ),848            'hello.py': DALS(849                """850                    def run():851                        print('hello')852                    """853            ),854        }855 856        path.build(files)857 858        build_backend = self.get_build_backend()859 860        dist_dir = os.path.abspath('pip-dist-info')861        os.makedirs(dist_dir)862 863        # does-not-exist can't be satisfied, so if it attempts to install864        # setup_requires, it will fail.865        build_backend.prepare_metadata_for_build_wheel(dist_dir)866 867    _sys_argv_0_passthrough = {868        'setup.py': DALS(869            """870            import os871            import sys872 873            __import__('setuptools').setup(874                name='foo',875                version='0.0.0',876            )877 878            sys_argv = os.path.abspath(sys.argv[0])879            file_path = os.path.abspath('setup.py')880            assert sys_argv == file_path881            """882        )883    }884 885    def test_sys_argv_passthrough(self, tmpdir_cwd):886        path.build(self._sys_argv_0_passthrough)887        build_backend = self.get_build_backend()888        with pytest.raises(AssertionError):889            build_backend.build_sdist("temp")890 891    _setup_py_file_abspath = {892        'setup.py': DALS(893            """894            import os895            assert os.path.isabs(__file__)896            __import__('setuptools').setup(897                name='foo',898                version='0.0.0',899                py_modules=['hello'],900                setup_requires=['six'],901            )902            """903        )904    }905 906    def test_setup_py_file_abspath(self, tmpdir_cwd):907        path.build(self._setup_py_file_abspath)908        build_backend = self.get_build_backend()909        build_backend.build_sdist("temp")910 911    @pytest.mark.parametrize('build_hook', ('build_sdist', 'build_wheel'))912    def test_build_with_empty_setuppy(self, build_backend, build_hook):913        files = {'setup.py': ''}914        path.build(files)915 916        msg = re.escape('No distribution was found.')917        with pytest.raises(ValueError, match=msg):918            getattr(build_backend, build_hook)("temp")919 920 921class TestBuildMetaLegacyBackend(TestBuildMetaBackend):922    backend_name = 'setuptools.build_meta:__legacy__'923 924    # build_meta_legacy-specific tests925    def test_build_sdist_relative_path_import(self, tmpdir_cwd):926        # This must fail in build_meta, but must pass in build_meta_legacy927        path.build(self._relative_path_import_files)928 929        build_backend = self.get_build_backend()930        build_backend.build_sdist("temp")931 932    def test_sys_argv_passthrough(self, tmpdir_cwd):933        path.build(self._sys_argv_0_passthrough)934 935        build_backend = self.get_build_backend()936        build_backend.build_sdist("temp")937 938 939@pytest.mark.filterwarnings("ignore::setuptools.SetuptoolsDeprecationWarning")940def test_sys_exit_0_in_setuppy(monkeypatch, tmp_path):941    """Setuptools should be resilient to setup.py with ``sys.exit(0)`` (#3973)."""942    monkeypatch.chdir(tmp_path)943    setuppy = """944        import sys, setuptools945        setuptools.setup(name='foo', version='0.0.0')946        sys.exit(0)947        """948    (tmp_path / "setup.py").write_text(DALS(setuppy), encoding="utf-8")949    backend = BuildBackend(backend_name="setuptools.build_meta")950    assert backend.get_requires_for_build_wheel() == []951 952 953def test_system_exit_in_setuppy(monkeypatch, tmp_path):954    monkeypatch.chdir(tmp_path)955    setuppy = "import sys; sys.exit('some error')"956    (tmp_path / "setup.py").write_text(setuppy, encoding="utf-8")957    with pytest.raises(SystemExit, match="some error"):958        backend = BuildBackend(backend_name="setuptools.build_meta")959        backend.get_requires_for_build_wheel()960 
codekingpro/portable-devtools · Team Ai