1import os
2import sys
3import subprocess
4from urllib.request import urlopen
5from urllib.error import URLError
6
7import pathlib
8
9import pytest
10
11from . import contexts
12from .textwrap import DALS
13from .test_easy_install import make_nspkg_sdist
14
15
16@pytest.fixture(autouse=True)
17def pytest_virtualenv_works(venv):
18    """
19    pytest_virtualenv may not work. if it doesn't, skip these
20    tests. See #1284.
21    """
22    venv_prefix = venv.run(["python" , "-c", "import sys; print(sys.prefix)"]).strip()
23    if venv_prefix == sys.prefix:
24        pytest.skip("virtualenv is broken (see pypa/setuptools#1284)")
25
26
27def test_clean_env_install(venv_without_setuptools, setuptools_wheel):
28    """
29    Check setuptools can be installed in a clean environment.
30    """
31    cmd = ["python", "-m", "pip", "install", str(setuptools_wheel)]
32    venv_without_setuptools.run(cmd)
33
34
35def access_pypi():
36    # Detect if tests are being run without connectivity
37    if not os.environ.get('NETWORK_REQUIRED', False):  # pragma: nocover
38        try:
39            urlopen('https://pypi.org', timeout=1)
40        except URLError:
41            # No network, disable most of these tests
42            return False
43
44    return True
45
46
47@pytest.mark.skipif(
48    'platform.python_implementation() == "PyPy"',
49    reason="https://github.com/pypa/setuptools/pull/2865#issuecomment-965834995",
50)
51@pytest.mark.skipif(not access_pypi(), reason="no network")
52# ^-- Even when it is not necessary to install a different version of `pip`
53#     the build process will still try to download `wheel`, see #3147 and #2986.
54@pytest.mark.parametrize(
55    'pip_version',
56    [
57        None,
58        pytest.param('pip<20', marks=pytest.mark.xfail(reason='pypa/pip#6599')),
59        'pip<20.1',
60        'pip<21',
61        'pip<22',
62        pytest.param(
63            'https://github.com/pypa/pip/archive/main.zip',
64            marks=pytest.mark.xfail(reason='#2975'),
65        ),
66    ]
67)
68def test_pip_upgrade_from_source(pip_version, venv_without_setuptools,
69                                 setuptools_wheel, setuptools_sdist):
70    """
71    Check pip can upgrade setuptools from source.
72    """
73    # Install pip/wheel, in a venv without setuptools (as it
74    # should not be needed for bootstraping from source)
75    venv = venv_without_setuptools
76    venv.run(["pip", "install", "-U", "wheel"])
77    if pip_version is not None:
78        venv.run(["python", "-m", "pip", "install", "-U", pip_version, "--retries=1"])
79    with pytest.raises(subprocess.CalledProcessError):
80        # Meta-test to make sure setuptools is not installed
81        venv.run(["python", "-c", "import setuptools"])
82
83    # Then install from wheel.
84    venv.run(["pip", "install", str(setuptools_wheel)])
85    # And finally try to upgrade from source.
86    venv.run(["pip", "install", "--no-cache-dir", "--upgrade", str(setuptools_sdist)])
87
88
89def _check_test_command_install_requirements(venv, tmpdir):
90    """
91    Check the test command will install all required dependencies.
92    """
93    def sdist(distname, version):
94        dist_path = tmpdir.join('%s-%s.tar.gz' % (distname, version))
95        make_nspkg_sdist(str(dist_path), distname, version)
96        return dist_path
97    dependency_links = [
98        pathlib.Path(str(dist_path)).as_uri()
99        for dist_path in (
100            sdist('foobar', '2.4'),
101            sdist('bits', '4.2'),
102            sdist('bobs', '6.0'),
103            sdist('pieces', '0.6'),
104        )
105    ]
106    with tmpdir.join('setup.py').open('w') as fp:
107        fp.write(DALS(
108            '''
109            from setuptools import setup
110
111            setup(
112                dependency_links={dependency_links!r},
113                install_requires=[
114                    'barbazquux1; sys_platform in ""',
115                    'foobar==2.4',
116                ],
117                setup_requires='bits==4.2',
118                tests_require="""
119                    bobs==6.0
120                """,
121                extras_require={{
122                    'test': ['barbazquux2'],
123                    ':"" in sys_platform': 'pieces==0.6',
124                    ':python_version > "1"': """
125                        pieces
126                        foobar
127                    """,
128                }}
129            )
130            '''.format(dependency_links=dependency_links)))
131    with tmpdir.join('test.py').open('w') as fp:
132        fp.write(DALS(
133            '''
134            import foobar
135            import bits
136            import bobs
137            import pieces
138
139            open('success', 'w').close()
140            '''))
141
142    cmd = ["python", 'setup.py', 'test', '-s', 'test']
143    venv.run(cmd, cwd=str(tmpdir))
144    assert tmpdir.join('success').check()
145
146
147def test_test_command_install_requirements(venv, tmpdir, tmpdir_cwd):
148    # Ensure pip/wheel packages are installed.
149    venv.run(["python", "-c", "__import__('pkg_resources').require(['pip', 'wheel'])"])
150    # disable index URL so bits and bobs aren't requested from PyPI
151    with contexts.environment(PYTHONPATH=None, PIP_NO_INDEX="1"):
152        _check_test_command_install_requirements(venv, tmpdir)
153
154
155def test_no_missing_dependencies(bare_venv, request):
156    """
157    Quick and dirty test to ensure all external dependencies are vendored.
158    """
159    setuptools_dir = request.config.rootdir
160    for command in ('upload',):  # sorted(distutils.command.__all__):
161        bare_venv.run(['python', 'setup.py', command, '-h'], cwd=setuptools_dir)
162