1from __future__ import absolute_import 2 3import sys 4import os 5import distutils.errors 6 7from setuptools.extern import six 8from setuptools.extern.six.moves import urllib, http_client 9 10import pkg_resources 11import setuptools.package_index 12from setuptools.tests.server import IndexServer 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 = 'url:%20https://svn.plone.org/svn/collective/inquant.contentmirror.plone/trunk' 46 try: 47 v = index.open_url(url) 48 except Exception as v: 49 assert url in str(v) 50 else: 51 assert isinstance(v, urllib.error.HTTPError) 52 53 def test_bad_url_bad_status_line(self): 54 index = setuptools.package_index.PackageIndex( 55 hosts=('www.example.com',) 56 ) 57 58 def _urlopen(*args): 59 raise http_client.BadStatusLine('line') 60 61 index.opener = _urlopen 62 url = 'http://example.com' 63 try: 64 v = index.open_url(url) 65 except Exception as v: 66 assert 'line' in str(v) 67 else: 68 raise AssertionError('Should have raise here!') 69 70 def test_bad_url_double_scheme(self): 71 """ 72 A bad URL with a double scheme should raise a DistutilsError. 73 """ 74 index = setuptools.package_index.PackageIndex( 75 hosts=('www.example.com',) 76 ) 77 78 # issue 20 79 url = 'http://http://svn.pythonpaste.org/Paste/wphp/trunk' 80 try: 81 index.open_url(url) 82 except distutils.errors.DistutilsError as error: 83 msg = six.text_type(error) 84 assert 'nonnumeric port' in msg or 'getaddrinfo failed' in msg or 'Name or service not known' in msg 85 return 86 raise RuntimeError("Did not raise") 87 88 def test_bad_url_screwy_href(self): 89 index = setuptools.package_index.PackageIndex( 90 hosts=('www.example.com',) 91 ) 92 93 # issue #160 94 if sys.version_info[0] == 2 and sys.version_info[1] == 7: 95 # this should not fail 96 url = 'http://example.com' 97 page = ('<a href="http://www.famfamfam.com](' 98 'http://www.famfamfam.com/">') 99 index.process_index(url, page) 100 101 def test_url_ok(self): 102 index = setuptools.package_index.PackageIndex( 103 hosts=('www.example.com',) 104 ) 105 url = 'file:///tmp/test_package_index' 106 assert index.url_ok(url, True) 107 108 def test_links_priority(self): 109 """ 110 Download links from the pypi simple index should be used before 111 external download links. 112 https://bitbucket.org/tarek/distribute/issue/163 113 114 Usecase : 115 - someone uploads a package on pypi, a md5 is generated 116 - someone manually copies this link (with the md5 in the url) onto an 117 external page accessible from the package page. 118 - someone reuploads the package (with a different md5) 119 - while easy_installing, an MD5 error occurs because the external link 120 is used 121 -> Setuptools should use the link from pypi, not the external one. 122 """ 123 if sys.platform.startswith('java'): 124 # Skip this test on jython because binding to :0 fails 125 return 126 127 # start an index server 128 server = IndexServer() 129 server.start() 130 index_url = server.base_url() + 'test_links_priority/simple/' 131 132 # scan a test index 133 pi = setuptools.package_index.PackageIndex(index_url) 134 requirement = pkg_resources.Requirement.parse('foobar') 135 pi.find_packages(requirement) 136 server.stop() 137 138 # the distribution has been found 139 assert 'foobar' in pi 140 # we have only one link, because links are compared without md5 141 assert len(pi['foobar']) == 1 142 # the link should be from the index 143 assert 'correct_md5' in pi['foobar'][0].location 144 145 def test_parse_bdist_wininst(self): 146 parse = setuptools.package_index.parse_bdist_wininst 147 148 actual = parse('reportlab-2.5.win32-py2.4.exe') 149 expected = 'reportlab-2.5', '2.4', 'win32' 150 assert actual == expected 151 152 actual = parse('reportlab-2.5.win32.exe') 153 expected = 'reportlab-2.5', None, 'win32' 154 assert actual == expected 155 156 actual = parse('reportlab-2.5.win-amd64-py2.7.exe') 157 expected = 'reportlab-2.5', '2.7', 'win-amd64' 158 assert actual == expected 159 160 actual = parse('reportlab-2.5.win-amd64.exe') 161 expected = 'reportlab-2.5', None, 'win-amd64' 162 assert actual == expected 163 164 def test__vcs_split_rev_from_url(self): 165 """ 166 Test the basic usage of _vcs_split_rev_from_url 167 """ 168 vsrfu = setuptools.package_index.PackageIndex._vcs_split_rev_from_url 169 url, rev = vsrfu('https://example.com/bar@2995') 170 assert url == 'https://example.com/bar' 171 assert rev == '2995' 172 173 def test_local_index(self, tmpdir): 174 """ 175 local_open should be able to read an index from the file system. 176 """ 177 index_file = tmpdir / 'index.html' 178 with index_file.open('w') as f: 179 f.write('<div>content</div>') 180 url = 'file:' + urllib.request.pathname2url(str(tmpdir)) + '/' 181 res = setuptools.package_index.local_open(url) 182 assert 'content' in res.read() 183 184 def test_egg_fragment(self): 185 """ 186 EGG fragments must comply to PEP 440 187 """ 188 epoch = [ 189 '', 190 '1!', 191 ] 192 releases = [ 193 '0', 194 '0.0', 195 '0.0.0', 196 ] 197 pre = [ 198 'a0', 199 'b0', 200 'rc0', 201 ] 202 post = [ 203 '.post0' 204 ] 205 dev = [ 206 '.dev0', 207 ] 208 local = [ 209 ('', ''), 210 ('+ubuntu.0', '+ubuntu.0'), 211 ('+ubuntu-0', '+ubuntu.0'), 212 ('+ubuntu_0', '+ubuntu.0'), 213 ] 214 versions = [ 215 [''.join([e, r, p, l]) for l in ll] 216 for e in epoch 217 for r in releases 218 for p in sum([pre, post, dev], ['']) 219 for ll in local] 220 for v, vc in versions: 221 dists = list(setuptools.package_index.distros_for_url( 222 'http://example.com/example.zip#egg=example-' + v)) 223 assert dists[0].version == '' 224 assert dists[1].version == vc 225 226 227class TestContentCheckers: 228 def test_md5(self): 229 checker = setuptools.package_index.HashChecker.from_url( 230 'http://foo/bar#md5=f12895fdffbd45007040d2e44df98478') 231 checker.feed('You should probably not be using MD5'.encode('ascii')) 232 assert checker.hash.hexdigest() == 'f12895fdffbd45007040d2e44df98478' 233 assert checker.is_valid() 234 235 def test_other_fragment(self): 236 "Content checks should succeed silently if no hash is present" 237 checker = setuptools.package_index.HashChecker.from_url( 238 'http://foo/bar#something%20completely%20different') 239 checker.feed('anything'.encode('ascii')) 240 assert checker.is_valid() 241 242 def test_blank_md5(self): 243 "Content checks should succeed if a hash is empty" 244 checker = setuptools.package_index.HashChecker.from_url( 245 'http://foo/bar#md5=') 246 checker.feed('anything'.encode('ascii')) 247 assert checker.is_valid() 248 249 def test_get_hash_name_md5(self): 250 checker = setuptools.package_index.HashChecker.from_url( 251 'http://foo/bar#md5=f12895fdffbd45007040d2e44df98478') 252 assert checker.hash_name == 'md5' 253 254 def test_report(self): 255 checker = setuptools.package_index.HashChecker.from_url( 256 'http://foo/bar#md5=f12895fdffbd45007040d2e44df98478') 257 rep = checker.report(lambda x: x, 'My message about %s') 258 assert rep == 'My message about md5' 259 260 261class TestPyPIConfig: 262 def test_percent_in_password(self, tmpdir, monkeypatch): 263 monkeypatch.setitem(os.environ, 'HOME', str(tmpdir)) 264 pypirc = tmpdir / '.pypirc' 265 with pypirc.open('w') as strm: 266 strm.write(DALS(""" 267 [pypi] 268 repository=https://pypi.org 269 username=jaraco 270 password=pity% 271 """)) 272 cfg = setuptools.package_index.PyPIConfig() 273 cred = cfg.creds_by_repository['https://pypi.org'] 274 assert cred.username == 'jaraco' 275 assert cred.password == 'pity%' 276