1import os 2import sys 3import functools 4import platform 5import textwrap 6 7import pytest 8 9 10IS_PYPY = '__pypy__' in sys.builtin_module_names 11 12 13def popen_text(call): 14 """ 15 Augment the Popen call with the parameters to ensure unicode text. 16 """ 17 return functools.partial(call, universal_newlines=True) \ 18 if sys.version_info < (3, 7) else functools.partial(call, text=True) 19 20 21def win_sr(env): 22 """ 23 On Windows, SYSTEMROOT must be present to avoid 24 25 > Fatal Python error: _Py_HashRandomization_Init: failed to 26 > get random numbers to initialize Python 27 """ 28 if env is None: 29 return 30 if platform.system() == 'Windows': 31 env['SYSTEMROOT'] = os.environ['SYSTEMROOT'] 32 return env 33 34 35def find_distutils(venv, imports='distutils', env=None, **kwargs): 36 py_cmd = 'import {imports}; print(distutils.__file__)'.format(**locals()) 37 cmd = ['python', '-c', py_cmd] 38 return popen_text(venv.run)(cmd, env=win_sr(env), **kwargs) 39 40 41def count_meta_path(venv, env=None): 42 py_cmd = textwrap.dedent( 43 """ 44 import sys 45 is_distutils = lambda finder: finder.__class__.__name__ == "DistutilsMetaFinder" 46 print(len(list(filter(is_distutils, sys.meta_path)))) 47 """) 48 cmd = ['python', '-c', py_cmd] 49 return int(popen_text(venv.run)(cmd, env=win_sr(env))) 50 51 52def test_distutils_stdlib(venv): 53 """ 54 Ensure stdlib distutils is used when appropriate. 55 """ 56 env = dict(SETUPTOOLS_USE_DISTUTILS='stdlib') 57 assert venv.name not in find_distutils(venv, env=env).split(os.sep) 58 assert count_meta_path(venv, env=env) == 0 59 60 61def test_distutils_local_with_setuptools(venv): 62 """ 63 Ensure local distutils is used when appropriate. 64 """ 65 env = dict(SETUPTOOLS_USE_DISTUTILS='local') 66 loc = find_distutils(venv, imports='setuptools, distutils', env=env) 67 assert venv.name in loc.split(os.sep) 68 assert count_meta_path(venv, env=env) <= 1 69 70 71@pytest.mark.xfail('IS_PYPY', reason='pypy imports distutils on startup') 72def test_distutils_local(venv): 73 """ 74 Even without importing, the setuptools-local copy of distutils is 75 preferred. 76 """ 77 env = dict(SETUPTOOLS_USE_DISTUTILS='local') 78 assert venv.name in find_distutils(venv, env=env).split(os.sep) 79 assert count_meta_path(venv, env=env) <= 1 80 81 82def test_pip_import(venv): 83 """ 84 Ensure pip can be imported. 85 Regression test for #3002. 86 """ 87 cmd = ['python', '-c', 'import pip'] 88 popen_text(venv.run)(cmd) 89 90 91def test_distutils_has_origin(): 92 """ 93 Distutils module spec should have an origin. #2990. 94 """ 95 assert __import__('distutils').__spec__.origin 96 97 98ENSURE_IMPORTS_ARE_NOT_DUPLICATED = r""" 99# Depending on the importlib machinery and _distutils_hack, some imports are 100# duplicated resulting in different module objects being loaded, which prevents 101# patches as shown in #3042. 102# This script provides a way of verifying if this duplication is happening. 103 104from distutils import cmd 105import distutils.command.sdist as sdist 106 107# import last to prevent caching 108from distutils import {imported_module} 109 110for mod in (cmd, sdist): 111 assert mod.{imported_module} == {imported_module}, ( 112 f"\n{{mod.dir_util}}\n!=\n{{{imported_module}}}" 113 ) 114 115print("success") 116""" 117 118 119@pytest.mark.parametrize( 120 "distutils_version, imported_module", 121 [ 122 ("stdlib", "dir_util"), 123 ("stdlib", "file_util"), 124 ("stdlib", "archive_util"), 125 ("local", "dir_util"), 126 ("local", "file_util"), 127 ("local", "archive_util"), 128 ] 129) 130def test_modules_are_not_duplicated_on_import( 131 distutils_version, imported_module, tmpdir_cwd, venv 132): 133 env = dict(SETUPTOOLS_USE_DISTUTILS=distutils_version) 134 script = ENSURE_IMPORTS_ARE_NOT_DUPLICATED.format(imported_module=imported_module) 135 cmd = ['python', '-c', script] 136 output = popen_text(venv.run)(cmd, env=win_sr(env)).strip() 137 assert output == "success" 138 139 140ENSURE_LOG_IMPORT_IS_NOT_DUPLICATED = r""" 141# Similar to ENSURE_IMPORTS_ARE_NOT_DUPLICATED 142import distutils.dist as dist 143from distutils import log 144 145assert dist.log == log, ( 146 f"\n{dist.log}\n!=\n{log}" 147) 148 149print("success") 150""" 151 152 153@pytest.mark.parametrize("distutils_version", "local stdlib".split()) 154def test_log_module_is_not_duplicated_on_import(distutils_version, tmpdir_cwd, venv): 155 env = dict(SETUPTOOLS_USE_DISTUTILS=distutils_version) 156 cmd = ['python', '-c', ENSURE_LOG_IMPORT_IS_NOT_DUPLICATED] 157 output = popen_text(venv.run)(cmd, env=win_sr(env)).strip() 158 assert output == "success" 159