2015-04-08 04:22:37 +02:00
|
|
|
import os
|
|
|
|
import subprocess
|
|
|
|
from textwrap import dedent
|
2015-04-07 06:53:19 +02:00
|
|
|
|
2015-04-08 04:22:37 +02:00
|
|
|
from mock import patch
|
|
|
|
import pytest
|
2015-04-07 06:45:10 +02:00
|
|
|
from pretend import stub
|
|
|
|
|
2015-04-17 05:03:34 +02:00
|
|
|
import pip
|
2015-04-17 07:10:46 +02:00
|
|
|
from pip.exceptions import (RequirementsFileParseError,
|
|
|
|
ReqFileOnleOneOptionPerLineError,
|
|
|
|
ReqFileOptionNotAllowedWithReqError)
|
2015-04-07 06:53:19 +02:00
|
|
|
from pip.download import PipSession
|
|
|
|
from pip.index import PackageFinder
|
2015-04-07 06:45:10 +02:00
|
|
|
from pip.req.req_install import InstallRequirement
|
2015-04-17 07:10:46 +02:00
|
|
|
from pip.req.req_file import (parse_requirements, process_line, join_lines,
|
|
|
|
ignore_comments)
|
2015-03-31 06:11:32 +02:00
|
|
|
|
|
|
|
|
2015-04-18 20:42:06 +02:00
|
|
|
@pytest.fixture
|
|
|
|
def session():
|
|
|
|
return PipSession()
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def finder(session):
|
|
|
|
return PackageFinder([], [], session=session)
|
|
|
|
|
|
|
|
|
2015-03-31 06:11:32 +02:00
|
|
|
class TestIgnoreComments(object):
|
|
|
|
"""tests for `ignore_comment`"""
|
2015-04-07 06:45:10 +02:00
|
|
|
|
|
|
|
def test_strip_empty_line(self):
|
|
|
|
lines = ['req1', '', 'req2']
|
|
|
|
result = ignore_comments(lines)
|
|
|
|
assert list(result) == ['req1', 'req2']
|
|
|
|
|
|
|
|
def test_strip_comment(self):
|
|
|
|
lines = ['req1', '# comment', 'req2']
|
|
|
|
result = ignore_comments(lines)
|
|
|
|
assert list(result) == ['req1', 'req2']
|
2015-03-31 06:11:32 +02:00
|
|
|
|
|
|
|
|
|
|
|
class TestJoinLines(object):
|
|
|
|
"""tests for `join_lines`"""
|
2015-04-07 06:45:10 +02:00
|
|
|
|
|
|
|
def test_join_lines(self):
|
2015-04-08 04:22:37 +02:00
|
|
|
lines = dedent('''\
|
|
|
|
line 1
|
|
|
|
line 2:1 \\
|
|
|
|
line 2:2
|
|
|
|
line 3:1 \\
|
|
|
|
line 3:2 \\
|
|
|
|
line 3:3
|
|
|
|
line 4
|
|
|
|
''').splitlines()
|
|
|
|
|
|
|
|
expect = [
|
|
|
|
'line 1',
|
|
|
|
'line 2:1 line 2:2',
|
|
|
|
'line 3:1 line 3:2 line 3:3',
|
|
|
|
'line 4',
|
|
|
|
]
|
|
|
|
assert expect == list(join_lines(lines))
|
2015-03-31 06:11:32 +02:00
|
|
|
|
|
|
|
|
2015-04-17 07:10:46 +02:00
|
|
|
class TestProcessLine(object):
|
|
|
|
"""tests for `process_line`"""
|
2015-04-07 06:45:10 +02:00
|
|
|
|
2015-04-17 07:10:46 +02:00
|
|
|
def setup(self):
|
|
|
|
self.options = stub(isolated_mode=False, default_vcs=None,
|
|
|
|
skip_requirements_regex=False)
|
2015-04-09 06:04:46 +02:00
|
|
|
|
2015-04-17 07:10:46 +02:00
|
|
|
def test_parser_error(self):
|
2015-04-09 06:04:46 +02:00
|
|
|
with pytest.raises(RequirementsFileParseError):
|
2015-04-17 07:10:46 +02:00
|
|
|
list(process_line("--bogus", "file", 1))
|
2015-04-09 06:04:46 +02:00
|
|
|
|
2015-04-17 07:10:46 +02:00
|
|
|
def test_only_one_req_per_line(self):
|
2015-04-23 10:31:48 +02:00
|
|
|
# pkg_resources raises the ValueError
|
|
|
|
with pytest.raises(ValueError):
|
2015-04-17 07:10:46 +02:00
|
|
|
list(process_line("req1 req2", "file", 1))
|
2015-04-09 06:04:46 +02:00
|
|
|
|
2015-04-17 07:10:46 +02:00
|
|
|
def test_only_one_option_per_line(self):
|
|
|
|
with pytest.raises(ReqFileOnleOneOptionPerLineError):
|
|
|
|
list(process_line("--index-url=url --no-use-wheel", "file", 1))
|
2015-04-07 06:45:10 +02:00
|
|
|
|
2015-04-17 07:10:46 +02:00
|
|
|
def test_option_not_allowed_on_req_line(self):
|
|
|
|
with pytest.raises(ReqFileOptionNotAllowedWithReqError):
|
|
|
|
list(process_line("req --index-url=url", "file", 1))
|
2015-04-07 06:45:10 +02:00
|
|
|
|
2015-04-17 07:10:46 +02:00
|
|
|
def test_yield_line_requirement(self):
|
|
|
|
line = 'SomeProject'
|
2015-04-07 06:45:10 +02:00
|
|
|
filename = 'filename'
|
|
|
|
comes_from = '-r %s (line %s)' % (filename, 1)
|
2015-04-17 07:10:46 +02:00
|
|
|
req = InstallRequirement.from_line(line, comes_from=comes_from)
|
|
|
|
assert repr(list(process_line(line, filename, 1))[0]) == repr(req)
|
2015-04-07 06:45:10 +02:00
|
|
|
|
2015-04-23 10:31:48 +02:00
|
|
|
def test_yield_line_requirement_with_spaces_in_specifier(self):
|
|
|
|
line = 'SomeProject >= 2'
|
|
|
|
filename = 'filename'
|
|
|
|
comes_from = '-r %s (line %s)' % (filename, 1)
|
|
|
|
req = InstallRequirement.from_line(line, comes_from=comes_from)
|
|
|
|
assert repr(list(process_line(line, filename, 1))[0]) == repr(req)
|
|
|
|
assert req.req.specs == [('>=', '2')]
|
|
|
|
|
2015-04-17 07:10:46 +02:00
|
|
|
def test_yield_editable_requirement(self):
|
2015-04-07 06:45:10 +02:00
|
|
|
url = 'git+https://url#egg=SomeProject'
|
2015-04-17 07:10:46 +02:00
|
|
|
line = '-e %s' % url
|
2015-04-07 06:45:10 +02:00
|
|
|
filename = 'filename'
|
|
|
|
comes_from = '-r %s (line %s)' % (filename, 1)
|
|
|
|
req = InstallRequirement.from_editable(url, comes_from=comes_from)
|
2015-04-17 07:10:46 +02:00
|
|
|
assert repr(list(process_line(line, filename, 1))[0]) == repr(req)
|
2015-04-07 06:45:10 +02:00
|
|
|
|
2015-04-17 07:10:46 +02:00
|
|
|
def test_nested_requirements_file(self, monkeypatch):
|
|
|
|
line = '-r another_file'
|
2015-04-07 06:45:10 +02:00
|
|
|
req = InstallRequirement.from_line('SomeProject')
|
|
|
|
import pip.req.req_file
|
2015-04-08 04:50:40 +02:00
|
|
|
|
|
|
|
def stub_parse_requirements(req_url, finder, comes_from, options,
|
2015-04-20 06:43:02 +02:00
|
|
|
session, wheel_cache):
|
2015-04-08 04:50:40 +02:00
|
|
|
return [req]
|
|
|
|
parse_requirements_stub = stub(call=stub_parse_requirements)
|
|
|
|
monkeypatch.setattr(pip.req.req_file, 'parse_requirements',
|
|
|
|
parse_requirements_stub.call)
|
2015-04-17 07:10:46 +02:00
|
|
|
assert list(process_line(line, 'filename', 1)) == [req]
|
|
|
|
|
|
|
|
def test_options_on_a_requirement_line(self):
|
|
|
|
line = 'SomeProject --install-option=yo1 --install-option yo2 '\
|
|
|
|
'--global-option="yo3" --global-option "yo4"'
|
|
|
|
filename = 'filename'
|
|
|
|
req = list(process_line(line, filename, 1))[0]
|
|
|
|
assert req.options == {
|
|
|
|
'global_options': ['yo3', 'yo4'],
|
|
|
|
'install_options': ['yo1', 'yo2']}
|
2015-03-31 06:11:32 +02:00
|
|
|
|
2015-04-17 07:10:46 +02:00
|
|
|
def test_set_isolated(self):
|
|
|
|
line = 'SomeProject'
|
2015-04-11 19:08:33 +02:00
|
|
|
filename = 'filename'
|
|
|
|
self.options.isolated_mode = True
|
2015-04-17 07:10:46 +02:00
|
|
|
result = process_line(line, filename, 1, options=self.options)
|
2015-04-11 19:08:33 +02:00
|
|
|
assert list(result)[0].isolated
|
|
|
|
|
2015-04-17 07:10:46 +02:00
|
|
|
def test_set_default_vcs(self):
|
2015-04-11 19:08:33 +02:00
|
|
|
url = 'https://url#egg=SomeProject'
|
2015-04-17 07:10:46 +02:00
|
|
|
line = '-e %s' % url
|
2015-04-11 19:08:33 +02:00
|
|
|
filename = 'filename'
|
|
|
|
self.options.default_vcs = 'git'
|
2015-04-17 07:10:46 +02:00
|
|
|
result = process_line(line, filename, 1, options=self.options)
|
2015-04-11 19:08:33 +02:00
|
|
|
assert list(result)[0].link.url == 'git+' + url
|
|
|
|
|
2015-04-18 20:42:06 +02:00
|
|
|
def test_set_finder_no_index(self, finder):
|
|
|
|
list(process_line("--no-index", "file", 1, finder=finder))
|
|
|
|
assert finder.index_urls == []
|
2015-03-31 06:11:32 +02:00
|
|
|
|
2015-04-18 20:42:06 +02:00
|
|
|
def test_set_finder_index_url(self, finder):
|
|
|
|
list(process_line("--index-url=url", "file", 1, finder=finder))
|
|
|
|
assert finder.index_urls == ['url']
|
2015-04-08 04:22:37 +02:00
|
|
|
|
2015-04-18 20:42:06 +02:00
|
|
|
def test_set_finder_find_links(self, finder):
|
|
|
|
list(process_line("--find-links=url", "file", 1, finder=finder))
|
|
|
|
assert finder.find_links == ['url']
|
2015-04-08 04:22:37 +02:00
|
|
|
|
2015-04-18 20:42:06 +02:00
|
|
|
def test_set_finder_extra_index_urls(self, finder):
|
|
|
|
list(process_line("--extra-index-url=url", "file", 1, finder=finder))
|
|
|
|
assert finder.index_urls == ['url']
|
|
|
|
|
|
|
|
def test_set_finder_allow_external(self, finder):
|
2015-04-18 22:24:44 +02:00
|
|
|
list(process_line("--allow-external=SomeProject",
|
|
|
|
"file", 1, finder=finder))
|
2015-04-18 20:42:06 +02:00
|
|
|
assert finder.allow_external == set(['someproject'])
|
|
|
|
|
|
|
|
def test_set_finder_allow_unsafe(self, finder):
|
2015-04-18 22:24:44 +02:00
|
|
|
list(process_line("--allow-unverified=SomeProject",
|
|
|
|
"file", 1, finder=finder))
|
2015-04-18 20:42:06 +02:00
|
|
|
assert finder.allow_unverified == set(['someproject'])
|
|
|
|
|
|
|
|
def test_set_finder_use_wheel(self, finder):
|
|
|
|
list(process_line("--use-wheel", "file", 1, finder=finder))
|
2015-04-17 05:03:34 +02:00
|
|
|
no_use_wheel_fmt = pip.index.FormatControl(set(), set())
|
|
|
|
assert finder.format_control == no_use_wheel_fmt
|
2015-04-18 20:42:06 +02:00
|
|
|
|
|
|
|
def test_set_finder_no_use_wheel(self, finder):
|
|
|
|
list(process_line("--no-use-wheel", "file", 1, finder=finder))
|
2015-04-17 05:03:34 +02:00
|
|
|
no_use_wheel_fmt = pip.index.FormatControl(set([':all:']), set())
|
|
|
|
assert finder.format_control == no_use_wheel_fmt
|
2015-04-18 20:42:06 +02:00
|
|
|
|
|
|
|
def test_noop_always_unzip(self, finder):
|
|
|
|
# noop, but confirm it can be set
|
|
|
|
list(process_line("--always-unzip", "file", 1, finder=finder))
|
|
|
|
|
|
|
|
def test_noop_finder_no_allow_unsafe(self, finder):
|
|
|
|
# noop, but confirm it can be set
|
|
|
|
list(process_line("--no-allow-insecure", "file", 1, finder=finder))
|
2015-04-08 04:22:37 +02:00
|
|
|
|
2015-04-18 20:53:14 +02:00
|
|
|
|
|
|
|
class TestOptionVariants(object):
|
|
|
|
|
|
|
|
# this suite is really just testing optparse, but added it anyway
|
|
|
|
|
|
|
|
def test_variant1(self, finder):
|
|
|
|
list(process_line("-i url", "file", 1, finder=finder))
|
|
|
|
assert finder.index_urls == ['url']
|
|
|
|
|
|
|
|
def test_variant2(self, finder):
|
|
|
|
list(process_line("-i 'url'", "file", 1, finder=finder))
|
|
|
|
assert finder.index_urls == ['url']
|
|
|
|
|
|
|
|
def test_variant3(self, finder):
|
|
|
|
list(process_line("--index-url=url", "file", 1, finder=finder))
|
|
|
|
assert finder.index_urls == ['url']
|
|
|
|
|
2015-04-18 20:59:17 +02:00
|
|
|
def test_variant4(self, finder):
|
2015-04-18 20:53:14 +02:00
|
|
|
list(process_line("--index-url url", "file", 1, finder=finder))
|
|
|
|
assert finder.index_urls == ['url']
|
|
|
|
|
2015-04-18 20:59:17 +02:00
|
|
|
def test_variant5(self, finder):
|
2015-04-18 20:53:14 +02:00
|
|
|
list(process_line("--index-url='url'", "file", 1, finder=finder))
|
|
|
|
assert finder.index_urls == ['url']
|
|
|
|
|
2015-04-08 04:22:37 +02:00
|
|
|
|
2015-03-31 06:11:32 +02:00
|
|
|
class TestParseRequirements(object):
|
|
|
|
"""tests for `parse_requirements`"""
|
2015-04-07 06:53:19 +02:00
|
|
|
|
|
|
|
@pytest.mark.network
|
|
|
|
def test_remote_reqs_parse(self):
|
|
|
|
"""
|
|
|
|
Test parsing a simple remote requirements file
|
|
|
|
"""
|
2015-04-08 04:50:40 +02:00
|
|
|
# this requirements file just contains a comment previously this has
|
|
|
|
# failed in py3: https://github.com/pypa/pip/issues/760
|
2015-04-07 06:53:19 +02:00
|
|
|
for req in parse_requirements(
|
2015-04-08 04:50:40 +02:00
|
|
|
'https://raw.githubusercontent.com/pypa/'
|
|
|
|
'pip-test-package/master/'
|
2015-04-07 06:53:19 +02:00
|
|
|
'tests/req_just_comment.txt', session=PipSession()):
|
|
|
|
pass
|
|
|
|
|
2015-04-18 21:32:30 +02:00
|
|
|
def test_multiple_appending_options(self, tmpdir, finder):
|
|
|
|
with open(tmpdir.join("req1.txt"), "w") as fp:
|
|
|
|
fp.write("--extra-index-url url1 \n")
|
|
|
|
fp.write("--extra-index-url url2 ")
|
|
|
|
|
|
|
|
list(parse_requirements(tmpdir.join("req1.txt"), finder=finder,
|
|
|
|
session=PipSession()))
|
|
|
|
|
|
|
|
assert finder.index_urls == ['url1', 'url2']
|
|
|
|
|
|
|
|
def test_skip_regex(self, tmpdir, finder):
|
|
|
|
options = stub(isolated_mode=False, default_vcs=None,
|
|
|
|
skip_requirements_regex='.*Bad.*')
|
|
|
|
with open(tmpdir.join("req1.txt"), "w") as fp:
|
|
|
|
fp.write("--extra-index-url Bad \n")
|
|
|
|
fp.write("--extra-index-url Good ")
|
|
|
|
|
|
|
|
list(parse_requirements(tmpdir.join("req1.txt"), finder=finder,
|
|
|
|
options=options, session=PipSession()))
|
|
|
|
|
|
|
|
assert finder.index_urls == ['Good']
|
|
|
|
|
|
|
|
def test_join_lines(self, tmpdir, finder):
|
|
|
|
with open(tmpdir.join("req1.txt"), "w") as fp:
|
|
|
|
fp.write("--extra-index-url url1 \\\n--extra-index-url url2")
|
|
|
|
|
|
|
|
list(parse_requirements(tmpdir.join("req1.txt"), finder=finder,
|
|
|
|
session=PipSession()))
|
|
|
|
|
|
|
|
assert finder.index_urls == ['url1', 'url2']
|
|
|
|
|
2015-04-17 05:03:34 +02:00
|
|
|
def test_req_file_parse_no_only_binary(self, data):
|
|
|
|
finder = PackageFinder([], [], session=PipSession())
|
|
|
|
list(parse_requirements(
|
|
|
|
data.reqfiles.join("supported_options2.txt"), finder,
|
|
|
|
session=PipSession()))
|
|
|
|
expected = pip.index.FormatControl(set(['fred']), set(['wilma']))
|
|
|
|
assert finder.format_control == expected
|
|
|
|
|
2015-04-18 20:59:17 +02:00
|
|
|
def test_req_file_parse_comment_start_of_line(self, tmpdir, finder):
|
2015-04-07 06:53:19 +02:00
|
|
|
"""
|
|
|
|
Test parsing comments in a requirements file
|
|
|
|
"""
|
|
|
|
with open(tmpdir.join("req1.txt"), "w") as fp:
|
|
|
|
fp.write("# Comment ")
|
|
|
|
|
|
|
|
reqs = list(parse_requirements(tmpdir.join("req1.txt"), finder,
|
|
|
|
session=PipSession()))
|
|
|
|
|
|
|
|
assert not reqs
|
|
|
|
|
2015-04-18 20:59:17 +02:00
|
|
|
def test_req_file_parse_comment_end_of_line_with_url(self, tmpdir, finder):
|
2015-04-07 06:53:19 +02:00
|
|
|
"""
|
|
|
|
Test parsing comments in a requirements file
|
|
|
|
"""
|
|
|
|
with open(tmpdir.join("req1.txt"), "w") as fp:
|
|
|
|
fp.write("https://example.com/foo.tar.gz # Comment ")
|
|
|
|
|
|
|
|
reqs = list(parse_requirements(tmpdir.join("req1.txt"), finder,
|
|
|
|
session=PipSession()))
|
|
|
|
|
|
|
|
assert len(reqs) == 1
|
|
|
|
assert reqs[0].link.url == "https://example.com/foo.tar.gz"
|
|
|
|
|
2015-04-18 20:59:17 +02:00
|
|
|
def test_req_file_parse_egginfo_end_of_line_with_url(self, tmpdir, finder):
|
2015-04-07 06:53:19 +02:00
|
|
|
"""
|
|
|
|
Test parsing comments in a requirements file
|
|
|
|
"""
|
|
|
|
with open(tmpdir.join("req1.txt"), "w") as fp:
|
|
|
|
fp.write("https://example.com/foo.tar.gz#egg=wat")
|
|
|
|
|
|
|
|
reqs = list(parse_requirements(tmpdir.join("req1.txt"), finder,
|
|
|
|
session=PipSession()))
|
|
|
|
|
|
|
|
assert len(reqs) == 1
|
|
|
|
assert reqs[0].name == "wat"
|
|
|
|
|
|
|
|
def test_req_file_no_finder(self, tmpdir):
|
|
|
|
"""
|
|
|
|
Test parsing a requirements file without a finder
|
|
|
|
"""
|
|
|
|
with open(tmpdir.join("req.txt"), "w") as fp:
|
|
|
|
fp.write("""
|
|
|
|
--find-links https://example.com/
|
|
|
|
--index-url https://example.com/
|
|
|
|
--extra-index-url https://two.example.com/
|
|
|
|
--no-use-wheel
|
|
|
|
--no-index
|
|
|
|
--allow-external foo
|
|
|
|
--allow-all-external
|
|
|
|
--allow-insecure foo
|
|
|
|
--allow-unverified foo
|
|
|
|
""")
|
|
|
|
|
|
|
|
parse_requirements(tmpdir.join("req.txt"), session=PipSession())
|
2015-04-08 04:22:37 +02:00
|
|
|
|
|
|
|
def test_install_requirements_with_options(self, tmpdir, finder, session):
|
2015-04-08 04:33:44 +02:00
|
|
|
global_option = '--dry-run'
|
|
|
|
install_option = '--prefix=/opt'
|
|
|
|
|
2015-04-08 04:22:37 +02:00
|
|
|
content = '''
|
2015-04-17 07:10:46 +02:00
|
|
|
INITools==2.0 --global-option="{global_option}" \
|
2015-04-08 04:33:44 +02:00
|
|
|
--install-option "{install_option}"
|
|
|
|
'''.format(global_option=global_option, install_option=install_option)
|
2015-04-08 04:22:37 +02:00
|
|
|
|
|
|
|
req_path = tmpdir.join('requirements.txt')
|
|
|
|
with open(req_path, 'w') as fh:
|
|
|
|
fh.write(content)
|
|
|
|
|
2015-04-08 04:50:40 +02:00
|
|
|
req = next(parse_requirements(req_path, finder=finder,
|
|
|
|
session=session))
|
2015-04-08 04:22:37 +02:00
|
|
|
|
|
|
|
req.source_dir = os.curdir
|
|
|
|
with patch.object(subprocess, 'Popen') as popen:
|
|
|
|
try:
|
|
|
|
req.install([])
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
|
|
|
call = popen.call_args_list[0][0][0]
|
2015-04-08 04:37:36 +02:00
|
|
|
assert call.index(install_option) > \
|
|
|
|
call.index('install') > \
|
|
|
|
call.index(global_option) > 0
|