1import sys 2import os 3import distutils.errors 4import platform 5import urllib.request 6import urllib.error 7import http.client 8 9import mock 10import pytest 11 12import setuptools.package_index 13from .textwrap import DALS 14 15 16class TestPackageIndex: 17 def test_regex(self): 18 hash_url = 'http://other_url?:action=show_md5&' 19 hash_url += 'digest=0123456789abcdef0123456789abcdef' 20 doc = """ 21 <a href="http://some_url">Name</a> 22 (<a title="MD5 hash" 23 href="{hash_url}">md5</a>) 24 """.lstrip().format(**locals()) 25 assert setuptools.package_index.PYPI_MD5.match(doc) 26 27 def test_bad_url_bad_port(self): 28 index = setuptools.package_index.PackageIndex() 29 url = 'http://127.0.0.1:0/nonesuch/test_package_index' 30 try: 31 v = index.open_url(url) 32 except Exception as v: 33 assert url in str(v) 34 else: 35 assert isinstance(v, urllib.error.HTTPError) 36 37 def test_bad_url_typo(self): 38 # issue 16 39 # easy_install inquant.contentmirror.plone breaks because of a typo 40 # in its home URL 41 index = setuptools.package_index.PackageIndex( 42 hosts=('www.example.com',) 43 ) 44 45 url = ( 46 'url:%20https://svn.plone.org/svn' 47 '/collective/inquant.contentmirror.plone/trunk' 48 ) 49 try: 50 v = index.open_url(url) 51 except Exception as v: 52 assert url in str(v) 53 else: 54 assert isinstance(v, urllib.error.HTTPError) 55 56 def test_bad_url_bad_status_line(self): 57 index = setuptools.package_index.PackageIndex( 58 hosts=('www.example.com',) 59 ) 60 61 def _urlopen(*args): 62 raise http.client.BadStatusLine('line') 63 64 index.opener = _urlopen 65 url = 'http://example.com' 66 try: 67 index.open_url(url) 68 except Exception as exc: 69 assert 'line' in str(exc) 70 else: 71 raise AssertionError('Should have raise here!') 72 73 def test_bad_url_double_scheme(self): 74 """ 75 A bad URL with a double scheme should raise a DistutilsError. 76 """ 77 index = setuptools.package_index.PackageIndex( 78 hosts=('www.example.com',) 79 ) 80 81 # issue 20 82 url = 'http://http://svn.pythonpaste.org/Paste/wphp/trunk' 83 try: 84 index.open_url(url) 85 except distutils.errors.DistutilsError as error: 86 msg = str(error) 87 assert ( 88 'nonnumeric port' in msg 89 or 'getaddrinfo failed' in msg 90 or 'Name or service not known' in msg 91 ) 92 return 93 raise RuntimeError("Did not raise") 94 95 def test_bad_url_screwy_href(self): 96 index = setuptools.package_index.PackageIndex( 97 hosts=('www.example.com',) 98 ) 99 100 # issue #160 101 if sys.version_info[0] == 2 and sys.version_info[1] == 7: 102 # this should not fail 103 url = 'http://example.com' 104 page = ('<a href="http://www.famfamfam.com](' 105 'http://www.famfamfam.com/">') 106 index.process_index(url, page) 107 108 def test_url_ok(self): 109 index = setuptools.package_index.PackageIndex( 110 hosts=('www.example.com',) 111 ) 112 url = 'file:///tmp/test_package_index' 113 assert index.url_ok(url, True) 114 115 def test_parse_bdist_wininst(self): 116 parse = setuptools.package_index.parse_bdist_wininst 117 118 actual = parse('reportlab-2.5.win32-py2.4.exe') 119 expected = 'reportlab-2.5', '2.4', 'win32' 120 assert actual == expected 121 122 actual = parse('reportlab-2.5.win32.exe') 123 expected = 'reportlab-2.5', None, 'win32' 124 assert actual == expected 125 126 actual = parse('reportlab-2.5.win-amd64-py2.7.exe') 127 expected = 'reportlab-2.5', '2.7', 'win-amd64' 128 assert actual == expected 129 130 actual = parse('reportlab-2.5.win-amd64.exe') 131 expected = 'reportlab-2.5', None, 'win-amd64' 132 assert actual == expected 133 134 def test__vcs_split_rev_from_url(self): 135 """ 136 Test the basic usage of _vcs_split_rev_from_url 137 """ 138 vsrfu = setuptools.package_index.PackageIndex._vcs_split_rev_from_url 139 url, rev = vsrfu('https://example.com/bar@2995') 140 assert url == 'https://example.com/bar' 141 assert rev == '2995' 142 143 def test_local_index(self, tmpdir): 144 """ 145 local_open should be able to read an index from the file system. 146 """ 147 index_file = tmpdir / 'index.html' 148 with index_file.open('w') as f: 149 f.write('<div>content</div>') 150 url = 'file:' + urllib.request.pathname2url(str(tmpdir)) + '/' 151 res = setuptools.package_index.local_open(url) 152 assert 'content' in res.read() 153 154 def test_egg_fragment(self): 155 """ 156 EGG fragments must comply to PEP 440 157 """ 158 epoch = [ 159 '', 160 '1!', 161 ] 162 releases = [ 163 '0', 164 '0.0', 165 '0.0.0', 166 ] 167 pre = [ 168 'a0', 169 'b0', 170 'rc0', 171 ] 172 post = [ 173 '.post0' 174 ] 175 dev = [ 176 '.dev0', 177 ] 178 local = [ 179 ('', ''), 180 ('+ubuntu.0', '+ubuntu.0'), 181 ('+ubuntu-0', '+ubuntu.0'), 182 ('+ubuntu_0', '+ubuntu.0'), 183 ] 184 versions = [ 185 [''.join([e, r, p, loc]) for loc in locs] 186 for e in epoch 187 for r in releases 188 for p in sum([pre, post, dev], ['']) 189 for locs in local] 190 for v, vc in versions: 191 dists = list(setuptools.package_index.distros_for_url( 192 'http://example.com/example.zip#egg=example-' + v)) 193 assert dists[0].version == '' 194 assert dists[1].version == vc 195 196 def test_download_git_with_rev(self, tmpdir): 197 url = 'git+https://github.example/group/project@master#egg=foo' 198 index = setuptools.package_index.PackageIndex() 199 200 with mock.patch("os.system") as os_system_mock: 201 result = index.download(url, str(tmpdir)) 202 203 os_system_mock.assert_called() 204 205 expected_dir = str(tmpdir / 'project@master') 206 expected = ( 207 'git clone --quiet ' 208 'https://github.example/group/project {expected_dir}' 209 ).format(**locals()) 210 first_call_args = os_system_mock.call_args_list[0][0] 211 assert first_call_args == (expected,) 212 213 tmpl = 'git -C {expected_dir} checkout --quiet master' 214 expected = tmpl.format(**locals()) 215 assert os_system_mock.call_args_list[1][0] == (expected,) 216 assert result == expected_dir 217 218 def test_download_git_no_rev(self, tmpdir): 219 url = 'git+https://github.example/group/project#egg=foo' 220 index = setuptools.package_index.PackageIndex() 221 222 with mock.patch("os.system") as os_system_mock: 223 result = index.download(url, str(tmpdir)) 224 225 os_system_mock.assert_called() 226 227 expected_dir = str(tmpdir / 'project') 228 expected = ( 229 'git clone --quiet ' 230 'https://github.example/group/project {expected_dir}' 231 ).format(**locals()) 232 os_system_mock.assert_called_once_with(expected) 233 234 def test_download_svn(self, tmpdir): 235 url = 'svn+https://svn.example/project#egg=foo' 236 index = setuptools.package_index.PackageIndex() 237 238 with pytest.warns(UserWarning): 239 with mock.patch("os.system") as os_system_mock: 240 result = index.download(url, str(tmpdir)) 241 242 os_system_mock.assert_called() 243 244 expected_dir = str(tmpdir / 'project') 245 expected = ( 246 'svn checkout -q ' 247 'svn+https://svn.example/project {expected_dir}' 248 ).format(**locals()) 249 os_system_mock.assert_called_once_with(expected) 250 251 252class TestContentCheckers: 253 def test_md5(self): 254 checker = setuptools.package_index.HashChecker.from_url( 255 'http://foo/bar#md5=f12895fdffbd45007040d2e44df98478') 256 checker.feed('You should probably not be using MD5'.encode('ascii')) 257 assert checker.hash.hexdigest() == 'f12895fdffbd45007040d2e44df98478' 258 assert checker.is_valid() 259 260 def test_other_fragment(self): 261 "Content checks should succeed silently if no hash is present" 262 checker = setuptools.package_index.HashChecker.from_url( 263 'http://foo/bar#something%20completely%20different') 264 checker.feed('anything'.encode('ascii')) 265 assert checker.is_valid() 266 267 def test_blank_md5(self): 268 "Content checks should succeed if a hash is empty" 269 checker = setuptools.package_index.HashChecker.from_url( 270 'http://foo/bar#md5=') 271 checker.feed('anything'.encode('ascii')) 272 assert checker.is_valid() 273 274 def test_get_hash_name_md5(self): 275 checker = setuptools.package_index.HashChecker.from_url( 276 'http://foo/bar#md5=f12895fdffbd45007040d2e44df98478') 277 assert checker.hash_name == 'md5' 278 279 def test_report(self): 280 checker = setuptools.package_index.HashChecker.from_url( 281 'http://foo/bar#md5=f12895fdffbd45007040d2e44df98478') 282 rep = checker.report(lambda x: x, 'My message about %s') 283 assert rep == 'My message about md5' 284 285 286@pytest.fixture 287def temp_home(tmpdir, monkeypatch): 288 key = ( 289 'USERPROFILE' 290 if platform.system() == 'Windows' and sys.version_info > (3, 8) else 291 'HOME' 292 ) 293 294 monkeypatch.setitem(os.environ, key, str(tmpdir)) 295 return tmpdir 296 297 298class TestPyPIConfig: 299 def test_percent_in_password(self, temp_home): 300 pypirc = temp_home / '.pypirc' 301 pypirc.write(DALS(""" 302 [pypi] 303 repository=https://pypi.org 304 username=jaraco 305 password=pity% 306 """)) 307 cfg = setuptools.package_index.PyPIConfig() 308 cred = cfg.creds_by_repository['https://pypi.org'] 309 assert cred.username == 'jaraco' 310 assert cred.password == 'pity%' 311