Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
test_wheel.py691 linesDownload Raw Back to tests
1"""wheel tests"""2 3from __future__ import annotations4 5import contextlib6import glob7import inspect8import os9import pathlib10import stat11import subprocess12import sys13import sysconfig14import zipfile15from typing import Any16 17import pytest18from jaraco import path19from packaging.tags import parse_tag20 21from setuptools._importlib import metadata22from setuptools.wheel import Wheel23 24from .contexts import tempdir25from .textwrap import DALS26 27from distutils.sysconfig import get_config_var28from distutils.util import get_platform29 30WHEEL_INFO_TESTS = (31    ('invalid.whl', ValueError),32    (33        'simplewheel-2.0-1-py2.py3-none-any.whl',34        {35            'project_name': 'simplewheel',36            'version': '2.0',37            'build': '1',38            'py_version': 'py2.py3',39            'abi': 'none',40            'platform': 'any',41        },42    ),43    (44        'simple.dist-0.1-py2.py3-none-any.whl',45        {46            'project_name': 'simple.dist',47            'version': '0.1',48            'build': None,49            'py_version': 'py2.py3',50            'abi': 'none',51            'platform': 'any',52        },53    ),54    (55        'example_pkg_a-1-py3-none-any.whl',56        {57            'project_name': 'example_pkg_a',58            'version': '1',59            'build': None,60            'py_version': 'py3',61            'abi': 'none',62            'platform': 'any',63        },64    ),65    (66        'PyQt5-5.9-5.9.1-cp35.cp36.cp37-abi3-manylinux1_x86_64.whl',67        {68            'project_name': 'PyQt5',69            'version': '5.9',70            'build': '5.9.1',71            'py_version': 'cp35.cp36.cp37',72            'abi': 'abi3',73            'platform': 'manylinux1_x86_64',74        },75    ),76)77 78 79@pytest.mark.parametrize(80    ('filename', 'info'), WHEEL_INFO_TESTS, ids=[t[0] for t in WHEEL_INFO_TESTS]81)82def test_wheel_info(filename, info):83    if inspect.isclass(info):84        with pytest.raises(info):85            Wheel(filename)86        return87    w = Wheel(filename)88    assert {k: getattr(w, k) for k in info.keys()} == info89 90 91@contextlib.contextmanager92def build_wheel(extra_file_defs=None, **kwargs):93    file_defs = {94        'setup.py': (95            DALS(96                """97            # -*- coding: utf-8 -*-98            from setuptools import setup99            import setuptools100            setup(**%r)101            """102            )103            % kwargs104        ).encode('utf-8'),105    }106    if extra_file_defs:107        file_defs.update(extra_file_defs)108    with tempdir() as source_dir:109        path.build(file_defs, source_dir)110        subprocess.check_call(111            (sys.executable, 'setup.py', '-q', 'bdist_wheel'), cwd=source_dir112        )113        yield glob.glob(os.path.join(source_dir, 'dist', '*.whl'))[0]114 115 116def tree_set(root):117    return {118        os.path.join(os.path.relpath(dirpath, root), filename)119        for dirpath, dirnames, filenames in os.walk(root)120        for filename in filenames121    }122 123 124def flatten_tree(tree):125    """Flatten nested dicts and lists into a full list of paths"""126    output = set()127    for node, contents in tree.items():128        if isinstance(contents, dict):129            contents = flatten_tree(contents)130 131        for elem in contents:132            if isinstance(elem, dict):133                output |= {os.path.join(node, val) for val in flatten_tree(elem)}134            else:135                output.add(os.path.join(node, elem))136    return output137 138 139def format_install_tree(tree):140    return {141        x.format(142            py_version=sysconfig.get_python_version(),143            platform=get_platform(),144            shlib_ext=get_config_var('EXT_SUFFIX') or get_config_var('SO'),145        )146        for x in tree147    }148 149 150def _check_wheel_install(151    filename, install_dir, install_tree_includes, project_name, version, requires_txt152):153    w = Wheel(filename)154    egg_path = os.path.join(install_dir, w.egg_name())155    w.install_as_egg(egg_path)156    if install_tree_includes is not None:157        install_tree = format_install_tree(install_tree_includes)158        exp = tree_set(install_dir)159        assert install_tree.issubset(exp), install_tree - exp160 161    (dist,) = metadata.Distribution.discover(path=[egg_path])162 163    # pyright is nitpicky; fine to assume dist.metadata.__getitem__ will fail or return None164    # (https://github.com/pypa/setuptools/pull/5006#issuecomment-2894774288)165    assert dist.metadata['Name'] == project_name  # pyright: ignore  # noqa: PGH003166    assert dist.metadata['Version'] == version  # pyright: ignore  # noqa: PGH003167    assert dist.read_text('requires.txt') == requires_txt168 169 170class Record:171    def __init__(self, id, **kwargs) -> None:172        self._id = id173        self._fields = kwargs174 175    def __repr__(self) -> str:176        return f'{self._id}(**{self._fields!r})'177 178 179# Using Any to avoid possible type union issues later in test180# making a TypedDict is not worth in a test and anonymous/inline TypedDict are experimental181# https://github.com/python/mypy/issues/9884182WHEEL_INSTALL_TESTS: tuple[dict[str, Any], ...] = (183    dict(184        id='basic',185        file_defs={'foo': {'__init__.py': ''}},186        setup_kwargs=dict(187            packages=['foo'],188        ),189        install_tree=flatten_tree({190            'foo-1.0-py{py_version}.egg': {191                'EGG-INFO': ['PKG-INFO', 'RECORD', 'WHEEL', 'top_level.txt'],192                'foo': ['__init__.py'],193            }194        }),195    ),196    dict(197        id='utf-8',198        setup_kwargs=dict(199            description='Description accentuée',200        ),201    ),202    dict(203        id='data',204        file_defs={205            'data.txt': DALS(206                """207                Some data...208                """209            ),210        },211        setup_kwargs=dict(212            data_files=[('data_dir', ['data.txt'])],213        ),214        install_tree=flatten_tree({215            'foo-1.0-py{py_version}.egg': {216                'EGG-INFO': ['PKG-INFO', 'RECORD', 'WHEEL', 'top_level.txt'],217                'data_dir': ['data.txt'],218            }219        }),220    ),221    dict(222        id='extension',223        file_defs={224            'extension.c': DALS(225                """226                #include "Python.h"227 228                #if PY_MAJOR_VERSION >= 3229 230                static struct PyModuleDef moduledef = {231                        PyModuleDef_HEAD_INIT,232                        "extension",233                        NULL,234                        0,235                        NULL,236                        NULL,237                        NULL,238                        NULL,239                        NULL240                };241 242                #define INITERROR return NULL243 244                PyMODINIT_FUNC PyInit_extension(void)245 246                #else247 248                #define INITERROR return249 250                void initextension(void)251 252                #endif253                {254                #if PY_MAJOR_VERSION >= 3255                    PyObject *module = PyModule_Create(&moduledef);256                #else257                    PyObject *module = Py_InitModule("extension", NULL);258                #endif259                    if (module == NULL)260                        INITERROR;261                #if PY_MAJOR_VERSION >= 3262                    return module;263                #endif264                }265                """266            ),267        },268        setup_kwargs=dict(269            ext_modules=[270                Record(271                    'setuptools.Extension', name='extension', sources=['extension.c']272                )273            ],274        ),275        install_tree=flatten_tree({276            'foo-1.0-py{py_version}-{platform}.egg': [277                'extension{shlib_ext}',278                {279                    'EGG-INFO': [280                        'PKG-INFO',281                        'RECORD',282                        'WHEEL',283                        'top_level.txt',284                    ]285                },286            ]287        }),288    ),289    dict(290        id='header',291        file_defs={292            'header.h': DALS(293                """294                """295            ),296        },297        setup_kwargs=dict(298            headers=['header.h'],299        ),300        install_tree=flatten_tree({301            'foo-1.0-py{py_version}.egg': [302                'header.h',303                {304                    'EGG-INFO': [305                        'PKG-INFO',306                        'RECORD',307                        'WHEEL',308                        'top_level.txt',309                    ]310                },311            ]312        }),313    ),314    dict(315        id='script',316        file_defs={317            'script.py': DALS(318                """319                #/usr/bin/python320                print('hello world!')321                """322            ),323            'script.sh': DALS(324                """325                #/bin/sh326                echo 'hello world!'327                """328            ),329        },330        setup_kwargs=dict(331            scripts=['script.py', 'script.sh'],332        ),333        install_tree=flatten_tree({334            'foo-1.0-py{py_version}.egg': {335                'EGG-INFO': [336                    'PKG-INFO',337                    'RECORD',338                    'WHEEL',339                    'top_level.txt',340                    {'scripts': ['script.py', 'script.sh']},341                ]342            }343        }),344    ),345    dict(346        id='requires1',347        install_requires='foobar==2.0',348        install_tree=flatten_tree({349            'foo-1.0-py{py_version}.egg': {350                'EGG-INFO': [351                    'PKG-INFO',352                    'RECORD',353                    'WHEEL',354                    'requires.txt',355                    'top_level.txt',356                ]357            }358        }),359        requires_txt=DALS(360            """361            foobar==2.0362            """363        ),364    ),365    dict(366        id='requires2',367        install_requires=f"""368        bar369        foo<=2.0; {sys.platform!r} in sys_platform370        """,371        requires_txt=DALS(372            """373            bar374            foo<=2.0375            """376        ),377    ),378    dict(379        id='requires3',380        install_requires=f"""381        bar; {sys.platform!r} != sys_platform382        """,383    ),384    dict(385        id='requires4',386        install_requires="""387        foo388        """,389        extras_require={390            'extra': 'foobar>3',391        },392        requires_txt=DALS(393            """394            foo395 396            [extra]397            foobar>3398            """399        ),400    ),401    dict(402        id='requires5',403        extras_require={404            'extra': f'foobar; {sys.platform!r} != sys_platform',405        },406        requires_txt='\n'407        + DALS(408            """409            [extra]410            """411        ),412    ),413    dict(414        id='requires_ensure_order',415        install_requires="""416        foo417        bar418        baz419        qux420        """,421        extras_require={422            'extra': """423            foobar>3424            barbaz>4425            bazqux>5426            quxzap>6427            """,428        },429        requires_txt=DALS(430            """431            foo432            bar433            baz434            qux435 436            [extra]437            foobar>3438            barbaz>4439            bazqux>5440            quxzap>6441            """442        ),443    ),444    dict(445        id='namespace_package',446        file_defs={447            'foo': {448                'bar': {'__init__.py': ''},449            },450        },451        setup_kwargs=dict(452            namespace_packages=['foo'],453            packages=['foo.bar'],454        ),455        install_tree=flatten_tree({456            'foo-1.0-py{py_version}.egg': [457                'foo-1.0-py{py_version}-nspkg.pth',458                {459                    'EGG-INFO': [460                        'PKG-INFO',461                        'RECORD',462                        'WHEEL',463                        'namespace_packages.txt',464                        'top_level.txt',465                    ]466                },467                {468                    'foo': [469                        '__init__.py',470                        {'bar': ['__init__.py']},471                    ]472                },473            ]474        }),475    ),476    dict(477        id='empty_namespace_package',478        file_defs={479            'foobar': {480                '__init__.py': (481                    "__import__('pkg_resources').declare_namespace(__name__)"482                )483            },484        },485        setup_kwargs=dict(486            namespace_packages=['foobar'],487            packages=['foobar'],488        ),489        install_tree=flatten_tree({490            'foo-1.0-py{py_version}.egg': [491                'foo-1.0-py{py_version}-nspkg.pth',492                {493                    'EGG-INFO': [494                        'PKG-INFO',495                        'RECORD',496                        'WHEEL',497                        'namespace_packages.txt',498                        'top_level.txt',499                    ]500                },501                {502                    'foobar': [503                        '__init__.py',504                    ]505                },506            ]507        }),508    ),509    dict(510        id='data_in_package',511        file_defs={512            'foo': {513                '__init__.py': '',514                'data_dir': {515                    'data.txt': DALS(516                        """517                        Some data...518                        """519                    ),520                },521            }522        },523        setup_kwargs=dict(524            packages=['foo'],525            data_files=[('foo/data_dir', ['foo/data_dir/data.txt'])],526        ),527        install_tree=flatten_tree({528            'foo-1.0-py{py_version}.egg': {529                'EGG-INFO': [530                    'PKG-INFO',531                    'RECORD',532                    'WHEEL',533                    'top_level.txt',534                ],535                'foo': [536                    '__init__.py',537                    {538                        'data_dir': [539                            'data.txt',540                        ]541                    },542                ],543            }544        }),545    ),546)547 548 549@pytest.mark.parametrize(550    'params',551    WHEEL_INSTALL_TESTS,552    ids=[params['id'] for params in WHEEL_INSTALL_TESTS],553)554def test_wheel_install(params):555    project_name = params.get('name', 'foo')556    version = params.get('version', '1.0')557    install_requires = params.get('install_requires', [])558    extras_require = params.get('extras_require', {})559    requires_txt = params.get('requires_txt', None)560    install_tree = params.get('install_tree')561    file_defs = params.get('file_defs', {})562    setup_kwargs = params.get('setup_kwargs', {})563    with (564        build_wheel(565            name=project_name,566            version=version,567            install_requires=install_requires,568            extras_require=extras_require,569            extra_file_defs=file_defs,570            **setup_kwargs,571        ) as filename,572        tempdir() as install_dir,573    ):574        _check_wheel_install(575            filename, install_dir, install_tree, project_name, version, requires_txt576        )577 578 579def test_wheel_no_dist_dir():580    project_name = 'nodistinfo'581    version = '1.0'582    wheel_name = f'{project_name}-{version}-py2.py3-none-any.whl'583    with tempdir() as source_dir:584        wheel_path = os.path.join(source_dir, wheel_name)585        # create an empty zip file586        zipfile.ZipFile(wheel_path, 'w').close()587        with tempdir() as install_dir:588            with pytest.raises(ValueError):589                _check_wheel_install(590                    wheel_path, install_dir, None, project_name, version, None591                )592 593 594def test_wheel_is_compatible(monkeypatch):595    def sys_tags():596        return {597            (t.interpreter, t.abi, t.platform)598            for t in parse_tag('cp36-cp36m-manylinux1_x86_64')599        }600 601    monkeypatch.setattr('setuptools.wheel._get_supported_tags', sys_tags)602    assert Wheel('onnxruntime-0.1.2-cp36-cp36m-manylinux1_x86_64.whl').is_compatible()603 604 605def test_wheel_mode():606    @contextlib.contextmanager607    def build_wheel(extra_file_defs=None, **kwargs):608        file_defs = {609            'setup.py': (610                DALS(611                    """612                # -*- coding: utf-8 -*-613                from setuptools import setup614                import setuptools615                setup(**%r)616                """617                )618                % kwargs619            ).encode('utf-8'),620        }621        if extra_file_defs:622            file_defs.update(extra_file_defs)623        with tempdir() as source_dir:624            path.build(file_defs, source_dir)625            runsh = pathlib.Path(source_dir) / "script.sh"626            os.chmod(runsh, 0o777)627            subprocess.check_call(628                (sys.executable, 'setup.py', '-q', 'bdist_wheel'), cwd=source_dir629            )630            yield glob.glob(os.path.join(source_dir, 'dist', '*.whl'))[0]631 632    params = dict(633        id='script',634        file_defs={635            'script.py': DALS(636                """637                #/usr/bin/python638                print('hello world!')639                """640            ),641            'script.sh': DALS(642                """643                #/bin/sh644                echo 'hello world!'645                """646            ),647        },648        setup_kwargs=dict(649            scripts=['script.py', 'script.sh'],650        ),651        install_tree=flatten_tree({652            'foo-1.0-py{py_version}.egg': {653                'EGG-INFO': [654                    'PKG-INFO',655                    'RECORD',656                    'WHEEL',657                    'top_level.txt',658                    {'scripts': ['script.py', 'script.sh']},659                ]660            }661        }),662    )663 664    project_name = params.get('name', 'foo')665    version = params.get('version', '1.0')666    install_tree = params.get('install_tree')667    file_defs = params.get('file_defs', {})668    setup_kwargs = params.get('setup_kwargs', {})669 670    with (671        build_wheel(672            name=project_name,673            version=version,674            install_requires=[],675            extras_require={},676            extra_file_defs=file_defs,677            **setup_kwargs,678        ) as filename,679        tempdir() as install_dir,680    ):681        _check_wheel_install(682            filename, install_dir, install_tree, project_name, version, None683        )684        w = Wheel(filename)685        base = pathlib.Path(install_dir) / w.egg_name()686        script_sh = base / "EGG-INFO" / "scripts" / "script.sh"687        assert script_sh.exists()688        if sys.platform != 'win32':689            # Editable file mode has no effect on Windows690            assert oct(stat.S_IMODE(script_sh.stat().st_mode)) == "0o777"691 
codekingpro/portable-devtools · Team Ai