codekingpro/portable-devtools
114k
1import os2import re3import urllib.parse4import urllib.request5 6import pytest7 8from setuptools import Distribution9from setuptools.dist import check_package_data, check_specifier10 11from .fixtures import make_trivial_sdist12from .test_find_packages import ensure_files13from .textwrap import DALS14 15from distutils.errors import DistutilsSetupError16 17 18def test_dist_fetch_build_egg(tmpdir, setuptools_wheel):19 """20 Check multiple calls to `Distribution.fetch_build_egg` work as expected.21 """22 index = tmpdir.mkdir('index')23 index_url = urllib.parse.urljoin('file://', urllib.request.pathname2url(str(index)))24 25 def sdist_with_index(distname, version):26 dist_dir = index.mkdir(distname)27 dist_sdist = f'{distname}-{version}.tar.gz'28 make_trivial_sdist(29 str(dist_dir.join(dist_sdist)), distname, version, setuptools_wheel30 )31 with dist_dir.join('index.html').open('w') as fp:32 fp.write(33 DALS(34 """35 <!DOCTYPE html><html><body>36 <a href="{dist_sdist}" rel="internal">{dist_sdist}</a><br/>37 </body></html>38 """39 ).format(dist_sdist=dist_sdist)40 )41 42 sdist_with_index('barbazquux', '3.2.0')43 sdist_with_index('barbazquux-runner', '2.11.1')44 with tmpdir.join('setup.cfg').open('w') as fp:45 fp.write(46 DALS(47 """48 [easy_install]49 index_url = {index_url}50 """51 ).format(index_url=index_url)52 )53 reqs = """54 barbazquux-runner55 barbazquux56 """.split()57 with tmpdir.as_cwd():58 dist = Distribution()59 dist.parse_config_files()60 resolved_dists = [dist.fetch_build_egg(r) for r in reqs]61 assert [dist.name for dist in resolved_dists if dist] == reqs62 63 64EXAMPLE_BASE_INFO = dict(65 name="package",66 version="0.0.1",67 author="Foo Bar",68 author_email="foo@bar.net",69 long_description="Long\ndescription",70 description="Short description",71 keywords=["one", "two"],72)73 74 75def test_provides_extras_deterministic_order():76 attrs = dict(extras_require=dict(a=['foo'], b=['bar']))77 dist = Distribution(attrs)78 assert list(dist.metadata.provides_extras) == ['a', 'b']79 attrs['extras_require'] = dict(reversed(attrs['extras_require'].items()))80 dist = Distribution(attrs)81 assert list(dist.metadata.provides_extras) == ['b', 'a']82 83 84CHECK_PACKAGE_DATA_TESTS = (85 # Valid.86 (87 {88 '': ['*.txt', '*.rst'],89 'hello': ['*.msg'],90 },91 None,92 ),93 # Not a dictionary.94 (95 (96 ('', ['*.txt', '*.rst']),97 ('hello', ['*.msg']),98 ),99 (100 "'package_data' must be a dictionary mapping package"101 " names to lists of string wildcard patterns"102 ),103 ),104 # Invalid key type.105 (106 {107 400: ['*.txt', '*.rst'],108 },109 ("keys of 'package_data' dict must be strings (got 400)"),110 ),111 # Invalid value type.112 (113 {114 'hello': '*.msg',115 },116 (117 "\"values of 'package_data' dict\" must be of type <tuple[str, ...] | list[str]>"118 " (got '*.msg')"119 ),120 ),121 # Invalid value type (generators are single use)122 (123 {124 'hello': (x for x in "generator"),125 },126 (127 "\"values of 'package_data' dict\" must be of type <tuple[str, ...] | list[str]>"128 " (got <generator object"129 ),130 ),131)132 133 134@pytest.mark.parametrize(('package_data', 'expected_message'), CHECK_PACKAGE_DATA_TESTS)135def test_check_package_data(package_data, expected_message):136 if expected_message is None:137 assert check_package_data(None, 'package_data', package_data) is None138 else:139 with pytest.raises(DistutilsSetupError, match=re.escape(expected_message)):140 check_package_data(None, 'package_data', package_data)141 142 143def test_check_specifier():144 # valid specifier value145 attrs = {'name': 'foo', 'python_requires': '>=3.0, !=3.1'}146 dist = Distribution(attrs)147 check_specifier(dist, attrs, attrs['python_requires'])148 149 attrs = {'name': 'foo', 'python_requires': ['>=3.0', '!=3.1']}150 dist = Distribution(attrs)151 check_specifier(dist, attrs, attrs['python_requires'])152 153 # invalid specifier value154 attrs = {'name': 'foo', 'python_requires': '>=invalid-version'}155 with pytest.raises(DistutilsSetupError):156 dist = Distribution(attrs)157 158 159def test_metadata_name():160 with pytest.raises(DistutilsSetupError, match='missing.*name'):161 Distribution()._validate_metadata()162 163 164@pytest.mark.parametrize(165 ('dist_name', 'py_module'),166 [167 ("my.pkg", "my_pkg"),168 ("my-pkg", "my_pkg"),169 ("my_pkg", "my_pkg"),170 ("pkg", "pkg"),171 ],172)173def test_dist_default_py_modules(tmp_path, dist_name, py_module):174 (tmp_path / f"{py_module}.py").touch()175 176 (tmp_path / "setup.py").touch()177 (tmp_path / "noxfile.py").touch()178 # ^-- make sure common tool files are ignored179 180 attrs = {**EXAMPLE_BASE_INFO, "name": dist_name, "src_root": str(tmp_path)}181 # Find `py_modules` corresponding to dist_name if not given182 dist = Distribution(attrs)183 dist.set_defaults()184 assert dist.py_modules == [py_module]185 # When `py_modules` is given, don't do anything186 dist = Distribution({**attrs, "py_modules": ["explicity_py_module"]})187 dist.set_defaults()188 assert dist.py_modules == ["explicity_py_module"]189 # When `packages` is given, don't do anything190 dist = Distribution({**attrs, "packages": ["explicity_package"]})191 dist.set_defaults()192 assert not dist.py_modules193 194 195@pytest.mark.parametrize(196 ('dist_name', 'package_dir', 'package_files', 'packages'),197 [198 ("my.pkg", None, ["my_pkg/__init__.py", "my_pkg/mod.py"], ["my_pkg"]),199 ("my-pkg", None, ["my_pkg/__init__.py", "my_pkg/mod.py"], ["my_pkg"]),200 ("my_pkg", None, ["my_pkg/__init__.py", "my_pkg/mod.py"], ["my_pkg"]),201 ("my.pkg", None, ["my/pkg/__init__.py"], ["my", "my.pkg"]),202 (203 "my_pkg",204 None,205 ["src/my_pkg/__init__.py", "src/my_pkg2/__init__.py"],206 ["my_pkg", "my_pkg2"],207 ),208 (209 "my_pkg",210 {"pkg": "lib", "pkg2": "lib2"},211 ["lib/__init__.py", "lib/nested/__init__.pyt", "lib2/__init__.py"],212 ["pkg", "pkg.nested", "pkg2"],213 ),214 ],215)216def test_dist_default_packages(217 tmp_path, dist_name, package_dir, package_files, packages218):219 ensure_files(tmp_path, package_files)220 221 (tmp_path / "setup.py").touch()222 (tmp_path / "noxfile.py").touch()223 # ^-- should not be included by default224 225 attrs = {226 **EXAMPLE_BASE_INFO,227 "name": dist_name,228 "src_root": str(tmp_path),229 "package_dir": package_dir,230 }231 # Find `packages` either corresponding to dist_name or inside src232 dist = Distribution(attrs)233 dist.set_defaults()234 assert not dist.py_modules235 assert not dist.py_modules236 assert set(dist.packages) == set(packages)237 # When `py_modules` is given, don't do anything238 dist = Distribution({**attrs, "py_modules": ["explicit_py_module"]})239 dist.set_defaults()240 assert not dist.packages241 assert set(dist.py_modules) == {"explicit_py_module"}242 # When `packages` is given, don't do anything243 dist = Distribution({**attrs, "packages": ["explicit_package"]})244 dist.set_defaults()245 assert not dist.py_modules246 assert set(dist.packages) == {"explicit_package"}247 248 249@pytest.mark.parametrize(250 ('dist_name', 'package_dir', 'package_files'),251 [252 ("my.pkg.nested", None, ["my/pkg/nested/__init__.py"]),253 ("my.pkg", None, ["my/pkg/__init__.py", "my/pkg/file.py"]),254 ("my_pkg", None, ["my_pkg.py"]),255 ("my_pkg", None, ["my_pkg/__init__.py", "my_pkg/nested/__init__.py"]),256 ("my_pkg", None, ["src/my_pkg/__init__.py", "src/my_pkg/nested/__init__.py"]),257 (258 "my_pkg",259 {"my_pkg": "lib", "my_pkg.lib2": "lib2"},260 ["lib/__init__.py", "lib/nested/__init__.pyt", "lib2/__init__.py"],261 ),262 # Should not try to guess a name from multiple py_modules/packages263 ("UNKNOWN", None, ["src/mod1.py", "src/mod2.py"]),264 ("UNKNOWN", None, ["src/pkg1/__ini__.py", "src/pkg2/__init__.py"]),265 ],266)267def test_dist_default_name(tmp_path, dist_name, package_dir, package_files):268 """Make sure dist.name is discovered from packages/py_modules"""269 ensure_files(tmp_path, package_files)270 attrs = {271 **EXAMPLE_BASE_INFO,272 "src_root": "/".join(os.path.split(tmp_path)), # POSIX-style273 "package_dir": package_dir,274 }275 del attrs["name"]276 277 dist = Distribution(attrs)278 dist.set_defaults()279 assert dist.py_modules or dist.packages280 assert dist.get_name() == dist_name281 