1"""develop tests 2""" 3 4import os 5import sys 6import subprocess 7import platform 8import pathlib 9 10from setuptools.command import test 11 12import pytest 13import pip_run.launch 14 15from setuptools.command.develop import develop 16from setuptools.dist import Distribution 17from . import contexts 18from . import namespaces 19 20SETUP_PY = """\ 21from setuptools import setup 22 23setup(name='foo', 24 packages=['foo'], 25) 26""" 27 28INIT_PY = """print "foo" 29""" 30 31 32@pytest.fixture 33def temp_user(monkeypatch): 34 with contexts.tempdir() as user_base: 35 with contexts.tempdir() as user_site: 36 monkeypatch.setattr('site.USER_BASE', user_base) 37 monkeypatch.setattr('site.USER_SITE', user_site) 38 yield 39 40 41@pytest.fixture 42def test_env(tmpdir, temp_user): 43 target = tmpdir 44 foo = target.mkdir('foo') 45 setup = target / 'setup.py' 46 if setup.isfile(): 47 raise ValueError(dir(target)) 48 with setup.open('w') as f: 49 f.write(SETUP_PY) 50 init = foo / '__init__.py' 51 with init.open('w') as f: 52 f.write(INIT_PY) 53 with target.as_cwd(): 54 yield target 55 56 57class TestDevelop: 58 in_virtualenv = hasattr(sys, 'real_prefix') 59 in_venv = hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix 60 61 def test_console_scripts(self, tmpdir): 62 """ 63 Test that console scripts are installed and that they reference 64 only the project by name and not the current version. 65 """ 66 pytest.skip( 67 "TODO: needs a fixture to cause 'develop' " 68 "to be invoked without mutating environment." 69 ) 70 settings = dict( 71 name='foo', 72 packages=['foo'], 73 version='0.0', 74 entry_points={ 75 'console_scripts': [ 76 'foocmd = foo:foo', 77 ], 78 }, 79 ) 80 dist = Distribution(settings) 81 dist.script_name = 'setup.py' 82 cmd = develop(dist) 83 cmd.ensure_finalized() 84 cmd.install_dir = tmpdir 85 cmd.run() 86 # assert '0.0' not in foocmd_text 87 88 89class TestResolver: 90 """ 91 TODO: These tests were written with a minimal understanding 92 of what _resolve_setup_path is intending to do. Come up with 93 more meaningful cases that look like real-world scenarios. 94 """ 95 96 def test_resolve_setup_path_cwd(self): 97 assert develop._resolve_setup_path('.', '.', '.') == '.' 98 99 def test_resolve_setup_path_one_dir(self): 100 assert develop._resolve_setup_path('pkgs', '.', 'pkgs') == '../' 101 102 def test_resolve_setup_path_one_dir_trailing_slash(self): 103 assert develop._resolve_setup_path('pkgs/', '.', 'pkgs') == '../' 104 105 106class TestNamespaces: 107 @staticmethod 108 def install_develop(src_dir, target): 109 110 develop_cmd = [ 111 sys.executable, 112 'setup.py', 113 'develop', 114 '--install-dir', 115 str(target), 116 ] 117 with src_dir.as_cwd(): 118 with test.test.paths_on_pythonpath([str(target)]): 119 subprocess.check_call(develop_cmd) 120 121 @pytest.mark.skipif( 122 bool(os.environ.get("APPVEYOR")), 123 reason="https://github.com/pypa/setuptools/issues/851", 124 ) 125 @pytest.mark.skipif( 126 platform.python_implementation() == 'PyPy', 127 reason="https://github.com/pypa/setuptools/issues/1202", 128 ) 129 def test_namespace_package_importable(self, tmpdir): 130 """ 131 Installing two packages sharing the same namespace, one installed 132 naturally using pip or `--single-version-externally-managed` 133 and the other installed using `develop` should leave the namespace 134 in tact and both packages reachable by import. 135 """ 136 pkg_A = namespaces.build_namespace_package(tmpdir, 'myns.pkgA') 137 pkg_B = namespaces.build_namespace_package(tmpdir, 'myns.pkgB') 138 target = tmpdir / 'packages' 139 # use pip to install to the target directory 140 install_cmd = [ 141 sys.executable, 142 '-m', 143 'pip', 144 'install', 145 str(pkg_A), 146 '-t', 147 str(target), 148 ] 149 subprocess.check_call(install_cmd) 150 self.install_develop(pkg_B, target) 151 namespaces.make_site_dir(target) 152 try_import = [ 153 sys.executable, 154 '-c', 155 'import myns.pkgA; import myns.pkgB', 156 ] 157 with test.test.paths_on_pythonpath([str(target)]): 158 subprocess.check_call(try_import) 159 160 # additionally ensure that pkg_resources import works 161 pkg_resources_imp = [ 162 sys.executable, 163 '-c', 164 'import pkg_resources', 165 ] 166 with test.test.paths_on_pythonpath([str(target)]): 167 subprocess.check_call(pkg_resources_imp) 168 169 @pytest.mark.xfail( 170 platform.python_implementation() == 'PyPy', 171 reason="Workaround fails on PyPy (why?)", 172 ) 173 def test_editable_prefix(self, tmp_path, sample_project): 174 """ 175 Editable install to a prefix should be discoverable. 176 """ 177 prefix = tmp_path / 'prefix' 178 179 # figure out where pip will likely install the package 180 site_packages = prefix / next( 181 pathlib.Path(path).relative_to(sys.prefix) 182 for path in sys.path 183 if 'site-packages' in path and path.startswith(sys.prefix) 184 ) 185 site_packages.mkdir(parents=True) 186 187 # install workaround 188 pip_run.launch.inject_sitecustomize(str(site_packages)) 189 190 env = dict(os.environ, PYTHONPATH=str(site_packages)) 191 cmd = [ 192 sys.executable, 193 '-m', 194 'pip', 195 'install', 196 '--editable', 197 str(sample_project), 198 '--prefix', 199 str(prefix), 200 '--no-build-isolation', 201 ] 202 subprocess.check_call(cmd, env=env) 203 204 # now run 'sample' with the prefix on the PYTHONPATH 205 bin = 'Scripts' if platform.system() == 'Windows' else 'bin' 206 exe = prefix / bin / 'sample' 207 if sys.version_info < (3, 8) and platform.system() == 'Windows': 208 exe = str(exe) 209 subprocess.check_call([exe], env=env) 210