mirror of
https://github.com/kennethreitz/pipenv.git
synced 2026-06-05 22:50:18 +00:00
a08a2da524
- Stop preferring resolution of VCS dependencies in all cases - Resolve vcs dependencies together with non-vcs dependencies - Clarify blocking and no-deps logic - Add artifacts and tests - Add vendoring task for artifacts - Clean up release tasks - Fixes #3296 Signed-off-by: Dan Ryan <dan@danryan.co>
563 lines
18 KiB
Python
563 lines
18 KiB
Python
import pytest
|
|
import os
|
|
import sys
|
|
|
|
from pipenv.utils import temp_environ
|
|
|
|
from flaky import flaky
|
|
|
|
|
|
@pytest.mark.lock
|
|
@pytest.mark.requirements
|
|
def test_lock_handle_eggs(PipenvInstance, pypi):
|
|
"""Ensure locking works with packages provoding egg formats.
|
|
"""
|
|
with PipenvInstance() as p:
|
|
with open(p.pipfile_path, 'w') as f:
|
|
f.write("""
|
|
[packages]
|
|
RandomWords = "*"
|
|
""")
|
|
c = p.pipenv('lock --verbose')
|
|
assert c.return_code == 0
|
|
assert 'randomwords' in p.lockfile['default']
|
|
assert p.lockfile['default']['randomwords']['version'] == '==0.2.1'
|
|
|
|
|
|
@pytest.mark.lock
|
|
@pytest.mark.requirements
|
|
def test_lock_requirements_file(PipenvInstance, pypi):
|
|
|
|
with PipenvInstance(pypi=pypi) as p:
|
|
with open(p.pipfile_path, 'w') as f:
|
|
contents = """
|
|
[packages]
|
|
requests = "==2.14.0"
|
|
[dev-packages]
|
|
flask = "==0.12.2"
|
|
""".strip()
|
|
f.write(contents)
|
|
|
|
req_list = ("requests==2.14.0",)
|
|
|
|
dev_req_list = ("flask==0.12.2",)
|
|
|
|
c = p.pipenv('lock -r')
|
|
d = p.pipenv('lock -r -d')
|
|
assert c.return_code == 0
|
|
assert d.return_code == 0
|
|
|
|
for req in req_list:
|
|
assert req in c.out
|
|
|
|
for req in dev_req_list:
|
|
assert req in d.out
|
|
|
|
|
|
@pytest.mark.lock
|
|
def test_lock_keep_outdated(PipenvInstance, pypi):
|
|
|
|
with PipenvInstance(pypi=pypi) as p:
|
|
with open(p.pipfile_path, 'w') as f:
|
|
contents = """
|
|
[packages]
|
|
requests = {version = "==2.14.0"}
|
|
PyTest = "==3.1.0"
|
|
""".strip()
|
|
f.write(contents)
|
|
|
|
c = p.pipenv('lock')
|
|
assert c.return_code == 0
|
|
lock = p.lockfile
|
|
assert 'requests' in lock['default']
|
|
assert lock['default']['requests']['version'] == "==2.14.0"
|
|
assert 'pytest' in lock['default']
|
|
assert lock['default']['pytest']['version'] == "==3.1.0"
|
|
|
|
with open(p.pipfile_path, 'w') as f:
|
|
updated_contents = """
|
|
[packages]
|
|
requests = {version = "==2.18.4"}
|
|
PyTest = "*"
|
|
""".strip()
|
|
f.write(updated_contents)
|
|
|
|
c = p.pipenv('lock --keep-outdated')
|
|
assert c.return_code == 0
|
|
lock = p.lockfile
|
|
assert 'requests' in lock['default']
|
|
assert lock['default']['requests']['version'] == "==2.18.4"
|
|
assert 'pytest' in lock['default']
|
|
assert lock['default']['pytest']['version'] == "==3.1.0"
|
|
|
|
|
|
@pytest.mark.lock
|
|
@pytest.mark.complex
|
|
@pytest.mark.needs_internet
|
|
def test_complex_lock_with_vcs_deps(PipenvInstance, pip_src_dir):
|
|
# This uses the real PyPI since we need Internet to access the Git
|
|
# dependency anyway.
|
|
with PipenvInstance() as p:
|
|
with open(p.pipfile_path, 'w') as f:
|
|
contents = """
|
|
[packages]
|
|
click = "==6.7"
|
|
|
|
[dev-packages]
|
|
requests = {git = "https://github.com/requests/requests.git"}
|
|
""".strip()
|
|
f.write(contents)
|
|
|
|
c = p.pipenv('install')
|
|
assert c.return_code == 0
|
|
lock = p.lockfile
|
|
assert 'requests' in lock['develop']
|
|
assert 'click' in lock['default']
|
|
|
|
c = p.pipenv('run pip install -e git+https://github.com/dateutil/dateutil#egg=python_dateutil')
|
|
assert c.return_code == 0
|
|
|
|
c = p.pipenv('lock')
|
|
assert c.return_code == 0
|
|
lock = p.lockfile
|
|
assert 'requests' in lock['develop']
|
|
assert 'click' in lock['default']
|
|
assert 'python_dateutil' not in lock['default']
|
|
assert 'python_dateutil' not in lock['develop']
|
|
|
|
|
|
@pytest.mark.lock
|
|
@pytest.mark.requirements
|
|
def test_lock_with_prereleases(PipenvInstance, pypi):
|
|
|
|
with PipenvInstance(pypi=pypi) as p:
|
|
with open(p.pipfile_path, 'w') as f:
|
|
contents = """
|
|
[packages]
|
|
sqlalchemy = "==1.2.0b3"
|
|
|
|
[pipenv]
|
|
allow_prereleases = true
|
|
""".strip()
|
|
f.write(contents)
|
|
|
|
c = p.pipenv('lock')
|
|
assert c.return_code == 0
|
|
assert p.lockfile['default']['sqlalchemy']['version'] == '==1.2.0b3'
|
|
|
|
|
|
@pytest.mark.lock
|
|
@pytest.mark.complex
|
|
@pytest.mark.maya
|
|
@pytest.mark.needs_internet
|
|
@flaky
|
|
def test_complex_deps_lock_and_install_properly(PipenvInstance, pip_src_dir, pypi):
|
|
# This uses the real PyPI because Maya has too many dependencies...
|
|
with PipenvInstance(chdir=True, pypi=pypi) as p:
|
|
with open(p.pipfile_path, 'w') as f:
|
|
contents = """
|
|
[packages]
|
|
maya = "*"
|
|
""".strip()
|
|
f.write(contents)
|
|
|
|
c = p.pipenv('lock --verbose')
|
|
assert c.return_code == 0
|
|
|
|
c = p.pipenv('install')
|
|
assert c.return_code == 0
|
|
|
|
|
|
@pytest.mark.extras
|
|
@pytest.mark.lock
|
|
def test_lock_extras_without_install(PipenvInstance, pypi):
|
|
with PipenvInstance(pypi=pypi) as p:
|
|
with open(p.pipfile_path, 'w') as f:
|
|
contents = """
|
|
[packages]
|
|
requests = {version = "*", extras = ["socks"]}
|
|
""".strip()
|
|
f.write(contents)
|
|
|
|
c = p.pipenv('lock')
|
|
assert c.return_code == 0
|
|
assert "requests" in p.lockfile["default"]
|
|
assert "pysocks" in p.lockfile["default"]
|
|
assert "markers" not in p.lockfile["default"]['pysocks']
|
|
|
|
c = p.pipenv('lock -r')
|
|
assert c.return_code == 0
|
|
assert "extra == 'socks'" not in c.out.strip()
|
|
|
|
|
|
@pytest.mark.extras
|
|
@pytest.mark.lock
|
|
@pytest.mark.complex
|
|
@pytest.mark.skip(reason='Needs numpy to be mocked')
|
|
@pytest.mark.needs_internet
|
|
def test_complex_lock_deep_extras(PipenvInstance, pypi):
|
|
# records[pandas] requires tablib[pandas] which requires pandas.
|
|
# This uses the real PyPI; Pandas has too many requirements to mock.
|
|
|
|
with PipenvInstance(pypi=pypi) as p:
|
|
with open(p.pipfile_path, 'w') as f:
|
|
contents = """
|
|
[packages]
|
|
records = {extras = ["pandas"], version = "==0.5.2"}
|
|
""".strip()
|
|
f.write(contents)
|
|
|
|
c = p.pipenv('install')
|
|
assert c.return_code == 0
|
|
c = p.pipenv('lock')
|
|
assert c.return_code == 0
|
|
assert 'tablib' in p.lockfile['default']
|
|
assert 'pandas' in p.lockfile['default']
|
|
|
|
|
|
@pytest.mark.skip_lock
|
|
@pytest.mark.index
|
|
@pytest.mark.needs_internet
|
|
@pytest.mark.install # private indexes need to be uncached for resolution
|
|
def test_private_index_skip_lock(PipenvInstance):
|
|
with PipenvInstance() as p:
|
|
with open(p.pipfile_path, 'w') as f:
|
|
contents = """
|
|
[[source]]
|
|
url = "https://pypi.org/simple"
|
|
verify_ssl = true
|
|
name = "pypi"
|
|
|
|
[[source]]
|
|
url = "https://test.pypi.org/simple"
|
|
verify_ssl = true
|
|
name = "testpypi"
|
|
|
|
[packages]
|
|
pipenv-test-private-package = {version = "*", index = "testpypi"}
|
|
requests = "*"
|
|
""".strip()
|
|
f.write(contents)
|
|
c = p.pipenv('install --skip-lock')
|
|
assert c.return_code == 0
|
|
|
|
|
|
@pytest.mark.requirements
|
|
@pytest.mark.lock
|
|
@pytest.mark.index
|
|
@pytest.mark.install # private indexes need to be uncached for resolution
|
|
@pytest.mark.needs_internet
|
|
def test_private_index_lock_requirements(PipenvInstance):
|
|
# Don't use the local fake pypi
|
|
with PipenvInstance() as p:
|
|
with open(p.pipfile_path, 'w') as f:
|
|
contents = """
|
|
[[source]]
|
|
url = "https://pypi.org/simple"
|
|
verify_ssl = true
|
|
name = "pypi"
|
|
|
|
[[source]]
|
|
url = "https://test.pypi.org/simple"
|
|
verify_ssl = true
|
|
name = "testpypi"
|
|
|
|
[packages]
|
|
pipenv-test-private-package = {version = "*", index = "testpypi"}
|
|
requests = "*"
|
|
""".strip()
|
|
f.write(contents)
|
|
c = p.pipenv('install')
|
|
assert c.return_code == 0
|
|
c = p.pipenv('lock -r')
|
|
assert c.return_code == 0
|
|
assert '-i https://pypi.org/simple' in c.out.strip()
|
|
assert '--extra-index-url https://test.pypi.org/simple' in c.out.strip()
|
|
|
|
|
|
@pytest.mark.requirements
|
|
@pytest.mark.lock
|
|
@pytest.mark.index
|
|
@pytest.mark.install # private indexes need to be uncached for resolution
|
|
@pytest.mark.needs_internet
|
|
def test_private_index_mirror_lock_requirements(PipenvInstance):
|
|
# Don't use the local fake pypi
|
|
with temp_environ(), PipenvInstance(chdir=True) as p:
|
|
# Using pypi.python.org as pipenv-test-public-package is not
|
|
# included in the local pypi mirror
|
|
mirror_url = os.environ.pop('PIPENV_TEST_INDEX', "https://pypi.kennethreitz.org/simple")
|
|
# os.environ.pop('PIPENV_TEST_INDEX', None)
|
|
with open(p.pipfile_path, 'w') as f:
|
|
contents = """
|
|
[[source]]
|
|
url = "https://pypi.org/simple"
|
|
verify_ssl = true
|
|
name = "pypi"
|
|
|
|
[[source]]
|
|
url = "https://test.pypi.org/simple"
|
|
verify_ssl = true
|
|
name = "testpypi"
|
|
|
|
[packages]
|
|
six = {version = "*", index = "testpypi"}
|
|
requests = "*"
|
|
""".strip()
|
|
f.write(contents)
|
|
c = p.pipenv('install --pypi-mirror {0}'.format(mirror_url))
|
|
assert c.return_code == 0
|
|
c = p.pipenv('lock -r --pypi-mirror {0}'.format(mirror_url))
|
|
assert c.return_code == 0
|
|
assert '-i https://pypi.org/simple' in c.out.strip()
|
|
assert '--extra-index-url https://test.pypi.org/simple' in c.out.strip()
|
|
# Mirror url should not have replaced source URLs
|
|
assert '-i {0}'.format(mirror_url) not in c.out.strip()
|
|
assert '--extra-index-url {}'.format(mirror_url) not in c.out.strip()
|
|
|
|
|
|
@pytest.mark.install
|
|
@pytest.mark.index
|
|
def test_lock_updated_source(PipenvInstance, pypi):
|
|
|
|
with PipenvInstance(pypi=pypi) as p:
|
|
with open(p.pipfile_path, 'w') as f:
|
|
contents = """
|
|
[[source]]
|
|
url = "{url}/${{MY_ENV_VAR}}"
|
|
|
|
[packages]
|
|
requests = "==2.14.0"
|
|
""".strip().format(url=pypi.url)
|
|
f.write(contents)
|
|
|
|
with temp_environ():
|
|
os.environ['MY_ENV_VAR'] = 'simple'
|
|
c = p.pipenv('lock')
|
|
assert c.return_code == 0
|
|
assert 'requests' in p.lockfile['default']
|
|
|
|
with open(p.pipfile_path, 'w') as f:
|
|
contents = """
|
|
[[source]]
|
|
url = "{url}/simple"
|
|
|
|
[packages]
|
|
requests = "==2.14.0"
|
|
""".strip().format(url=pypi.url)
|
|
f.write(contents)
|
|
|
|
c = p.pipenv('lock')
|
|
assert c.return_code == 0
|
|
assert 'requests' in p.lockfile['default']
|
|
|
|
|
|
@pytest.mark.lock
|
|
@pytest.mark.vcs
|
|
@pytest.mark.needs_internet
|
|
def test_lock_editable_vcs_without_install(PipenvInstance, pypi):
|
|
with PipenvInstance(pypi=pypi, chdir=True) as p:
|
|
with open(p.pipfile_path, 'w') as f:
|
|
f.write("""
|
|
[packages]
|
|
requests = {git = "https://github.com/requests/requests.git", ref = "master", editable = true}
|
|
""".strip())
|
|
c = p.pipenv('lock')
|
|
assert c.return_code == 0
|
|
assert 'requests' in p.lockfile['default']
|
|
assert 'idna' in p.lockfile['default']
|
|
assert 'chardet' in p.lockfile['default']
|
|
c = p.pipenv('install')
|
|
assert c.return_code == 0
|
|
|
|
|
|
@pytest.mark.lock
|
|
@pytest.mark.vcs
|
|
@pytest.mark.needs_internet
|
|
def test_lock_editable_vcs_with_ref_in_git(PipenvInstance, pypi):
|
|
with PipenvInstance(pypi=pypi, chdir=True) as p:
|
|
with open(p.pipfile_path, 'w') as f:
|
|
f.write("""
|
|
[packages]
|
|
requests = {git = "https://github.com/requests/requests.git@883caaf", editable = true}
|
|
""".strip())
|
|
c = p.pipenv('lock')
|
|
assert c.return_code == 0
|
|
assert p.lockfile['default']['requests']['git'] == 'https://github.com/requests/requests.git'
|
|
assert p.lockfile['default']['requests']['ref'] == '883caaf145fbe93bd0d208a6b864de9146087312'
|
|
c = p.pipenv('install')
|
|
assert c.return_code == 0
|
|
|
|
|
|
@pytest.mark.lock
|
|
@pytest.mark.vcs
|
|
@pytest.mark.needs_internet
|
|
def test_lock_editable_vcs_with_ref(PipenvInstance, pypi):
|
|
with PipenvInstance(pypi=pypi, chdir=True) as p:
|
|
with open(p.pipfile_path, 'w') as f:
|
|
f.write("""
|
|
[packages]
|
|
requests = {git = "https://github.com/requests/requests.git", ref = "883caaf", editable = true}
|
|
""".strip())
|
|
c = p.pipenv('lock')
|
|
assert c.return_code == 0
|
|
assert p.lockfile['default']['requests']['git'] == 'https://github.com/requests/requests.git'
|
|
assert p.lockfile['default']['requests']['ref'] == '883caaf145fbe93bd0d208a6b864de9146087312'
|
|
c = p.pipenv('install')
|
|
assert c.return_code == 0
|
|
|
|
|
|
@pytest.mark.extras
|
|
@pytest.mark.lock
|
|
@pytest.mark.vcs
|
|
@pytest.mark.needs_internet
|
|
def test_lock_editable_vcs_with_extras_without_install(PipenvInstance, pypi):
|
|
with PipenvInstance(pypi=pypi, chdir=True) as p:
|
|
with open(p.pipfile_path, 'w') as f:
|
|
f.write("""
|
|
[packages]
|
|
requests = {git = "https://github.com/requests/requests.git", editable = true, extras = ["socks"]}
|
|
""".strip())
|
|
c = p.pipenv('lock')
|
|
assert c.return_code == 0
|
|
assert 'requests' in p.lockfile['default']
|
|
assert 'idna' in p.lockfile['default']
|
|
assert 'chardet' in p.lockfile['default']
|
|
assert "socks" in p.lockfile["default"]["requests"]["extras"]
|
|
c = p.pipenv('install')
|
|
assert c.return_code == 0
|
|
|
|
|
|
@pytest.mark.lock
|
|
@pytest.mark.vcs
|
|
@pytest.mark.needs_internet
|
|
def test_lock_editable_vcs_with_markers_without_install(PipenvInstance, pypi):
|
|
with PipenvInstance(pypi=pypi, chdir=True) as p:
|
|
with open(p.pipfile_path, 'w') as f:
|
|
f.write("""
|
|
[packages]
|
|
requests = {git = "https://github.com/requests/requests.git", ref = "master", editable = true, markers = "python_version >= '2.6'"}
|
|
""".strip())
|
|
c = p.pipenv('lock')
|
|
assert c.return_code == 0
|
|
assert 'requests' in p.lockfile['default']
|
|
assert 'idna' in p.lockfile['default']
|
|
assert 'chardet' in p.lockfile['default']
|
|
c = p.pipenv('install')
|
|
assert c.return_code == 0
|
|
|
|
|
|
@pytest.mark.lock
|
|
@pytest.mark.skip(reason="This doesn't work for some reason.")
|
|
def test_lock_respecting_python_version(PipenvInstance, pypi):
|
|
with PipenvInstance(pypi=pypi, chdir=True) as p:
|
|
with open(p.pipfile_path, 'w') as f:
|
|
f.write("""
|
|
[packages]
|
|
django = "*"
|
|
""".strip())
|
|
c = p.pipenv('install ')
|
|
assert c.return_code == 0
|
|
c = p.pipenv('run python --version')
|
|
assert c.return_code == 0
|
|
py_version = c.err.splitlines()[-1].strip().split()[-1]
|
|
django_version = '==2.0.6' if py_version.startswith('3') else '==1.11.13'
|
|
assert py_version == '2.7.14'
|
|
assert p.lockfile['default']['django']['version'] == django_version
|
|
|
|
|
|
@pytest.mark.lock
|
|
@pytest.mark.install
|
|
def test_lockfile_corrupted(PipenvInstance):
|
|
with PipenvInstance() as p:
|
|
with open(p.lockfile_path, 'w') as f:
|
|
f.write('{corrupted}')
|
|
c = p.pipenv('install')
|
|
assert c.return_code == 0
|
|
assert 'Pipfile.lock is corrupted' in c.err
|
|
assert p.lockfile['_meta']
|
|
|
|
|
|
@pytest.mark.lock
|
|
@pytest.mark.install
|
|
def test_lockfile_with_empty_dict(PipenvInstance):
|
|
with PipenvInstance() as p:
|
|
with open(p.lockfile_path, 'w') as f:
|
|
f.write('{}')
|
|
c = p.pipenv('install')
|
|
assert c.return_code == 0
|
|
assert 'Pipfile.lock is corrupted' in c.err
|
|
assert p.lockfile['_meta']
|
|
|
|
|
|
@pytest.mark.lock
|
|
@pytest.mark.install
|
|
def test_lock_with_incomplete_source(PipenvInstance, pypi):
|
|
with PipenvInstance(pypi=pypi, chdir=True) as p:
|
|
with open(p.pipfile_path, 'w') as f:
|
|
f.write("""
|
|
[[source]]
|
|
url = "https://test.pypi.org/simple"
|
|
|
|
[packages]
|
|
requests = "*"
|
|
""")
|
|
c = p.pipenv('install')
|
|
assert c.return_code == 0
|
|
assert p.lockfile['_meta']['sources']
|
|
|
|
|
|
@pytest.mark.lock
|
|
@pytest.mark.install
|
|
def test_lock_no_warnings(PipenvInstance, pypi):
|
|
with PipenvInstance(pypi=pypi, chdir=True) as p:
|
|
os.environ["PYTHONWARNINGS"] = str("once")
|
|
c = p.pipenv("install six")
|
|
assert c.return_code == 0
|
|
c = p.pipenv('run python -c "import warnings; warnings.warn(\\"This is a warning\\", DeprecationWarning); print(\\"hello\\")"')
|
|
assert c.return_code == 0
|
|
assert "Warning" in c.err
|
|
assert "Warning" not in c.out
|
|
assert "hello" in c.out
|
|
|
|
|
|
@pytest.mark.lock
|
|
@pytest.mark.install
|
|
@pytest.mark.skipif(sys.version_info >= (3, 5), reason="scandir doesn't get installed on python 3.5+")
|
|
def test_lock_missing_cache_entries_gets_all_hashes(monkeypatch, PipenvInstance, pypi, tmpdir):
|
|
"""
|
|
Test locking pathlib2 on python2.7 which needs `scandir`, but fails to resolve when
|
|
using a fresh dependency cache.
|
|
"""
|
|
|
|
with monkeypatch.context() as m:
|
|
monkeypatch.setattr("pipenv.patched.piptools.locations.CACHE_DIR", tmpdir.strpath)
|
|
with PipenvInstance(pypi=pypi, chdir=True) as p:
|
|
p._pipfile.add("pathlib2", "*")
|
|
assert "pathlib2" in p.pipfile["packages"]
|
|
c = p.pipenv("install")
|
|
assert c.return_code == 0, c.err
|
|
assert "pathlib2" in p.lockfile["default"]
|
|
assert "scandir" in p.lockfile["default"]
|
|
assert isinstance(p.lockfile["default"]["scandir"]["hashes"], list)
|
|
assert len(p.lockfile["default"]["scandir"]["hashes"]) > 1
|
|
|
|
|
|
@pytest.mark.lock
|
|
@pytest.mark.vcs
|
|
def test_vcs_lock_respects_top_level_pins(PipenvInstance, pypi):
|
|
"""Test that locking VCS dependencies respects top level packages pinned in Pipfiles"""
|
|
|
|
with PipenvInstance(pypi=pypi, chdir=True) as p:
|
|
requests_uri = p._pipfile.get_fixture_path("git/requests").as_uri()
|
|
p._pipfile.add("requests", {
|
|
"editable": True, "git": "{0}".format(requests_uri),
|
|
"ref": "v2.18.4"
|
|
})
|
|
p._pipfile.add("urllib3", "==1.21.1")
|
|
c = p.pipenv("install")
|
|
assert c.return_code == 0
|
|
assert "requests" in p.lockfile["default"]
|
|
assert "git" in p.lockfile["default"]["requests"]
|
|
assert "urllib3" in p.lockfile["default"]
|
|
assert p.lockfile["default"]["urllib3"]["version"] == "==1.21.1"
|