diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..5192d184 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,144 @@ +import json +import os +import warnings + +import pytest + +from pipenv.project import Project +from pipenv.utils import TemporaryDirectory +from pipenv.vendor import delegator +from pipenv.vendor import requests +from pipenv.vendor import six +from pipenv.vendor import toml + + +if six.PY2: + class ResourceWarning(Warning): + pass + + +def check_internet(): + try: + # Kenneth represents the Internet LGTM. + resp = requests.get('http://httpbin.org/ip', timeout=1.0) + resp.raise_for_status() + except Exception: + warnings.warn('Cannot connect to HTTPBin...', ResourceWarning) + warnings.warn('Will skip tests requiring Internet', ResourceWarning) + return False + return True + + +WE_HAVE_INTERNET = check_internet() + + +def pytest_runtest_setup(item): + if item.get_marker('needs_internet') is not None and not WE_HAVE_INTERNET: + pytest.skip('requires internet') + + +def pytest_sessionstart(session): + os.environ['PIPENV_DONT_USE_PYENV'] = '1' + os.environ['PIPENV_IGNORE_VIRTUALENVS'] = '1' + os.environ['PIPENV_VENV_IN_PROJECT'] = '1' + os.environ['PYPI_VENDOR_DIR'] = os.path.join(os.path.dirname(__file__), 'pypi') + + +class _PipenvInstance(object): + """An instance of a Pipenv Project...""" + def __init__(self, pypi=None, pipfile=True, chdir=False): + self.pypi = pypi + self.original_umask = os.umask(0o007) + self.original_dir = os.path.abspath(os.curdir) + self._path = TemporaryDirectory(suffix='-project', prefix='pipenv-') + self.path = self._path.name + # set file creation perms + self.pipfile_path = None + self.chdir = chdir + + if self.pypi: + os.environ['PIPENV_TEST_INDEX'] = '{0}/simple'.format(self.pypi.url) + + if pipfile: + p_path = os.sep.join([self.path, 'Pipfile']) + with open(p_path, 'a'): + os.utime(p_path, None) + + self.chdir = False or chdir + self.pipfile_path = p_path + + def __enter__(self): + if self.chdir: + os.chdir(self.path) + return self + + def __exit__(self, *args): + warn_msg = 'Failed to remove resource: {!r}' + if self.chdir: + os.chdir(self.original_dir) + self.path = None + try: + self._path.cleanup() + except OSError as e: + _warn_msg = warn_msg.format(e) + warnings.warn(_warn_msg, ResourceWarning) + finally: + os.umask(self.original_umask) + + def pipenv(self, cmd, block=True): + if self.pipfile_path: + os.environ['PIPENV_PIPFILE'] = self.pipfile_path + + with TemporaryDirectory(prefix='pipenv-', suffix='-cache') as tempdir: + os.environ['PIPENV_CACHE_DIR'] = tempdir.name + c = delegator.run('pipenv {0}'.format(cmd), block=block) + if 'PIPENV_CACHE_DIR' in os.environ: + del os.environ['PIPENV_CACHE_DIR'] + + if 'PIPENV_PIPFILE' in os.environ: + del os.environ['PIPENV_PIPFILE'] + + # Pretty output for failing tests. + if block: + print('$ pipenv {0}'.format(cmd)) + print(c.out) + print(c.err) + + # Where the action happens. + return c + + @property + def pipfile(self): + p_path = os.sep.join([self.path, 'Pipfile']) + with open(p_path, 'r') as f: + return toml.loads(f.read()) + + @property + def lockfile(self): + p_path = os.sep.join([self.path, 'Pipfile.lock']) + with open(p_path, 'r') as f: + return json.loads(f.read()) + + +@pytest.fixture() +def PipenvInstance(): + return _PipenvInstance + + +@pytest.fixture(scope='module') +def pip_src_dir(request): + old_src_dir = os.environ.get('PIP_SRC', '') + new_src_dir = TemporaryDirectory(prefix='pipenv-', suffix='-testsrc') + os.environ['PIP_SRC'] = new_src_dir.name + + def finalize(): + new_src_dir.cleanup() + os.environ['PIP_SRC'] = old_src_dir + + request.addfinalizer(finalize) + return request + + +@pytest.fixture() +def project(): + return Project() diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 00000000..ad84912b --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,148 @@ +"""Tests to ensure `pipenv --option` works. +""" + +import os +import re + +import pytest + +from pipenv.utils import normalize_drive + + +@pytest.mark.cli +def test_pipenv_where(PipenvInstance, pypi_secure): + with PipenvInstance(pypi=pypi_secure) as p: + assert normalize_drive(p.path) in p.pipenv('--where').out + + +@pytest.mark.cli +def test_pipenv_venv(PipenvInstance): + with PipenvInstance() as p: + p.pipenv('--python python') + venv_path = p.pipenv('--venv').out.strip() + assert os.path.isdir(venv_path) + + +@pytest.mark.cli +def test_pipenv_py(PipenvInstance): + with PipenvInstance() as p: + p.pipenv('--python python') + python = p.pipenv('--py').out.strip() + assert os.path.basename(python).startswith('python') + + +@pytest.mark.cli +def test_pipenv_rm(PipenvInstance): + with PipenvInstance() as p: + p.pipenv('--python python') + venv_path = p.pipenv('--venv').out.strip() + assert os.path.isdir(venv_path) + + assert p.pipenv('--rm').out + assert not os.path.isdir(venv_path) + + +@pytest.mark.cli +def test_pipenv_graph(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi) as p: + p.pipenv('install requests') + assert 'requests' in p.pipenv('graph').out + assert 'requests' in p.pipenv('graph --json').out + + +@pytest.mark.cli +def test_pipenv_graph_reverse(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi) as p: + p.pipenv('install requests==2.18.4') + output = p.pipenv('graph --reverse').out + + requests_dependency = [ + ('certifi', 'certifi>=2017.4.17'), + ('chardet', 'chardet(>=3.0.2,<3.1.0|<3.1.0,>=3.0.2)'), + ('idna', 'idna(>=2.5,<2.7|<2.7,>=2.5)'), + ('urllib3', 'urllib3(>=1.21.1,<1.23|<1.23,>=1.21.1)') + ] + + for dep_name, dep_constraint in requests_dependency: + dep_match = re.search(r'^{}==[\d.]+$'.format(dep_name), output, flags=re.MULTILINE) + dep_requests_match = re.search(r'^ - requests==2.18.4 \[requires: {}\]$'.format(dep_constraint), output, flags=re.MULTILINE) + assert dep_match is not None + assert dep_requests_match is not None + assert dep_requests_match.start() > dep_match.start() + + c = p.pipenv('graph --reverse --json') + assert c.return_code == 1 + assert 'Warning: Using both --reverse and --json together is not supported.' in c.err + + +@pytest.mark.cli +@pytest.mark.needs_internet(reason='required by check') +def test_pipenv_check(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi) as p: + p.pipenv('install requests==1.0.0') + assert 'requests' in p.pipenv('check').out + + +@pytest.mark.cli +def test_venv_envs(PipenvInstance): + with PipenvInstance() as p: + assert p.pipenv('--envs').out + + +@pytest.mark.cli +def test_bare_output(PipenvInstance): + with PipenvInstance() as p: + assert p.pipenv('').out + + +@pytest.mark.cli +def test_help(PipenvInstance): + with PipenvInstance() as p: + assert p.pipenv('--help').out + + +@pytest.mark.cli +def test_man(PipenvInstance): + with PipenvInstance() as p: + c = p.pipenv('--man') + assert c.return_code == 0 or c.err + + +@pytest.mark.cli +def test_install_parse_error(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi) as p: + + # Make sure unparseable packages don't wind up in the pipfile + # Escape $ for shell input + with open(p.pipfile_path, 'w') as f: + contents = """ +[packages] + +[dev-packages] + """.strip() + f.write(contents) + c = p.pipenv('install requests u/\\/p@r\$34b13+pkg') + assert c.return_code != 0 + assert 'u/\\/p@r$34b13+pkg' not in p.pipfile['packages'] + + +@pytest.mark.code +@pytest.mark.check +@pytest.mark.unused +@pytest.mark.skip(reason="non-deterministic") +def test_check_unused(PipenvInstance, pypi): + with PipenvInstance(chdir=True, pypi=pypi) as p: + with open('__init__.py', 'w') as f: + contents = """ +import tablib +import records + """.strip() + f.write(contents) + p.pipenv('install requests') + p.pipenv('install tablib') + p.pipenv('install records') + + assert all(pkg in p.pipfile['packages'] for pkg in ['requests', 'tablib', 'records']) + + c = p.pipenv('check --unused .') + assert 'tablib' not in c.out diff --git a/tests/test_dot_venv.py b/tests/test_dot_venv.py new file mode 100644 index 00000000..bb8a6607 --- /dev/null +++ b/tests/test_dot_venv.py @@ -0,0 +1,85 @@ +import os + +from pipenv.utils import temp_environ, normalize_drive, get_windows_path +from pipenv.vendor import delegator + +import pytest + + +@pytest.mark.dotvenv +def test_venv_in_project(PipenvInstance, pypi): + with temp_environ(): + os.environ['PIPENV_VENV_IN_PROJECT'] = '1' + with PipenvInstance(pypi=pypi) as p: + c = p.pipenv('install requests') + assert c.return_code == 0 + assert normalize_drive(p.path) in p.pipenv('--venv').out + + +@pytest.mark.dotvenv +def test_venv_at_project_root(PipenvInstance): + with temp_environ(): + with PipenvInstance(chdir=True) as p: + os.environ['PIPENV_VENV_IN_PROJECT'] = '1' + c = p.pipenv('install') + assert c.return_code == 0 + assert normalize_drive(p.path) in p.pipenv('--venv').out + del os.environ['PIPENV_VENV_IN_PROJECT'] + os.mkdir('subdir') + os.chdir('subdir') + # should still detect installed + assert normalize_drive(p.path) in p.pipenv('--venv').out + + +@pytest.mark.dotvenv +def test_reuse_previous_venv(PipenvInstance, pypi): + with PipenvInstance(chdir=True, pypi=pypi) as p: + os.mkdir('.venv') + c = p.pipenv('install requests') + assert c.return_code == 0 + assert normalize_drive(p.path) in p.pipenv('--venv').out + + +@pytest.mark.dotvenv +@pytest.mark.install +@pytest.mark.complex +@pytest.mark.shell +@pytest.mark.windows +@pytest.mark.pew +@pytest.mark.skip('Not mocking this.') +def test_shell_nested_venv_in_project(PipenvInstance, pypi, project): + import subprocess + with temp_environ(): + os.environ['PIPENV_VENV_IN_PROJECT'] = '1' + os.environ['PIPENV_IGNORE_VIRTUALENVS'] = '1' + with PipenvInstance(chdir=True, pypi=pypi) as p: + # Signal to pew to look in the project directory for the environment + os.environ['WORKON_HOME'] = p.path + c = p.pipenv('install requests') + assert c.return_code == 0 + assert 'requests' in p.pipfile['packages'] + assert 'requests' in p.lockfile['default'] + # Check that .venv now shows in pew's managed list + pew_list = delegator.run('pewtwo ls') + assert '.venv' in pew_list.out + # Check for the venv directory + c = delegator.run('pewtwo dir .venv') + # Compare pew's virtualenv path to what we expect + venv_path = get_windows_path(project.project_directory, '.venv') + # os.path.normpath will normalize slashes + assert venv_path == normalize_drive(os.path.normpath(c.out.strip())) + # Have pew run 'pip freeze' in the virtualenv + # This is functionally the same as spawning a subshell + # If we can do this we can theoretically make a subshell + # This test doesn't work on *nix + if os.name == 'nt': + process = subprocess.Popen( + 'pewtwo in .venv pip freeze', + shell=True, + universal_newlines=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + out, _ = process.communicate() + assert any(req.startswith('requests') for req in out.splitlines()) is True diff --git a/tests/test_install_basic.py b/tests/test_install_basic.py new file mode 100644 index 00000000..e46cc4e0 --- /dev/null +++ b/tests/test_install_basic.py @@ -0,0 +1,236 @@ +from pipenv.vendor import delegator + +import pytest + +from flaky import flaky + + +@pytest.mark.install +@pytest.mark.setup +@pytest.mark.skip(reason="this doesn't work on travis") +def test_basic_setup(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi) as p: + with PipenvInstance(pipfile=False) as p: + c = p.pipenv('install requests') + assert c.return_code == 0 + + assert 'requests' in p.pipfile['packages'] + assert 'requests' in p.lockfile['default'] + assert 'chardet' in p.lockfile['default'] + assert 'idna' in p.lockfile['default'] + assert 'urllib3' in p.lockfile['default'] + assert 'certifi' in p.lockfile['default'] + + +@pytest.mark.install +@flaky +def test_basic_install(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi) as p: + c = p.pipenv('install requests') + assert c.return_code == 0 + assert 'requests' in p.pipfile['packages'] + assert 'requests' in p.lockfile['default'] + assert 'chardet' in p.lockfile['default'] + assert 'idna' in p.lockfile['default'] + assert 'urllib3' in p.lockfile['default'] + assert 'certifi' in p.lockfile['default'] + + +@pytest.mark.complex +@pytest.mark.lock +@pytest.mark.skip(reason='Does not work unless you can explicitly install into py2') +def test_complex_lock(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi) as p: + c = p.pipenv('install apscheduler') + assert c.return_code == 0 + assert 'apscheduler' in p.pipfile['packages'] + assert 'funcsigs' in p.lockfile[u'default'] + assert 'futures' in p.lockfile[u'default'] + + +@pytest.mark.dev +@pytest.mark.run +@flaky +def test_basic_dev_install(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi) as p: + c = p.pipenv('install requests --dev') + assert c.return_code == 0 + assert 'requests' in p.pipfile['dev-packages'] + assert 'requests' in p.lockfile['develop'] + assert 'chardet' in p.lockfile['develop'] + assert 'idna' in p.lockfile['develop'] + assert 'urllib3' in p.lockfile['develop'] + assert 'certifi' in p.lockfile['develop'] + + c = p.pipenv('run python -m requests.help') + assert c.return_code == 0 + + +@pytest.mark.dev +@pytest.mark.install +@flaky +def test_install_without_dev(PipenvInstance, pypi): + """Ensure that running `pipenv install` doesn't install dev packages""" + with PipenvInstance(pypi=pypi, chdir=True) as p: + with open(p.pipfile_path, 'w') as f: + contents = """ +[packages] +six = "*" + +[dev-packages] +pytz = "*" + """.strip() + f.write(contents) + c = p.pipenv('install') + assert c.return_code == 0 + assert 'six' in p.pipfile['packages'] + assert 'pytz' in p.pipfile['dev-packages'] + assert 'six' in p.lockfile['default'] + assert 'pytz' in p.lockfile['develop'] + c = p.pipenv('run python -c "import pytz"') + assert c.return_code != 0 + c = p.pipenv('run python -c "import six"') + assert c.return_code == 0 + + +@pytest.mark.install +@flaky +def test_install_without_dev_section(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi) as p: + with open(p.pipfile_path, 'w') as f: + contents = """ +[packages] +six = "*" + """.strip() + f.write(contents) + c = p.pipenv('install') + assert c.return_code == 0 + assert 'six' in p.pipfile['packages'] + assert p.pipfile.get('dev-packages', {}) == {} + assert 'six' in p.lockfile['default'] + assert p.lockfile['develop'] == {} + c = p.pipenv('run python -c "import six"') + assert c.return_code == 0 + + +@pytest.mark.extras +@pytest.mark.install +@flaky +def test_extras_install(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi, chdir=True) as p: + c = p.pipenv('install requests[socks]') + assert c.return_code == 0 + assert 'requests' in p.pipfile['packages'] + assert 'extras' in p.pipfile['packages']['requests'] + + assert 'requests' in p.lockfile['default'] + assert 'chardet' in p.lockfile['default'] + assert 'idna' in p.lockfile['default'] + assert 'urllib3' in p.lockfile['default'] + assert 'pysocks' in p.lockfile['default'] + + +@pytest.mark.install +@pytest.mark.pin +@flaky +def test_windows_pinned_pipfile(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi) as p: + with open(p.pipfile_path, 'w') as f: + contents = """ +[packages] +tablib = "<0.12" + """.strip() + f.write(contents) + c = p.pipenv('install') + assert c.return_code == 0 + assert 'tablib' in p.pipfile['packages'] + assert 'tablib' in p.lockfile['default'] + + +@pytest.mark.install +@pytest.mark.resolver +@pytest.mark.backup_resolver +@flaky +def test_backup_resolver(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi) as p: + with open(p.pipfile_path, 'w') as f: + contents = """ +[packages] +"ibm-db-sa-py3" = "==0.3.1-1" + """.strip() + f.write(contents) + + c = p.pipenv('install') + assert c.return_code == 0 + assert 'ibm-db-sa-py3' in p.lockfile['default'] + + +@pytest.mark.run +@pytest.mark.alt +@flaky +def test_alternative_version_specifier(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi) as p: + with open(p.pipfile_path, 'w') as f: + contents = """ +[packages] +requests = {version = "*"} + """.strip() + f.write(contents) + + c = p.pipenv('install') + assert c.return_code == 0 + + assert 'requests' in p.lockfile['default'] + assert 'idna' in p.lockfile['default'] + assert 'urllib3' in p.lockfile['default'] + assert 'certifi' in p.lockfile['default'] + assert 'chardet' in p.lockfile['default'] + + c = p.pipenv('run python -c "import requests; import idna; import certifi;"') + assert c.return_code == 0 + + +@pytest.mark.bad +@pytest.mark.install +def test_bad_packages(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi) as p: + c = p.pipenv('install NotAPackage') + assert c.return_code > 0 + + +@pytest.mark.extras +@pytest.mark.install +@pytest.mark.requirements +@pytest.mark.skip(reason="Not mocking this.") +def test_requirements_to_pipfile(PipenvInstance, pypi): + + with PipenvInstance(pipfile=False, chdir=True, pypi=pypi) as p: + + # Write a requirements file + with open('requirements.txt', 'w') as f: + f.write('requests[socks]==2.18.1\n') + + c = p.pipenv('install') + assert c.return_code == 0 + print(c.out) + print(c.err) + print(delegator.run('ls -l').out) + + # assert stuff in pipfile + assert 'requests' in p.pipfile['packages'] + assert 'extras' in p.pipfile['packages']['requests'] + + # assert stuff in lockfile + assert 'requests' in p.lockfile['default'] + assert 'chardet' in p.lockfile['default'] + assert 'idna' in p.lockfile['default'] + assert 'urllib3' in p.lockfile['default'] + assert 'pysocks' in p.lockfile['default'] + + +@pytest.mark.cli +@pytest.mark.clean +def test_clean_on_empty_venv(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi) as p: + c = p.pipenv('clean') + assert c.return_code == 0 diff --git a/tests/test_install_markers.py b/tests/test_install_markers.py new file mode 100644 index 00000000..16806cf7 --- /dev/null +++ b/tests/test_install_markers.py @@ -0,0 +1,161 @@ +import os +import sys + +from pipenv.utils import temp_environ +from pipenv.vendor import pipfile + +import pytest + +from flaky import flaky + + +py3_only = pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") + + +@pytest.mark.markers +@flaky +def test_package_environment_markers(PipenvInstance, pypi): + + with PipenvInstance(pypi=pypi) as p: + with open(p.pipfile_path, 'w') as f: + contents = """ +[packages] +tablib = {version = "*", markers="os_name=='splashwear'"} + """.strip() + f.write(contents) + + c = p.pipenv('install') + assert c.return_code == 0 + assert 'Ignoring' in c.out + assert 'markers' in p.lockfile['default']['tablib'] + + c = p.pipenv('run python -c "import tablib;"') + assert c.return_code == 1 + + +@pytest.mark.run +@pytest.mark.alt +@pytest.mark.install +@flaky +def test_specific_package_environment_markers(PipenvInstance, pypi): + + with PipenvInstance(pypi=pypi) as p: + with open(p.pipfile_path, 'w') as f: + contents = """ +[packages] +requests = {version = "*", os_name = "== 'splashwear'"} + """.strip() + f.write(contents) + + c = p.pipenv('install') + assert c.return_code == 0 + + assert 'Ignoring' in c.out + assert 'markers' in p.lockfile['default']['requests'] + + c = p.pipenv('run python -c "import requests;"') + assert c.return_code == 1 + + +@pytest.mark.markers +@flaky +def test_top_level_overrides_environment_markers(PipenvInstance, pypi): + """Top-level environment markers should take precedence. + """ + with PipenvInstance(pypi=pypi) as p: + with open(p.pipfile_path, 'w') as f: + contents = """ +[packages] +apscheduler = "*" +funcsigs = {version = "*", os_name = "== 'splashwear'"} + """.strip() + f.write(contents) + + c = p.pipenv('install') + assert c.return_code == 0 + + assert p.lockfile['default']['funcsigs']['markers'] == "os_name == 'splashwear'" + + +@pytest.mark.markers +@pytest.mark.install +@flaky +def test_global_overrides_environment_markers(PipenvInstance, pypi): + """Empty (unconditional) dependency should take precedence. + If a dependency is specified without environment markers, it should + override dependencies with environment markers. In this example, + APScheduler requires funcsigs only on Python 2, but since funcsigs is + also specified as an unconditional dep, its markers should be empty. + """ + with PipenvInstance(pypi=pypi) as p: + with open(p.pipfile_path, 'w') as f: + contents = """ +[packages] +apscheduler = "*" +funcsigs = "*" + """.strip() + f.write(contents) + + c = p.pipenv('install') + assert c.return_code == 0 + + assert p.lockfile['default']['funcsigs'].get('markers', '') == '' + + +@pytest.mark.lock +@pytest.mark.complex +@flaky +@py3_only +def test_resolver_unique_markers(PipenvInstance, pypi): + """vcrpy has a dependency on `yarl` which comes with a marker + of 'python version in "3.4, 3.5, 3.6" - this marker duplicates itself: + + 'yarl; python version in "3.4, 3.5, 3.6"; python version in "3.4, 3.5, 3.6"' + + This verifies that we clean that successfully. + """ + with PipenvInstance(chdir=True) as p: + c = p.pipenv('install vcrpy==1.11.0') + assert c.return_code == 0 + assert 'yarl' in p.lockfile['default'] + yarl = p.lockfile['default']['yarl'] + assert 'markers' in yarl + assert yarl['markers'] == "python_version in '3.4, 3.5, 3.6'" + + +@pytest.mark.project +@flaky +def test_environment_variable_value_does_not_change_hash(PipenvInstance, pypi, project): + with PipenvInstance(chdir=True, pypi=pypi) as p: + with temp_environ(): + with open(p.pipfile_path, 'w') as f: + f.write(""" +[[source]] +url = 'https://${PYPI_USERNAME}:${PYPI_PASSWORD}@pypi.python.org/simple' +verify_ssl = true +name = 'pypi' +[requires] +python_version = '2.7' +[packages] +flask = "==0.12.2" +""") + os.environ['PYPI_USERNAME'] = 'whatever' + os.environ['PYPI_PASSWORD'] = 'pass' + assert project.get_lockfile_hash() is None + + c = p.pipenv('install') + lock_hash = project.get_lockfile_hash() + assert lock_hash is not None + assert lock_hash == project.calculate_pipfile_hash() + + # sanity check on pytest + assert 'PYPI_USERNAME' not in str(pipfile.load(p.pipfile_path)) + assert c.return_code == 0 + assert project.get_lockfile_hash() == project.calculate_pipfile_hash() + + os.environ['PYPI_PASSWORD'] = 'pass2' + assert project.get_lockfile_hash() == project.calculate_pipfile_hash() + + with open(p.pipfile_path, 'a') as f: + f.write('requests = "==2.14.0"\n') + assert project.get_lockfile_hash() != project.calculate_pipfile_hash() diff --git a/tests/test_install_twists.py b/tests/test_install_twists.py new file mode 100644 index 00000000..2fdec67c --- /dev/null +++ b/tests/test_install_twists.py @@ -0,0 +1,236 @@ +import os +import shutil + +try: + import pathlib +except ImportError: + import pathlib2 as pathlib + +from pipenv.utils import mkdir_p + +from pipenv.utils import temp_environ + +import pytest + +from flaky import flaky + + +@pytest.mark.extras +@pytest.mark.install +@pytest.mark.local +@pytest.mark.skip(reason="I'm not mocking this.") +def test_local_extras_install(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi) as p: + setup_py = os.path.join(p.path, 'setup.py') + with open(setup_py, 'w') as fh: + contents = """ +from setuptools import setup, find_packages + +setup( +name='test_pipenv', +version='0.1', +description='Pipenv Test Package', +author='Pipenv Test', +author_email='test@pipenv.package', +license='PIPENV', +packages=find_packages(), +install_requires=['tablib'], +extras_require={'dev': ['flake8', 'pylint']}, +zip_safe=False +) + """.strip() + fh.write(contents) + c = p.pipenv('install .[dev]') + assert c.return_code == 0 + key = [k for k in p.pipfile['packages'].keys()][0] + dep = p.pipfile['packages'][key] + assert dep['path'] == '.' + assert dep['extras'] == ['dev'] + assert key in p.lockfile['default'] + assert 'dev' in p.lockfile['default'][key]['extras'] + + +@pytest.mark.e +@pytest.mark.install +@pytest.mark.skip(reason="this doesn't work on windows") +def test_e_dot(PipenvInstance, pip_src_dir): + with PipenvInstance() as p: + path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + c = p.pipenv('install -e \'{0}\' --dev'.format(path)) + + assert c.return_code == 0 + + key = [k for k in p.pipfile['dev-packages'].keys()][0] + assert 'path' in p.pipfile['dev-packages'][key] + assert 'requests' in p.lockfile['develop'] + + +@pytest.mark.install +@flaky +def test_multiprocess_bug_and_install(PipenvInstance, pypi): + with temp_environ(): + os.environ['PIPENV_MAX_SUBPROCESS'] = '2' + + with PipenvInstance(pypi=pypi, chdir=True) as p: + with open(p.pipfile_path, 'w') as f: + contents = """ +[packages] +pytz = "*" +six = "*" +urllib3 = "*" + """.strip() + f.write(contents) + + c = p.pipenv('install') + assert c.return_code == 0 + + assert 'pytz' in p.lockfile['default'] + assert 'six' in p.lockfile['default'] + assert 'urllib3' in p.lockfile['default'] + + c = p.pipenv('run python -c "import six; import pytz; import urllib3;"') + assert c.return_code == 0 + + +@pytest.mark.sequential +@pytest.mark.install +@flaky +def test_sequential_mode(PipenvInstance, pypi): + + with PipenvInstance(pypi=pypi, chdir=True) as p: + with open(p.pipfile_path, 'w') as f: + contents = """ +[packages] +six = "*" +urllib3 = "*" +pytz = "*" + """.strip() + f.write(contents) + + c = p.pipenv('install --sequential') + assert c.return_code == 0 + + assert 'six' in p.lockfile['default'] + assert 'pytz' in p.lockfile['default'] + assert 'urllib3' in p.lockfile['default'] + + c = p.pipenv('run python -c "import six; import urllib3; import pytz;"') + assert c.return_code == 0 + + +@pytest.mark.install +@pytest.mark.run +def test_normalize_name_install(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi) as p: + with open(p.pipfile_path, 'w') as f: + contents = """ +# Pre comment +[packages] +Requests = "==2.14.0" # Inline comment +""" + f.write(contents) + + c = p.pipenv('install') + assert c.return_code == 0 + + c = p.pipenv('install requests') + assert c.return_code == 0 + assert 'requests' not in p.pipfile['packages'] + assert p.pipfile['packages']['Requests'] == '==2.14.0' + c = p.pipenv('install requests==2.18.4') + assert c.return_code == 0 + assert p.pipfile['packages']['Requests'] == '==2.18.4' + c = p.pipenv('install python_DateUtil') + assert c.return_code == 0 + assert 'python-dateutil' in p.pipfile['packages'] + contents = open(p.pipfile_path).read() + assert '# Pre comment' in contents + assert '# Inline comment' in contents + + +@pytest.mark.files +@pytest.mark.resolver +@pytest.mark.eggs +@flaky +def test_local_package(PipenvInstance, pip_src_dir, pypi): + """This test ensures that local packages (directories with a setup.py) + installed in editable mode have their dependencies resolved as well""" + file_name = 'tablib-0.12.1.tar.gz' + package = 'tablib-0.12.1' + # Not sure where travis/appveyor run tests from + test_dir = os.path.dirname(os.path.abspath(__file__)) + source_path = os.path.abspath(os.path.join(test_dir, 'test_artifacts', file_name)) + with PipenvInstance(chdir=True, pypi=pypi) as p: + # This tests for a bug when installing a zipfile in the current dir + copy_to = os.path.join(p.path, file_name) + shutil.copy(source_path, copy_to) + import tarfile + with tarfile.open(copy_to, 'r:gz') as tgz: + tgz.extractall(path=p.path) + c = p.pipenv('install -e {0}'.format(package)) + assert c.return_code == 0 + assert all(pkg in p.lockfile['default'] for pkg in ['xlrd', 'xlwt', 'pyyaml', 'odfpy']) + + +@pytest.mark.files +@flaky +def test_local_zipfiles(PipenvInstance, pypi): + file_name = 'tablib-0.12.1.tar.gz' + # Not sure where travis/appveyor run tests from + test_dir = os.path.dirname(os.path.abspath(__file__)) + source_path = os.path.abspath(os.path.join(test_dir, 'test_artifacts', file_name)) + + with PipenvInstance(chdir=True, pypi=pypi) as p: + # This tests for a bug when installing a zipfile in the current dir + shutil.copy(source_path, os.path.join(p.path, file_name)) + + c = p.pipenv('install {}'.format(file_name)) + assert c.return_code == 0 + key = [k for k in p.pipfile['packages'].keys()][0] + dep = p.pipfile['packages'][key] + + assert 'file' in dep or 'path' in dep + assert c.return_code == 0 + + key = [k for k in p.lockfile['default'].keys()][0] + dep = p.lockfile['default'][key] + + assert 'file' in dep or 'path' in dep + + +@pytest.mark.files +@flaky +def test_relative_paths(PipenvInstance, pypi): + file_name = 'tablib-0.12.1.tar.gz' + test_dir = os.path.join(os.path.dirname(os.path.abspath(__file__))) + source_path = os.path.abspath(os.path.join(test_dir, 'test_artifacts', file_name)) + + with PipenvInstance(pypi=pypi) as p: + artifact_dir = 'artifacts' + artifact_path = os.path.join(p.path, artifact_dir) + mkdir_p(artifact_path) + shutil.copy(source_path, os.path.join(artifact_path, file_name)) + # Test installing a relative path in a subdirectory + c = p.pipenv('install {}/{}'.format(artifact_dir, file_name)) + key = [k for k in p.pipfile['packages'].keys()][0] + dep = p.pipfile['packages'][key] + + assert 'path' in dep + assert pathlib.Path('.', artifact_dir, file_name) == pathlib.Path(dep['path']) + assert c.return_code == 0 + + +@pytest.mark.install +@pytest.mark.local_file +@flaky +def test_install_local_file_collision(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi) as p: + target_package = 'alembic' + fake_file = os.path.join(p.path, target_package) + with open(fake_file, 'w') as f: + f.write('') + c = p.pipenv('install {}'.format(target_package)) + assert c.return_code == 0 + assert target_package in p.pipfile['packages'] + assert p.pipfile['packages'][target_package] == '*' + assert target_package in p.lockfile['default'] diff --git a/tests/test_install_uri.py b/tests/test_install_uri.py new file mode 100644 index 00000000..347b0d5f --- /dev/null +++ b/tests/test_install_uri.py @@ -0,0 +1,94 @@ +import pytest + +from flaky import flaky + + +@pytest.mark.vcs +@pytest.mark.install +@pytest.mark.needs_internet +@flaky +def test_basic_vcs_install(PipenvInstance, pip_src_dir, pypi): + with PipenvInstance(pypi=pypi, chdir=True) as p: + c = p.pipenv('install git+https://github.com/benjaminp/six.git#egg=six') + assert c.return_code == 0 + # edge case where normal package starts with VCS name shouldn't be flagged as vcs + c = p.pipenv('install gitdb2') + assert c.return_code == 0 + assert all(package in p.pipfile['packages'] for package in ['six', 'gitdb2']) + assert 'git' in p.pipfile['packages']['six'] + assert p.lockfile['default']['six'] == {"git": "https://github.com/benjaminp/six.git"} + assert 'gitdb2' in p.lockfile['default'] + + +@pytest.mark.files +@pytest.mark.urls +@pytest.mark.needs_internet +@flaky +def test_urls_work(PipenvInstance, pypi, pip_src_dir): + with PipenvInstance(pypi=pypi) as p: + c = p.pipenv('install https://github.com/divio/django-cms/archive/release/3.4.x.zip') + assert c.return_code == 0 + + dep = list(p.pipfile['packages'].values())[0] + assert 'file' in dep, p.pipfile + + dep = list(p.lockfile['default'].values())[0] + assert 'file' in dep, p.lockfile + + +@pytest.mark.files +@pytest.mark.urls +@pytest.mark.needs_internet +@flaky +def test_install_remote_requirements(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi) as p: + # using a github hosted requirements.txt file + c = p.pipenv('install -r https://raw.githubusercontent.com/kennethreitz/pipenv/3688148ac7cfecefb085c474b092c31d791952c1/tests/test_artifacts/requirements.txt') + + assert c.return_code == 0 + # check Pipfile with versions + assert 'requests' in p.pipfile['packages'] + assert p.pipfile['packages']['requests'] == u'==2.18.4' + assert 'records' in p.pipfile['packages'] + assert p.pipfile['packages']['records'] == u'==0.5.2' + + # check Pipfile.lock + assert 'requests' in p.lockfile['default'] + assert 'records' in p.lockfile['default'] + + +@pytest.mark.e +@pytest.mark.vcs +@pytest.mark.install +@pytest.mark.needs_internet +@flaky +def test_editable_vcs_install(PipenvInstance, pip_src_dir, pypi): + with PipenvInstance(pypi=pypi) as p: + c = p.pipenv('install -e git+https://github.com/requests/requests.git#egg=requests') + assert c.return_code == 0 + assert 'requests' in p.pipfile['packages'] + assert 'git' in p.pipfile['packages']['requests'] + assert 'editable' in p.pipfile['packages']['requests'] + assert 'editable' in p.lockfile['default']['requests'] + assert 'chardet' in p.lockfile['default'] + assert 'idna' in p.lockfile['default'] + assert 'urllib3' in p.lockfile['default'] + assert 'certifi' in p.lockfile['default'] + + +@pytest.mark.install +@pytest.mark.vcs +@pytest.mark.tablib +@pytest.mark.needs_internet +@flaky +def test_install_editable_git_tag(PipenvInstance, pip_src_dir): + # This uses the real PyPI since we need Internet to access the Git + # dependency anyway. + with PipenvInstance() as p: + c = p.pipenv('install -e git+https://github.com/benjaminp/six.git@1.11.0#egg=six') + assert c.return_code == 0 + assert 'six' in p.pipfile['packages'] + assert 'six' in p.lockfile['default'] + assert 'git' in p.lockfile['default']['six'] + assert p.lockfile['default']['six']['git'] == 'https://github.com/benjaminp/six.git' + assert 'ref' in p.lockfile['default']['six'] diff --git a/tests/test_lock.py b/tests/test_lock.py new file mode 100644 index 00000000..b157e12a --- /dev/null +++ b/tests/test_lock.py @@ -0,0 +1,152 @@ +import pytest + +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 +@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 +@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'] diff --git a/tests/test_pipenv.py b/tests/test_pipenv.py index 3e71a239..49d6055f 100644 --- a/tests/test_pipenv.py +++ b/tests/test_pipenv.py @@ -1,1374 +1,59 @@ -import os -import sys -import re -import shutil -import json -import pytest -import warnings +"""Misc. tests that don't fit anywhere. + +XXX: Try our best to reduce tests in this file. +""" + from pipenv.core import activate_virtualenv -from pipenv.utils import ( - temp_environ, get_windows_path, mkdir_p, normalize_drive, TemporaryDirectory -) -from pipenv.vendor import toml -from pipenv.vendor import delegator -from pipenv.vendor import requests -from pipenv.patched import pipfile -from pipenv.project import Project -from pipenv.vendor.six import PY2 -from flaky import flaky -if PY2: - class ResourceWarning(Warning): - pass -try: - from pathlib import Path -except ImportError: - from pipenv.vendor.pathlib2 import Path -os.environ['PIPENV_DONT_USE_PYENV'] = '1' -os.environ['PIPENV_IGNORE_VIRTUALENVS'] = '1' -os.environ['PIPENV_VENV_IN_PROJECT'] = '1' -os.environ['PYPI_VENDOR_DIR'] = os.path.sep.join([os.path.dirname(__file__), 'pypi']) +import pytest -def check_internet(): - try: - # Kenneth represents the Internet LGTM. - resp = requests.get('http://httpbin.org/ip', timeout=1.0) - resp.raise_for_status() - except Exception: - warnings.warn('Cannot connect to HTTPBin...', ResourceWarning) - warnings.warn('Will skip tests requiring Internet', ResourceWarning) - return False - return True +@pytest.mark.code +@pytest.mark.install +@pytest.mark.skip(reason='non deterministic') +def test_code_import_manual(PipenvInstance): + with PipenvInstance(chdir=True) as p: + with open('t.py', 'w') as f: + f.write('import requests') + p.pipenv('install -c .') + assert 'requests' in p.pipfile['packages'] -WE_HAVE_INTERNET = check_internet() +@pytest.mark.code +@pytest.mark.virtualenv +@pytest.mark.project +def test_activate_virtualenv_no_source(project): + command = activate_virtualenv(source=False) + venv = project.virtualenv_location + assert command == '{0}/bin/activate'.format(venv) -needs_internet = pytest.mark.skipif(not WE_HAVE_INTERNET, reason='requires internet') -py3_only = pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") -nix_only = pytest.mark.skipif(os.name != 'nt', reason="doesn't run on windows") +@pytest.mark.lock +@pytest.mark.deploy +@pytest.mark.cli +def test_deploy_works(PipenvInstance, pypi): -@pytest.fixture(scope='module') -def pip_src_dir(request): - old_src_dir = os.environ.get('PIP_SRC', '') - new_src_dir = TemporaryDirectory(prefix='pipenv-', suffix='-testsrc') - os.environ['PIP_SRC'] = new_src_dir.name - - def finalize(): - new_src_dir.cleanup() - os.environ['PIP_SRC'] = old_src_dir - - request.addfinalizer(finalize) - return request - - -VERBOSE_COMMANDS = ('install', 'lock', 'uninstall') - - -class PipenvInstance(object): - """An instance of a Pipenv Project...""" - def __init__(self, pypi=None, pipfile=True, chdir=False): - self.pypi = pypi - self.original_umask = os.umask(0o007) - self.original_dir = os.path.abspath(os.curdir) - self._path = TemporaryDirectory(suffix='-project', prefix='pipenv-') - self.path = self._path.name - # set file creation perms - self.pipfile_path = None - self.chdir = chdir - - if self.pypi: - os.environ['PIPENV_TEST_INDEX'] = '{0}/simple'.format(self.pypi.url) - - if pipfile: - p_path = os.sep.join([self.path, 'Pipfile']) - with open(p_path, 'a'): - os.utime(p_path, None) - - self.chdir = False or chdir - self.pipfile_path = p_path - - def __enter__(self): - if self.chdir: - os.chdir(self.path) - return self - - def __exit__(self, *args): - warn_msg = 'Failed to remove resource: {!r}' - if self.chdir: - os.chdir(self.original_dir) - self.path = None - try: - self._path.cleanup() - except OSError as e: - _warn_msg = warn_msg.format(e) - warnings.warn(_warn_msg, ResourceWarning) - finally: - os.umask(self.original_umask) - - def pipenv(self, cmd, block=True): - if self.pipfile_path: - os.environ['PIPENV_PIPFILE'] = self.pipfile_path - - with TemporaryDirectory(prefix='pipenv-', suffix='-cache') as tempdir: - os.environ['PIPENV_CACHE_DIR'] = tempdir.name - c = delegator.run('pipenv {0}'.format(cmd), block=block) - if 'PIPENV_CACHE_DIR' in os.environ: - del os.environ['PIPENV_CACHE_DIR'] - - if 'PIPENV_PIPFILE' in os.environ: - del os.environ['PIPENV_PIPFILE'] - - # Pretty output for failing tests. - if block: - print('$ pipenv {0}'.format(cmd)) - print(c.out) - print(c.err) - - # Where the action happens. - return c - - @property - def pipfile(self): - p_path = os.sep.join([self.path, 'Pipfile']) - with open(p_path, 'r') as f: - return toml.loads(f.read()) - - @property - def lockfile(self): - p_path = os.sep.join([self.path, 'Pipfile.lock']) - with open(p_path, 'r') as f: - return json.loads(f.read()) - - -class TestPipenv: - """The ultimate testing class.""" - - @pytest.mark.cli - def test_pipenv_where(self, pypi_secure): - with PipenvInstance(pypi=pypi_secure) as p: - assert normalize_drive(p.path) in p.pipenv('--where').out - - @pytest.mark.cli - def test_pipenv_venv(self): - with PipenvInstance() as p: - p.pipenv('--python python') - venv_path = p.pipenv('--venv').out.strip() - assert os.path.isdir(venv_path) - - @pytest.mark.cli - def test_pipenv_py(self): - with PipenvInstance() as p: - p.pipenv('--python python') - python = p.pipenv('--py').out.strip() - assert os.path.basename(python).startswith('python') - - @pytest.mark.cli - def test_pipenv_rm(self): - with PipenvInstance() as p: - p.pipenv('--python python') - venv_path = p.pipenv('--venv').out.strip() - assert os.path.isdir(venv_path) - - assert p.pipenv('--rm').out - assert not os.path.isdir(venv_path) - - @pytest.mark.cli - def test_pipenv_graph(self, pypi): - with PipenvInstance(pypi=pypi) as p: - p.pipenv('install requests') - assert 'requests' in p.pipenv('graph').out - assert 'requests' in p.pipenv('graph --json').out - - @pytest.mark.cli - def test_pipenv_graph_reverse(self, pypi): - with PipenvInstance(pypi=pypi) as p: - p.pipenv('install requests==2.18.4') - output = p.pipenv('graph --reverse').out - - requests_dependency = [ - ('certifi', 'certifi>=2017.4.17'), - ('chardet', 'chardet(>=3.0.2,<3.1.0|<3.1.0,>=3.0.2)'), - ('idna', 'idna(>=2.5,<2.7|<2.7,>=2.5)'), - ('urllib3', 'urllib3(>=1.21.1,<1.23|<1.23,>=1.21.1)') - ] - - for dep_name, dep_constraint in requests_dependency: - dep_match = re.search(r'^{}==[\d.]+$'.format(dep_name), output, flags=re.MULTILINE) - dep_requests_match = re.search(r'^ - requests==2.18.4 \[requires: {}\]$'.format(dep_constraint), output, flags=re.MULTILINE) - assert dep_match is not None - assert dep_requests_match is not None - assert dep_requests_match.start() > dep_match.start() - - c = p.pipenv('graph --reverse --json') - assert c.return_code == 1 - assert 'Warning: Using both --reverse and --json together is not supported.' in c.err - - @pytest.mark.cli - @needs_internet - def test_pipenv_check(self, pypi): - with PipenvInstance(pypi=pypi) as p: - p.pipenv('install requests==1.0.0') - assert 'requests' in p.pipenv('check').out - - @pytest.mark.cli - def test_venv_envs(self): - with PipenvInstance() as p: - assert p.pipenv('--envs').out - - @pytest.mark.cli - def test_bare_output(self): - with PipenvInstance() as p: - assert p.pipenv('').out - - @pytest.mark.cli - def test_help(self): - with PipenvInstance() as p: - assert p.pipenv('--help').out - - @pytest.mark.cli - def test_man(self): - with PipenvInstance() as p: - c = p.pipenv('--man') - assert c.return_code == 0 or c.err - - @pytest.mark.cli - def test_install_parse_error(self, pypi): - with PipenvInstance(pypi=pypi) as p: - - # Make sure unparseable packages don't wind up in the pipfile - # Escape $ for shell input - with open(p.pipfile_path, 'w') as f: - contents = """ -[packages] - -[dev-packages] - """.strip() - f.write(contents) - c = p.pipenv('install requests u/\\/p@r\$34b13+pkg') - assert c.return_code != 0 - assert 'u/\\/p@r$34b13+pkg' not in p.pipfile['packages'] - - @pytest.mark.install - @pytest.mark.setup - @pytest.mark.skip(reason="this doesn't work on travis") - def test_basic_setup(self, pypi): - with PipenvInstance(pypi=pypi) as p: - with PipenvInstance(pipfile=False) as p: - c = p.pipenv('install requests') - assert c.return_code == 0 - - assert 'requests' in p.pipfile['packages'] - assert 'requests' in p.lockfile['default'] - assert 'chardet' in p.lockfile['default'] - assert 'idna' in p.lockfile['default'] - assert 'urllib3' in p.lockfile['default'] - assert 'certifi' in p.lockfile['default'] - - @pytest.mark.install - @flaky - def test_basic_install(self, pypi): - with PipenvInstance(pypi=pypi) as p: - c = p.pipenv('install requests') - assert c.return_code == 0 - assert 'requests' in p.pipfile['packages'] - assert 'requests' in p.lockfile['default'] - assert 'chardet' in p.lockfile['default'] - assert 'idna' in p.lockfile['default'] - assert 'urllib3' in p.lockfile['default'] - assert 'certifi' in p.lockfile['default'] - - @pytest.mark.complex - @pytest.mark.lock - @pytest.mark.skip(reason='Does not work unless you can explicitly install into py2') - def test_complex_lock(self, pypi): - with PipenvInstance(pypi=pypi) as p: - c = p.pipenv('install apscheduler') - assert c.return_code == 0 - assert 'apscheduler' in p.pipfile['packages'] - assert 'funcsigs' in p.lockfile[u'default'] - assert 'futures' in p.lockfile[u'default'] - - @pytest.mark.dev - @pytest.mark.run - @flaky - def test_basic_dev_install(self, pypi): - with PipenvInstance(pypi=pypi) as p: - c = p.pipenv('install requests --dev') - assert c.return_code == 0 - assert 'requests' in p.pipfile['dev-packages'] - assert 'requests' in p.lockfile['develop'] - assert 'chardet' in p.lockfile['develop'] - assert 'idna' in p.lockfile['develop'] - assert 'urllib3' in p.lockfile['develop'] - assert 'certifi' in p.lockfile['develop'] - - c = p.pipenv('run python -m requests.help') - assert c.return_code == 0 - - @pytest.mark.dev - @pytest.mark.install - @flaky - def test_install_without_dev(self, pypi): - """Ensure that running `pipenv install` doesn't install dev packages""" - with PipenvInstance(pypi=pypi, chdir=True) as p: - with open(p.pipfile_path, 'w') as f: - contents = """ -[packages] -six = "*" - -[dev-packages] -pytz = "*" - """.strip() - f.write(contents) - c = p.pipenv('install') - assert c.return_code == 0 - assert 'six' in p.pipfile['packages'] - assert 'pytz' in p.pipfile['dev-packages'] - assert 'six' in p.lockfile['default'] - assert 'pytz' in p.lockfile['develop'] - c = p.pipenv('run python -c "import pytz"') - assert c.return_code != 0 - c = p.pipenv('run python -c "import six"') - assert c.return_code == 0 - - @pytest.mark.install - @flaky - def test_install_without_dev_section(self, pypi): - with PipenvInstance(pypi=pypi) as p: - with open(p.pipfile_path, 'w') as f: - contents = """ -[packages] -six = "*" - """.strip() - f.write(contents) - c = p.pipenv('install') - assert c.return_code == 0 - assert 'six' in p.pipfile['packages'] - assert p.pipfile.get('dev-packages', {}) == {} - assert 'six' in p.lockfile['default'] - assert p.lockfile['develop'] == {} - c = p.pipenv('run python -c "import six"') - assert c.return_code == 0 - - @pytest.mark.run - @pytest.mark.uninstall - @pytest.mark.install - def test_uninstall(self, pypi): - with PipenvInstance(pypi=pypi) as p: - c = p.pipenv('install requests') - assert c.return_code == 0 - assert 'requests' in p.pipfile['packages'] - assert 'requests' in p.lockfile['default'] - assert 'chardet' in p.lockfile['default'] - assert 'idna' in p.lockfile['default'] - assert 'urllib3' in p.lockfile['default'] - assert 'certifi' in p.lockfile['default'] - - c = p.pipenv('uninstall requests') - assert c.return_code == 0 - assert 'requests' not in p.pipfile['dev-packages'] - assert 'requests' not in p.lockfile['develop'] - assert 'chardet' not in p.lockfile['develop'] - assert 'idna' not in p.lockfile['develop'] - assert 'urllib3' not in p.lockfile['develop'] - assert 'certifi' not in p.lockfile['develop'] - - c = p.pipenv('run python -m requests.help') - assert c.return_code > 0 - - @pytest.mark.files - @pytest.mark.uninstall - @pytest.mark.install - def test_uninstall_all_local_files(self): - file_name = 'tablib-0.12.1.tar.gz' - # Not sure where travis/appveyor run tests from - test_dir = os.path.dirname(os.path.abspath(__file__)) - source_path = os.path.abspath(os.path.join(test_dir, 'test_artifacts', file_name)) - - with PipenvInstance() as p: - shutil.copy(source_path, os.path.join(p.path, file_name)) - os.mkdir(os.path.join(p.path, "tablib")) - c = p.pipenv('install {}'.format(file_name)) - assert c.return_code == 0 - c = p.pipenv('uninstall --all') - assert c.return_code == 0 - assert 'tablib' in c.out - assert 'tablib' not in p.pipfile['packages'] - - @pytest.mark.run - @pytest.mark.uninstall - @pytest.mark.install - def test_uninstall_all_dev(self, pypi): - with PipenvInstance(pypi=pypi) as p: - c = p.pipenv('install --dev requests six') - assert c.return_code == 0 - - c = p.pipenv('install pytz') - assert c.return_code == 0 - - assert 'pytz' in p.pipfile['packages'] - assert 'requests' in p.pipfile['dev-packages'] - assert 'six' in p.pipfile['dev-packages'] - assert 'pytz' in p.lockfile['default'] - assert 'requests' in p.lockfile['develop'] - assert 'six' in p.lockfile['develop'] - - c = p.pipenv('uninstall --all-dev') - assert c.return_code == 0 - assert 'requests' not in p.pipfile['dev-packages'] - assert 'six' not in p.pipfile['dev-packages'] - assert 'requests' not in p.lockfile['develop'] - assert 'six' not in p.lockfile['develop'] - assert 'pytz' in p.pipfile['packages'] - assert 'pytz' in p.lockfile['default'] - - c = p.pipenv('run python -m requests.help') - assert c.return_code > 0 - - c = p.pipenv('run python -c "import pytz"') - assert c.return_code == 0 - - @pytest.mark.extras - @pytest.mark.install - @flaky - def test_extras_install(self, pypi): - with PipenvInstance(pypi=pypi, chdir=True) as p: - c = p.pipenv('install requests[socks]') - assert c.return_code == 0 - assert 'requests' in p.pipfile['packages'] - assert 'extras' in p.pipfile['packages']['requests'] - - assert 'requests' in p.lockfile['default'] - assert 'chardet' in p.lockfile['default'] - assert 'idna' in p.lockfile['default'] - assert 'urllib3' in p.lockfile['default'] - assert 'pysocks' in p.lockfile['default'] - - @pytest.mark.extras - @pytest.mark.install - @pytest.mark.local - @pytest.mark.skip(reason="I'm not mocking this.") - def test_local_extras_install(self, pypi): - with PipenvInstance(pypi=pypi) as p: - setup_py = os.path.join(p.path, 'setup.py') - with open(setup_py, 'w') as fh: - contents = """ -from setuptools import setup, find_packages - -setup( - name='test_pipenv', - version='0.1', - description='Pipenv Test Package', - author='Pipenv Test', - author_email='test@pipenv.package', - license='PIPENV', - packages=find_packages(), - install_requires=['tablib'], - extras_require={'dev': ['flake8', 'pylint']}, - zip_safe=False -) - """.strip() - fh.write(contents) - c = p.pipenv('install .[dev]') - assert c.return_code == 0 - key = [k for k in p.pipfile['packages'].keys()][0] - dep = p.pipfile['packages'][key] - assert dep['path'] == '.' - assert dep['extras'] == ['dev'] - assert key in p.lockfile['default'] - assert 'dev' in p.lockfile['default'][key]['extras'] - - @pytest.mark.vcs - @pytest.mark.install - @needs_internet - @flaky - def test_basic_vcs_install(self, pip_src_dir, pypi): - with PipenvInstance(pypi=pypi, chdir=True) as p: - c = p.pipenv('install git+https://github.com/benjaminp/six.git#egg=six') - assert c.return_code == 0 - # edge case where normal package starts with VCS name shouldn't be flagged as vcs - c = p.pipenv('install gitdb2') - assert c.return_code == 0 - assert all(package in p.pipfile['packages'] for package in ['six', 'gitdb2']) - assert 'git' in p.pipfile['packages']['six'] - assert p.lockfile['default']['six'] == {"git": "https://github.com/benjaminp/six.git"} - assert 'gitdb2' in p.lockfile['default'] - - @pytest.mark.e - @pytest.mark.vcs - @pytest.mark.install - @needs_internet - @flaky - def test_editable_vcs_install(self, pip_src_dir, pypi): - with PipenvInstance(pypi=pypi) as p: - c = p.pipenv('install -e git+https://github.com/requests/requests.git#egg=requests') - assert c.return_code == 0 - assert 'requests' in p.pipfile['packages'] - assert 'git' in p.pipfile['packages']['requests'] - assert 'editable' in p.pipfile['packages']['requests'] - assert 'editable' in p.lockfile['default']['requests'] - assert 'chardet' in p.lockfile['default'] - assert 'idna' in p.lockfile['default'] - assert 'urllib3' in p.lockfile['default'] - assert 'certifi' in p.lockfile['default'] - - @pytest.mark.install - @pytest.mark.pin - @flaky - def test_windows_pinned_pipfile(self, pypi): - with PipenvInstance(pypi=pypi) as p: - with open(p.pipfile_path, 'w') as f: - contents = """ -[packages] -tablib = "<0.12" - """.strip() - f.write(contents) - c = p.pipenv('install') - assert c.return_code == 0 - assert 'tablib' in p.pipfile['packages'] - assert 'tablib' in p.lockfile['default'] - - @pytest.mark.install - @flaky - def test_multiprocess_bug_and_install(self, pypi): - with temp_environ(): - os.environ['PIPENV_MAX_SUBPROCESS'] = '2' - - with PipenvInstance(pypi=pypi, chdir=True) as p: - with open(p.pipfile_path, 'w') as f: - contents = """ -[packages] -pytz = "*" -six = "*" -urllib3 = "*" - """.strip() - f.write(contents) - - c = p.pipenv('install') - assert c.return_code == 0 - - assert 'pytz' in p.lockfile['default'] - assert 'six' in p.lockfile['default'] - assert 'urllib3' in p.lockfile['default'] - - c = p.pipenv('run python -c "import six; import pytz; import urllib3;"') - assert c.return_code == 0 - - @pytest.mark.sequential - @pytest.mark.install - @flaky - def test_sequential_mode(self, pypi): - - with PipenvInstance(pypi=pypi, chdir=True) as p: - with open(p.pipfile_path, 'w') as f: - contents = """ -[packages] -six = "*" -urllib3 = "*" -pytz = "*" - """.strip() - f.write(contents) - - c = p.pipenv('install --sequential') - assert c.return_code == 0 - - assert 'six' in p.lockfile['default'] - assert 'pytz' in p.lockfile['default'] - assert 'urllib3' in p.lockfile['default'] - - c = p.pipenv('run python -c "import six; import urllib3; import pytz;"') - assert c.return_code == 0 - - @pytest.mark.install - @pytest.mark.run - def test_normalize_name_install(self, pypi): - with PipenvInstance(pypi=pypi) as p: - with open(p.pipfile_path, 'w') as f: - contents = """ -# Pre comment -[packages] -Requests = "==2.14.0" # Inline comment -""" - f.write(contents) - - c = p.pipenv('install') - assert c.return_code == 0 - - c = p.pipenv('install requests') - assert c.return_code == 0 - assert 'requests' not in p.pipfile['packages'] - assert p.pipfile['packages']['Requests'] == '==2.14.0' - c = p.pipenv('install requests==2.18.4') - assert c.return_code == 0 - assert p.pipfile['packages']['Requests'] == '==2.18.4' - c = p.pipenv('install python_DateUtil') - assert c.return_code == 0 - assert 'python-dateutil' in p.pipfile['packages'] - contents = open(p.pipfile_path).read() - assert '# Pre comment' in contents - assert '# Inline comment' in contents - - @pytest.mark.uninstall - @pytest.mark.run - def test_normalize_name_uninstall(self, pypi): - with PipenvInstance(pypi=pypi) as p: - with open(p.pipfile_path, 'w') as f: - contents = """ -# Pre comment -[packages] -Requests = "*" -python_DateUtil = "*" # Inline comment -""" - f.write(contents) - - c = p.pipenv('install') - assert c.return_code == 0 - - c = p.pipenv('uninstall python_dateutil') - assert 'Requests' in p.pipfile['packages'] - assert 'python_DateUtil' not in p.pipfile['packages'] - contents = open(p.pipfile_path).read() - assert '# Pre comment' in contents - assert '# Inline comment' in contents - - @pytest.mark.install - @pytest.mark.resolver - @pytest.mark.backup_resolver - @needs_internet - @flaky - def test_backup_resolver(self): - with PipenvInstance() as p: - with open(p.pipfile_path, 'w') as f: - contents = """ -[packages] -"ibm-db-sa-py3" = "==0.3.1-1" - """.strip() - f.write(contents) - - c = p.pipenv('install') - assert c.return_code == 0 - assert 'ibm-db-sa-py3' in p.lockfile['default'] - - @pytest.mark.markers - @flaky - def test_package_environment_markers(self, pypi): - - with PipenvInstance(pypi=pypi) as p: - with open(p.pipfile_path, 'w') as f: - contents = """ -[packages] -tablib = {version = "*", markers="os_name=='splashwear'"} - """.strip() - f.write(contents) - - c = p.pipenv('install') - assert c.return_code == 0 - assert 'Ignoring' in c.out - assert 'markers' in p.lockfile['default']['tablib'] - - c = p.pipenv('run python -c "import tablib;"') - assert c.return_code == 1 - - @pytest.mark.run - @pytest.mark.alt - @pytest.mark.install - @flaky - def test_specific_package_environment_markers(self, pypi): - - with PipenvInstance(pypi=pypi) as p: - with open(p.pipfile_path, 'w') as f: - contents = """ -[packages] -requests = {version = "*", os_name = "== 'splashwear'"} - """.strip() - f.write(contents) - - c = p.pipenv('install') - assert c.return_code == 0 - - assert 'Ignoring' in c.out - assert 'markers' in p.lockfile['default']['requests'] - - c = p.pipenv('run python -c "import requests;"') - assert c.return_code == 1 - - @pytest.mark.markers - @flaky - def test_top_level_overrides_environment_markers(self, pypi): - """Top-level environment markers should take precedence. - """ - with PipenvInstance(pypi=pypi) as p: - with open(p.pipfile_path, 'w') as f: - contents = """ -[packages] -apscheduler = "*" -funcsigs = {version = "*", os_name = "== 'splashwear'"} - """.strip() - f.write(contents) - - c = p.pipenv('install') - assert c.return_code == 0 - - assert p.lockfile['default']['funcsigs']['markers'] == "os_name == 'splashwear'" - - @pytest.mark.markers - @pytest.mark.install - @flaky - def test_global_overrides_environment_markers(self, pypi): - """Empty (unconditional) dependency should take precedence. - If a dependency is specified without environment markers, it should - override dependencies with environment markers. In this example, - APScheduler requires funcsigs only on Python 2, but since funcsigs is - also specified as an unconditional dep, its markers should be empty. - """ - with PipenvInstance(pypi=pypi) as p: - with open(p.pipfile_path, 'w') as f: - contents = """ -[packages] -apscheduler = "*" -funcsigs = "*" - """.strip() - f.write(contents) - - c = p.pipenv('install') - assert c.return_code == 0 - - assert p.lockfile['default']['funcsigs'].get('markers', '') == '' - - @pytest.mark.install - @pytest.mark.vcs - @pytest.mark.tablib - @needs_internet - @flaky - def test_install_editable_git_tag(self, pip_src_dir): - # This uses the real PyPI since we need Internet to access the Git - # dependency anyway. - with PipenvInstance() as p: - c = p.pipenv('install -e git+https://github.com/benjaminp/six.git@1.11.0#egg=six') - assert c.return_code == 0 - assert 'six' in p.pipfile['packages'] - assert 'six' in p.lockfile['default'] - assert 'git' in p.lockfile['default']['six'] - assert p.lockfile['default']['six']['git'] == 'https://github.com/benjaminp/six.git' - assert 'ref' in p.lockfile['default']['six'] - - @pytest.mark.run - @pytest.mark.alt - @flaky - def test_alternative_version_specifier(self, pypi): - - with PipenvInstance(pypi=pypi) as p: - with open(p.pipfile_path, 'w') as f: - contents = """ -[packages] -requests = {version = "*"} - """.strip() - f.write(contents) - - c = p.pipenv('install') - assert c.return_code == 0 - - assert 'requests' in p.lockfile['default'] - assert 'idna' in p.lockfile['default'] - assert 'urllib3' in p.lockfile['default'] - assert 'certifi' in p.lockfile['default'] - assert 'chardet' in p.lockfile['default'] - - c = p.pipenv('run python -c "import requests; import idna; import certifi;"') - assert c.return_code == 0 - - @pytest.mark.bad - @pytest.mark.install - def test_bad_packages(self, pypi): - - with PipenvInstance(pypi=pypi) as p: - c = p.pipenv('install NotAPackage') - assert c.return_code > 0 - - @pytest.mark.dotvenv - def test_venv_in_project(self, pypi): - - with temp_environ(): - os.environ['PIPENV_VENV_IN_PROJECT'] = '1' - with PipenvInstance(pypi=pypi) as p: - c = p.pipenv('install requests') - assert c.return_code == 0 - - assert normalize_drive(p.path) in p.pipenv('--venv').out - - @pytest.mark.dotvenv - def test_venv_at_project_root(self): - - with temp_environ(): - with PipenvInstance(chdir=True) as p: - os.environ['PIPENV_VENV_IN_PROJECT'] = '1' - c = p.pipenv('install') - assert c.return_code == 0 - assert normalize_drive(p.path) in p.pipenv('--venv').out - del os.environ['PIPENV_VENV_IN_PROJECT'] - os.mkdir('subdir') - os.chdir('subdir') - # should still detect installed - assert normalize_drive(p.path) in p.pipenv('--venv').out - - @pytest.mark.dotvenv - def test_reuse_previous_venv(self, pypi): - with PipenvInstance(chdir=True, pypi=pypi) as p: - os.mkdir('.venv') - c = p.pipenv('install requests') - assert c.return_code == 0 - - assert normalize_drive(p.path) in p.pipenv('--venv').out - - @pytest.mark.dotvenv - @pytest.mark.install - @pytest.mark.complex - @pytest.mark.shell - @pytest.mark.windows - @pytest.mark.pew - @pytest.mark.skip('Not mocking this.') - def test_shell_nested_venv_in_project(self, pypi): - import subprocess - with temp_environ(): - os.environ['PIPENV_VENV_IN_PROJECT'] = '1' - os.environ['PIPENV_IGNORE_VIRTUALENVS'] = '1' - with PipenvInstance(chdir=True, pypi=pypi) as p: - # Signal to pew to look in the project directory for the environment - os.environ['WORKON_HOME'] = p.path - project = Project() - c = p.pipenv('install requests') - assert c.return_code == 0 - assert 'requests' in p.pipfile['packages'] - assert 'requests' in p.lockfile['default'] - # Check that .venv now shows in pew's managed list - pew_list = delegator.run('pewtwo ls') - assert '.venv' in pew_list.out - # Check for the venv directory - c = delegator.run('pewtwo dir .venv') - # Compare pew's virtualenv path to what we expect - venv_path = get_windows_path(project.project_directory, '.venv') - # os.path.normpath will normalize slashes - assert venv_path == normalize_drive(os.path.normpath(c.out.strip())) - # Have pew run 'pip freeze' in the virtualenv - # This is functionally the same as spawning a subshell - # If we can do this we can theoretically make a subshell - # This test doesn't work on *nix - if os.name == 'nt': - process = subprocess.Popen( - 'pewtwo in .venv pip freeze', - shell=True, - universal_newlines=True, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) - out, _ = process.communicate() - assert any(req.startswith('requests') for req in out.splitlines()) is True - - @pytest.mark.run - @pytest.mark.dotenv - def test_env(self): - - with PipenvInstance(pipfile=False, chdir=True) as p: - with open('.env', 'w') as f: - f.write('HELLO=WORLD') - - c = p.pipenv('run python -c "import os; print(os.environ[\'HELLO\'])"') - assert c.return_code == 0 - assert 'WORLD' in c.out - - @pytest.mark.e - @pytest.mark.install - @pytest.mark.skip(reason="this doesn't work on windows") - def test_e_dot(self, pip_src_dir): - - with PipenvInstance() as p: - path = os.path.abspath(os.path.sep.join([os.path.dirname(__file__), '..'])) - c = p.pipenv('install -e \'{0}\' --dev'.format(path)) - - assert c.return_code == 0 - - key = [k for k in p.pipfile['dev-packages'].keys()][0] - assert 'path' in p.pipfile['dev-packages'][key] - assert 'requests' in p.lockfile['develop'] - - @pytest.mark.code - @pytest.mark.install - @pytest.mark.skip(reason='non deterministic') - def test_code_import_manual(self): - - with PipenvInstance() as p: - - with PipenvInstance(chdir=True) as p: - with open('t.py', 'w') as f: - f.write('import requests') - - p.pipenv('install -c .') - assert 'requests' in p.pipfile['packages'] - - @pytest.mark.code - @pytest.mark.check - @pytest.mark.unused - @pytest.mark.skip(reason="non-deterministic") - def test_check_unused(self, pypi): - - with PipenvInstance() as p: - with PipenvInstance(chdir=True, pypi=pypi) as p: - with open('__init__.py', 'w') as f: - contents = """ -import tablib -import records - """.strip() - f.write(contents) - p.pipenv('install requests') - p.pipenv('install tablib') - p.pipenv('install records') - - assert all(pkg in p.pipfile['packages'] for pkg in ['requests', 'tablib', 'records']) - - c = p.pipenv('check --unused .') - assert 'tablib' not in c.out - - @pytest.mark.extras - @pytest.mark.install - @pytest.mark.requirements - @pytest.mark.skip(reason="Not mocking this.") - def test_requirements_to_pipfile(self, pypi): - - with PipenvInstance(pipfile=False, chdir=True, pypi=pypi) as p: - - # Write a requirements file - with open('requirements.txt', 'w') as f: - f.write('requests[socks]==2.18.1\n') - - c = p.pipenv('install') - assert c.return_code == 0 - print(c.out) - print(c.err) - print(delegator.run('ls -l').out) - - # assert stuff in pipfile - assert 'requests' in p.pipfile['packages'] - assert 'extras' in p.pipfile['packages']['requests'] - - # assert stuff in lockfile - assert 'requests' in p.lockfile['default'] - assert 'chardet' in p.lockfile['default'] - assert 'idna' in p.lockfile['default'] - assert 'urllib3' in p.lockfile['default'] - assert 'pysocks' in p.lockfile['default'] - - @pytest.mark.code - @pytest.mark.virtualenv - @pytest.mark.project - def test_activate_virtualenv_no_source(self): - command = activate_virtualenv(source=False) - venv = Project().virtualenv_location - - assert command == '{0}/bin/activate'.format(venv) - - @pytest.mark.lock - @pytest.mark.requirements - def test_lock_handle_eggs(self, 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(self, 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 - @pytest.mark.complex - @needs_internet - def test_complex_lock_with_vcs_deps(self, 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(self, 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 - @needs_internet - @flaky - def test_complex_deps_lock_and_install_properly(self, 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 - @pytest.mark.complex - @pytest.mark.skip(reason='Needs numpy to be mocked') - @needs_internet - def test_complex_lock_deep_extras(self, 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.lock - @pytest.mark.deploy - @pytest.mark.cli - def test_deploy_works(self, pypi): - - with PipenvInstance(pypi=pypi) as p: - with open(p.pipfile_path, 'w') as f: - contents = """ + with PipenvInstance(pypi=pypi) as p: + with open(p.pipfile_path, 'w') as f: + contents = """ [packages] requests = "==2.14.0" flask = "==0.12.2" [dev-packages] pytest = "==3.1.1" - """.strip() - f.write(contents) - c = p.pipenv('install') - assert c.return_code == 0 - c = p.pipenv('lock') - assert c.return_code == 0 - with open(p.pipfile_path, 'w') as f: - contents = """ + """.strip() + f.write(contents) + c = p.pipenv('install') + assert c.return_code == 0 + c = p.pipenv('lock') + assert c.return_code == 0 + with open(p.pipfile_path, 'w') as f: + contents = """ [packages] requests = "==2.14.0" - """.strip() - f.write(contents) + """.strip() + f.write(contents) - c = p.pipenv('install --deploy') - assert c.return_code > 0 - - @pytest.mark.files - @pytest.mark.urls - @needs_internet - @flaky - def test_urls_work(self, pypi, pip_src_dir): - - with PipenvInstance(pypi=pypi) as p: - - c = p.pipenv('install https://github.com/divio/django-cms/archive/release/3.4.x.zip') - assert c.return_code == 0 - - dep = list(p.pipfile['packages'].values())[0] - assert 'file' in dep, p.pipfile - - dep = list(p.lockfile['default'].values())[0] - assert 'file' in dep, p.lockfile - - @pytest.mark.files - @pytest.mark.resolver - @pytest.mark.eggs - @flaky - def test_local_package(self, pip_src_dir, pypi): - """This test ensures that local packages (directories with a setup.py) - installed in editable mode have their dependencies resolved as well""" - file_name = 'tablib-0.12.1.tar.gz' - package = 'tablib-0.12.1' - # Not sure where travis/appveyor run tests from - test_dir = os.path.dirname(os.path.abspath(__file__)) - source_path = os.path.abspath(os.path.join(test_dir, 'test_artifacts', file_name)) - with PipenvInstance(chdir=True, pypi=pypi) as p: - # This tests for a bug when installing a zipfile in the current dir - copy_to = os.path.join(p.path, file_name) - shutil.copy(source_path, copy_to) - import tarfile - with tarfile.open(copy_to, 'r:gz') as tgz: - tgz.extractall(path=p.path) - c = p.pipenv('install -e {0}'.format(package)) - assert c.return_code == 0 - assert all(pkg in p.lockfile['default'] for pkg in ['xlrd', 'xlwt', 'pyyaml', 'odfpy']) - - @pytest.mark.files - @flaky - def test_local_zipfiles(self, pypi): - file_name = 'tablib-0.12.1.tar.gz' - # Not sure where travis/appveyor run tests from - test_dir = os.path.dirname(os.path.abspath(__file__)) - source_path = os.path.abspath(os.path.join(test_dir, 'test_artifacts', file_name)) - - with PipenvInstance(chdir=True, pypi=pypi) as p: - # This tests for a bug when installing a zipfile in the current dir - shutil.copy(source_path, os.path.join(p.path, file_name)) - - c = p.pipenv('install {}'.format(file_name)) - assert c.return_code == 0 - key = [k for k in p.pipfile['packages'].keys()][0] - dep = p.pipfile['packages'][key] - - assert 'file' in dep or 'path' in dep - assert c.return_code == 0 - - key = [k for k in p.lockfile['default'].keys()][0] - dep = p.lockfile['default'][key] - - assert 'file' in dep or 'path' in dep - - @pytest.mark.files - @pytest.mark.urls - @needs_internet - @flaky - def test_install_remote_requirements(self, pypi): - with PipenvInstance(pypi=pypi) as p: - # using a github hosted requirements.txt file - c = p.pipenv('install -r https://raw.githubusercontent.com/kennethreitz/pipenv/3688148ac7cfecefb085c474b092c31d791952c1/tests/test_artifacts/requirements.txt') - - assert c.return_code == 0 - # check Pipfile with versions - assert 'requests' in p.pipfile['packages'] - assert p.pipfile['packages']['requests'] == u'==2.18.4' - assert 'records' in p.pipfile['packages'] - assert p.pipfile['packages']['records'] == u'==0.5.2' - - # check Pipfile.lock - assert 'requests' in p.lockfile['default'] - assert 'records' in p.lockfile['default'] - - @pytest.mark.files - @flaky - def test_relative_paths(self, pypi): - file_name = 'tablib-0.12.1.tar.gz' - test_dir = os.path.join(os.path.dirname(os.path.abspath(__file__))) - source_path = os.path.abspath(os.path.join(test_dir, 'test_artifacts', file_name)) - - with PipenvInstance(pypi=pypi) as p: - artifact_dir = 'artifacts' - artifact_path = os.path.join(p.path, artifact_dir) - mkdir_p(artifact_path) - shutil.copy(source_path, os.path.join(artifact_path, file_name)) - # Test installing a relative path in a subdirectory - c = p.pipenv('install {}/{}'.format(artifact_dir, file_name)) - key = [k for k in p.pipfile['packages'].keys()][0] - dep = p.pipfile['packages'][key] - - assert 'path' in dep - assert Path(os.path.join('.', artifact_dir, file_name)) == Path(dep['path']) - assert c.return_code == 0 - - @pytest.mark.install - @pytest.mark.local_file - @flaky - def test_install_local_file_collision(self, pypi): - with PipenvInstance(pypi=pypi) as p: - target_package = 'alembic' - fake_file = os.path.join(p.path, target_package) - with open(fake_file, 'w') as f: - f.write('') - c = p.pipenv('install {}'.format(target_package)) - assert c.return_code == 0 - assert target_package in p.pipfile['packages'] - assert p.pipfile['packages'][target_package] == '*' - assert target_package in p.lockfile['default'] - - @pytest.mark.cli - @pytest.mark.clean - def test_clean_on_empty_venv(self, pypi): - with PipenvInstance(pypi=pypi) as p: - c = p.pipenv('clean') - assert c.return_code == 0 - - @pytest.mark.project - @flaky - def test_environment_variable_value_does_not_change_hash(self, pypi): - with PipenvInstance(chdir=True, pypi=pypi) as p: - with temp_environ(): - with open(p.pipfile_path, 'w') as f: - f.write(""" -[[source]] -url = 'https://${PYPI_USERNAME}:${PYPI_PASSWORD}@pypi.python.org/simple' -verify_ssl = true -name = 'pypi' -[requires] -python_version = '2.7' -[packages] -flask = "==0.12.2" -""") - os.environ['PYPI_USERNAME'] = 'whatever' - os.environ['PYPI_PASSWORD'] = 'pass' - assert Project().get_lockfile_hash() is None - c = p.pipenv('install') - lock_hash = Project().get_lockfile_hash() - assert lock_hash is not None - assert lock_hash == Project().calculate_pipfile_hash() - # sanity check on pytest - assert 'PYPI_USERNAME' not in str(pipfile.load(p.pipfile_path)) - assert c.return_code == 0 - assert Project().get_lockfile_hash() == Project().calculate_pipfile_hash() - os.environ['PYPI_PASSWORD'] = 'pass2' - assert Project().get_lockfile_hash() == Project().calculate_pipfile_hash() - with open(p.pipfile_path, 'a') as f: - f.write('requests = "==2.14.0"\n') - assert Project().get_lockfile_hash() != Project().calculate_pipfile_hash() - - @pytest.mark.run - def test_scripts(self): - with PipenvInstance(chdir=True) as p: - with open(p.pipfile_path, 'w') as f: - f.write(r""" -[scripts] -printfoo = "python -c \"print('foo')\"" -notfoundscript = "randomthingtotally" -appendscript = "cmd arg1" -multicommand = "bash -c \"cd docs && make html\"" - """) - c = p.pipenv('install') - assert c.return_code == 0 - - c = p.pipenv('run printfoo') - assert c.return_code == 0 - assert c.out == 'foo\n' - assert c.err == '' - - c = p.pipenv('run notfoundscript') - assert c.return_code == 1 - assert c.out == '' - if os.name != 'nt': # TODO: Implement this message for Windows. - assert 'Error' in c.err - assert 'randomthingtotally (from notfoundscript)' in c.err - - project = Project() - script = project.build_script('multicommand') - assert script.command == 'bash' - assert script.args == ['-c', 'cd docs && make html'] - script = project.build_script('appendscript', ['a', 'b']) - assert script.command == 'cmd' - assert script.args == ['arg1', 'a', 'b'] - - @pytest.mark.lock - @pytest.mark.complex - @flaky - @py3_only - def test_resolver_unique_markers(self, pypi): - """vcrpy has a dependency on `yarl` which comes with a marker - of 'python version in "3.4, 3.5, 3.6" - this marker duplicates itself: - - 'yarl; python version in "3.4, 3.5, 3.6"; python version in "3.4, 3.5, 3.6"' - - This verifies that we clean that successfully. - """ - with PipenvInstance(chdir=True) as p: - c = p.pipenv('install vcrpy==1.11.0') - assert c.return_code == 0 - assert 'yarl' in p.lockfile['default'] - yarl = p.lockfile['default']['yarl'] - assert 'markers' in yarl - assert yarl['markers'] == "python_version in '3.4, 3.5, 3.6'" - - @pytest.mark.project - @pytest.mark.skipif(os.name != 'nt', reason='Test project matching for case changes on win') - def test_case_changes_windows(self, pypi): - with PipenvInstance(pypi=pypi, chdir=True) as p: - c = p.pipenv('install pytz') - assert c.return_code == 0 - virtualenv_location = Project().virtualenv_location - target = p.path.upper() - if target == p.path: - target = p.path.lower() - os.chdir('..') - os.chdir(target) - assert os.path.abspath(os.curdir) != p.path - venv = delegator.run('pipenv --venv').out - assert venv.strip().lower() == virtualenv_location.lower() + c = p.pipenv('install --deploy') + assert c.return_code > 0 diff --git a/tests/test_run.py b/tests/test_run.py new file mode 100644 index 00000000..30efb077 --- /dev/null +++ b/tests/test_run.py @@ -0,0 +1,50 @@ +import os + +import pytest + + +@pytest.mark.run +@pytest.mark.dotenv +def test_env(PipenvInstance): + with PipenvInstance(pipfile=False, chdir=True) as p: + with open('.env', 'w') as f: + f.write('HELLO=WORLD') + + c = p.pipenv('run python -c "import os; print(os.environ[\'HELLO\'])"') + assert c.return_code == 0 + assert 'WORLD' in c.out + + +@pytest.mark.run +def test_scripts(PipenvInstance, project): + with PipenvInstance(chdir=True) as p: + with open(p.pipfile_path, 'w') as f: + f.write(r""" +[scripts] +printfoo = "python -c \"print('foo')\"" +notfoundscript = "randomthingtotally" +appendscript = "cmd arg1" +multicommand = "bash -c \"cd docs && make html\"" + """) + c = p.pipenv('install') + assert c.return_code == 0 + + c = p.pipenv('run printfoo') + assert c.return_code == 0 + assert c.out == 'foo\n' + assert c.err == '' + + c = p.pipenv('run notfoundscript') + assert c.return_code == 1 + assert c.out == '' + if os.name != 'nt': # TODO: Implement this message for Windows. + assert 'Error' in c.err + assert 'randomthingtotally (from notfoundscript)' in c.err + + script = project.build_script('multicommand') + assert script.command == 'bash' + assert script.args == ['-c', 'cd docs && make html'] + + script = project.build_script('appendscript', ['a', 'b']) + assert script.command == 'cmd' + assert script.args == ['arg1', 'a', 'b'] diff --git a/tests/test_uninstall.py b/tests/test_uninstall.py new file mode 100644 index 00000000..2984ef7f --- /dev/null +++ b/tests/test_uninstall.py @@ -0,0 +1,109 @@ +import os +import shutil + +import pytest + + +@pytest.mark.run +@pytest.mark.uninstall +@pytest.mark.install +def test_uninstall(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi) as p: + c = p.pipenv('install requests') + assert c.return_code == 0 + assert 'requests' in p.pipfile['packages'] + assert 'requests' in p.lockfile['default'] + assert 'chardet' in p.lockfile['default'] + assert 'idna' in p.lockfile['default'] + assert 'urllib3' in p.lockfile['default'] + assert 'certifi' in p.lockfile['default'] + + c = p.pipenv('uninstall requests') + assert c.return_code == 0 + assert 'requests' not in p.pipfile['dev-packages'] + assert 'requests' not in p.lockfile['develop'] + assert 'chardet' not in p.lockfile['develop'] + assert 'idna' not in p.lockfile['develop'] + assert 'urllib3' not in p.lockfile['develop'] + assert 'certifi' not in p.lockfile['develop'] + + c = p.pipenv('run python -m requests.help') + assert c.return_code > 0 + + +@pytest.mark.files +@pytest.mark.uninstall +@pytest.mark.install +def test_uninstall_all_local_files(PipenvInstance): + file_name = 'tablib-0.12.1.tar.gz' + # Not sure where travis/appveyor run tests from + test_dir = os.path.dirname(os.path.abspath(__file__)) + source_path = os.path.abspath(os.path.join(test_dir, 'test_artifacts', file_name)) + + with PipenvInstance() as p: + shutil.copy(source_path, os.path.join(p.path, file_name)) + os.mkdir(os.path.join(p.path, "tablib")) + c = p.pipenv('install {}'.format(file_name)) + assert c.return_code == 0 + c = p.pipenv('uninstall --all') + assert c.return_code == 0 + assert 'tablib' in c.out + assert 'tablib' not in p.pipfile['packages'] + + +@pytest.mark.run +@pytest.mark.uninstall +@pytest.mark.install +def test_uninstall_all_dev(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi) as p: + c = p.pipenv('install --dev requests six') + assert c.return_code == 0 + + c = p.pipenv('install pytz') + assert c.return_code == 0 + + assert 'pytz' in p.pipfile['packages'] + assert 'requests' in p.pipfile['dev-packages'] + assert 'six' in p.pipfile['dev-packages'] + assert 'pytz' in p.lockfile['default'] + assert 'requests' in p.lockfile['develop'] + assert 'six' in p.lockfile['develop'] + + c = p.pipenv('uninstall --all-dev') + assert c.return_code == 0 + assert 'requests' not in p.pipfile['dev-packages'] + assert 'six' not in p.pipfile['dev-packages'] + assert 'requests' not in p.lockfile['develop'] + assert 'six' not in p.lockfile['develop'] + assert 'pytz' in p.pipfile['packages'] + assert 'pytz' in p.lockfile['default'] + + c = p.pipenv('run python -m requests.help') + assert c.return_code > 0 + + c = p.pipenv('run python -c "import pytz"') + assert c.return_code == 0 + + +@pytest.mark.uninstall +@pytest.mark.run +def test_normalize_name_uninstall(PipenvInstance, pypi): + with PipenvInstance(pypi=pypi) as p: + with open(p.pipfile_path, 'w') as f: + contents = """ +# Pre comment +[packages] +Requests = "*" +python_DateUtil = "*" # Inline comment +""" + f.write(contents) + + c = p.pipenv('install') + assert c.return_code == 0 + + c = p.pipenv('uninstall python_dateutil') + assert 'Requests' in p.pipfile['packages'] + assert 'python_DateUtil' not in p.pipfile['packages'] + contents = open(p.pipfile_path).read() + assert '# Pre comment' in contents + assert '# Inline comment' in contents diff --git a/tests/test_windows.py b/tests/test_windows.py new file mode 100644 index 00000000..02ae4f32 --- /dev/null +++ b/tests/test_windows.py @@ -0,0 +1,27 @@ +import os + +import pytest + + +# This module is run only on Windows. +pytestmark = pytest.mark.skipif(os.name == 'nt', reason="only relevant on windows") + + +@pytest.mark.project +def test_case_changes_windows(PipenvInstance, pypi, project): + """Test project matching for case changes on Windows. + """ + with PipenvInstance(pypi=pypi, chdir=True) as p: + c = p.pipenv('install pytz') + assert c.return_code == 0 + + virtualenv_location = project.virtualenv_location + target = p.path.upper() + if target == p.path: + target = p.path.lower() + os.chdir('..') + os.chdir(target) + assert os.path.abspath(os.curdir) != p.path + + venv = p.pipenv('--venv').out + assert venv.strip().lower() == virtualenv_location.lower()