test_packageindex.py 9.9 KB

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