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