import os
from textwrap import dedent
from six.moves.urllib.request import pathname2url
import subprocess
import sys
import mock
from click.testing import CliRunner
import pytest
from piptools.scripts.compile import cli
from piptools.scripts.sync import cli as sync_cli
from pip._vendor.packaging.version import parse as parse_version
from pip import __version__ as pip_version
PIP_VERSION = parse_version(os.environ.get('PIP', pip_version))
fail_below_pip9 = pytest.mark.xfail(
PIP_VERSION < parse_version('9'),
reason="needs pip 9 or greater"
)
@pytest.yield_fixture
def pip_conf(tmpdir):
test_conf = dedent("""\
[global]
index-url = http://example.com
trusted-host = example.com
""")
pip_conf_file = 'pip.conf' if os.name != 'nt' else 'pip.ini'
path = (tmpdir / pip_conf_file).strpath
with open(path, 'w') as f:
f.write(test_conf)
old_value = os.environ.get('PIP_CONFIG_FILE')
try:
os.environ['PIP_CONFIG_FILE'] = path
yield path
finally:
if old_value is not None:
os.environ['PIP_CONFIG_FILE'] = old_value
else:
del os.environ['PIP_CONFIG_FILE']
os.remove(path)
def test_default_pip_conf_read(pip_conf):
assert os.path.exists(pip_conf)
runner = CliRunner()
with runner.isolated_filesystem():
with open('requirements.in', 'w'):
pass
out = runner.invoke(cli, ['-v'])
assert 'Using indexes:\n http://example.com' in out.output
assert '--index-url http://example.com' in out.output
def test_command_line_overrides_pip_conf(pip_conf):
assert os.path.exists(pip_conf)
runner = CliRunner()
with runner.isolated_filesystem():
with open('requirements.in', 'w'):
pass
out = runner.invoke(cli, ['-v', '-i', 'http://override.com'])
assert 'Using indexes:\n http://override.com' in out.output
def test_command_line_setuptools_read(pip_conf):
runner = CliRunner()
with runner.isolated_filesystem():
package = open('setup.py', 'w')
package.write(dedent("""\
from setuptools import setup
setup(install_requires=[])
"""))
package.close()
out = runner.invoke(cli)
assert 'This file is autogenerated by pip-compile' in out.output
def test_find_links_option(pip_conf):
assert os.path.exists(pip_conf)
runner = CliRunner()
with runner.isolated_filesystem():
with open('requirements.in', 'w'):
pass
out = runner.invoke(cli, ['-v', '-f', './libs1', '-f', './libs2'])
assert 'Configuration:\n -f ./libs1\n -f ./libs2' in out.output
def test_extra_index_option(pip_conf):
assert os.path.exists(pip_conf)
runner = CliRunner()
with runner.isolated_filesystem():
with open('requirements.in', 'w'):
pass
out = runner.invoke(cli, ['-v',
'--extra-index-url', 'http://extraindex1.com',
'--extra-index-url', 'http://extraindex2.com'])
assert ('Using indexes:\n'
' http://example.com\n'
' http://extraindex1.com\n'
' http://extraindex2.com' in out.output)
assert ('--index-url http://example.com\n'
'--extra-index-url http://extraindex1.com\n'
'--extra-index-url http://extraindex2.com' in out.output)
def test_trusted_host(pip_conf):
assert os.path.exists(pip_conf)
runner = CliRunner()
with runner.isolated_filesystem():
with open('requirements.in', 'w'):
pass
out = runner.invoke(cli, ['-v',
'--trusted-host', 'example.com',
'--trusted-host', 'example2.com'])
assert ('--trusted-host example.com\n'
'--trusted-host example2.com\n' in out.output)
def test_trusted_host_no_emit(pip_conf):
assert os.path.exists(pip_conf)
runner = CliRunner()
with runner.isolated_filesystem():
with open('requirements.in', 'w'):
pass
out = runner.invoke(cli, ['-v',
'--trusted-host', 'example.com',
'--no-emit-trusted-host'])
assert '--trusted-host example.com' not in out.output
assert '--no-emit-trusted-host' in out.output
def test_realistic_complex_sub_dependencies(tmpdir):
subprocess.check_output(['pip', 'wheel',
'--no-deps',
'-w', str(tmpdir),
os.path.join('.', 'tests', 'test_data', 'fake_package', '.')])
runner = CliRunner()
with runner.isolated_filesystem():
with open('requirements.in', 'w') as req_in:
req_in.write('fake_with_deps')
out = runner.invoke(cli, ['-v',
'-n', '--rebuild',
'-f', str(tmpdir)])
assert out.exit_code == 0
def _invoke(command):
try:
output = subprocess.check_output(
command,
stderr=subprocess.STDOUT,
)
status = 0
except subprocess.CalledProcessError as error:
output = error.output
status = error.returncode
return status, output
def test_run_as_module_compile(tmpdir):
status, output = _invoke([
sys.executable, '-m', 'piptools', 'compile', '--help',
])
output = output.decode('utf-8')
assert output.startswith('Usage:')
assert 'Compiles requirements.txt from requirements.in' in output
assert status == 0
def test_run_as_module_sync():
status, output = _invoke([
sys.executable, '-m', 'piptools', 'sync', '--help',
])
output = output.decode('utf-8')
assert output.startswith('Usage:')
assert 'Synchronize virtual environment with' in output
assert status == 0
def test_sync_quiet(tmpdir):
runner = CliRunner()
with runner.isolated_filesystem():
with open('requirements.txt', 'w') as req_in:
req_in.write('six==1.10.0')
with mock.patch('piptools.sync.check_call') as check_call:
out = runner.invoke(sync_cli, ['-q'])
assert out.output == ''
assert out.exit_code == 0
for call in check_call.call_args_list:
assert '-q' in call[0][0]
def test_editable_package(tmpdir):
fake_package_dir = os.path.join(os.path.split(__file__)[0], 'test_data', 'small_fake_package')
fake_package_dir = 'file:' + pathname2url(fake_package_dir)
runner = CliRunner()
with runner.isolated_filesystem():
with open('requirements.in', 'w') as req_in:
req_in.write('-e ' + fake_package_dir)
out = runner.invoke(cli, ['-n'])
assert out.exit_code == 0
assert fake_package_dir in out.output
assert 'six==1.10.0' in out.output
def test_editable_package_vcs(tmpdir):
vcs_package = (
'git+git://github.com/pytest-dev/pytest-django'
'@21492afc88a19d4ca01cd0ac392a5325b14f95c7'
'#egg=pytest-django'
)
runner = CliRunner()
with runner.isolated_filesystem():
with open('requirements.in', 'w') as req_in:
req_in.write('-e ' + vcs_package)
out = runner.invoke(cli, ['-n',
'--rebuild'])
print(out.output)
assert out.exit_code == 0
assert vcs_package in out.output
assert 'pytest' in out.output
def test_locally_available_editable_package_is_not_archived_in_cache_dir(tmpdir):
cache_dir = tmpdir.mkdir('cache_dir')
fake_package_dir = os.path.join(os.path.split(__file__)[0], 'test_data', 'small_fake_package')
fake_package_dir = 'file:' + pathname2url(fake_package_dir)
with mock.patch('piptools.repositories.pypi.CACHE_DIR', new=str(cache_dir)):
runner = CliRunner()
with runner.isolated_filesystem():
with open('requirements.in', 'w') as req_in:
req_in.write('-e ' + fake_package_dir)
out = runner.invoke(cli, ['-n'])
assert out.exit_code == 0
assert fake_package_dir in out.output
assert 'six==1.10.0' in out.output
assert not os.listdir(os.path.join(str(cache_dir), 'pkgs'))
def test_input_file_without_extension(tmpdir):
runner = CliRunner()
with runner.isolated_filesystem():
with open('requirements', 'w') as req_in:
req_in.write('six==1.10.0')
out = runner.invoke(cli, ['-n', 'requirements'])
assert out.exit_code == 0
assert '--output-file requirements.txt' in out.output
assert 'six==1.10.0' in out.output
def test_upgrade_packages_option(tmpdir):
fake_package_dir = os.path.join(os.path.split(__file__)[0], 'test_data', 'minimal_wheels')
runner = CliRunner()
with runner.isolated_filesystem():
with open('requirements.in', 'w') as req_in:
req_in.write('small-fake-a\nsmall-fake-b')
with open('requirements.txt', 'w') as req_in:
req_in.write('small-fake-a==0.1\nsmall-fake-b==0.1')
out = runner.invoke(cli, [
'-P', 'small_fake_b',
'-f', fake_package_dir,
])
assert out.exit_code == 0
assert 'small-fake-a==0.1' in out.output
assert 'small-fake-b==0.2' in out.output
def test_generate_hashes_with_editable():
small_fake_package_dir = os.path.join(
os.path.split(__file__)[0], 'test_data', 'small_fake_package')
small_fake_package_url = 'file:' + pathname2url(small_fake_package_dir)
runner = CliRunner()
with runner.isolated_filesystem():
with open('requirements.in', 'w') as fp:
fp.write('-e {}\n'.format(small_fake_package_url))
fp.write('pytz==2017.2\n')
out = runner.invoke(cli, ['--generate-hashes'])
expected = (
'#\n'
'# This file is autogenerated by pip-compile\n'
'# To update, run:\n'
'#\n'
'# pip-compile --generate-hashes --output-file requirements.txt requirements.in\n'
'#\n'
'-e {}\n'
'pytz==2017.2 \\\n'
' --hash=sha256:d1d6729c85acea5423671382868627129432fba9a89ecbb248d8d1c7a9f01c67 \\\n'
' --hash=sha256:f5c056e8f62d45ba8215e5cb8f50dfccb198b4b9fbea8500674f3443e4689589\n'
).format(small_fake_package_url)
assert out.exit_code == 0
assert expected in out.output
@fail_below_pip9
def test_filter_pip_markes():
runner = CliRunner()
with runner.isolated_filesystem():
with open('requirements', 'w') as req_in:
req_in.write(
"six==1.10.0\n"
"unknown_package==0.1; python_version == '1'")
out = runner.invoke(cli, ['-n', 'requirements'])
assert out.exit_code == 0
assert '--output-file requirements.txt' in out.output
assert 'six==1.10.0' in out.output
assert 'unknown_package' not in out.output
def test_no_candidates():
runner = CliRunner()
with runner.isolated_filesystem():
with open('requirements', 'w') as req_in:
req_in.write('six>1.0b0,<1.0b0')
out = runner.invoke(cli, ['-n', 'requirements'])
assert out.exit_code == 2
assert 'Skipped pre-versions:' in out.output
def test_no_candidates_pre():
runner = CliRunner()
with runner.isolated_filesystem():
with open('requirements', 'w') as req_in:
req_in.write('six>1.0b0,<1.0b0')
out = runner.invoke(cli, ['-n', 'requirements', '--pre'])
assert out.exit_code == 2
assert 'Tried pre-versions:' in out.output