diff --git a/pipenv/patched/contoml/file/file.py b/pipenv/patched/contoml/file/file.py index 16017b99..99ce1483 100755 --- a/pipenv/patched/contoml/file/file.py +++ b/pipenv/patched/contoml/file/file.py @@ -231,7 +231,7 @@ class TOMLFile: if has_anonymous_entry(): return items else: - return items + [('', self[''])] + return list(items) + [('', self[''])] @property def primitive(self): diff --git a/pipenv/patched/pipfile/api.py b/pipenv/patched/pipfile/api.py index 08aa49c3..18a1ea23 100644 --- a/pipenv/patched/pipfile/api.py +++ b/pipenv/patched/pipfile/api.py @@ -9,6 +9,13 @@ import sys import os +DEFAULT_SOURCE = { + u'url': u'https://pypi.python.org/simple', + u'verify_ssl': True, + u'name': u'pypi', +} + + def format_full_version(info): version = '{0.major}.{0.minor}.{0.micro}'.format(info) kind = info.releaselevel @@ -89,7 +96,7 @@ class PipfileParser(object): # Load the default configuration. default_config = { - u'source': [{u'url': u'https://pypi.python.org/simple', u'verify_ssl': True, 'name': "pypi"}], + u'source': [DEFAULT_SOURCE], u'packages': {}, u'requires': {}, u'dev-packages': {} diff --git a/pipenv/project.py b/pipenv/project.py index 4b8d1bb7..9da86876 100644 --- a/pipenv/project.py +++ b/pipenv/project.py @@ -12,6 +12,7 @@ import hashlib import contoml import delegator import pipfile +import pipfile.api import toml from pip9 import ConfigOptionParser @@ -51,6 +52,26 @@ if PIPENV_PIPFILE: _pipfile_cache = {} +if PIPENV_TEST_INDEX: + DEFAULT_SOURCE = { + u'url': PIPENV_TEST_INDEX, + u'verify_ssl': True, + u'name': u'custom', + } +else: + DEFAULT_SOURCE = { + u'url': u'https://pypi.python.org/simple', + u'verify_ssl': True, + u'name': u'pypi', + } + +pipfile.api.DEFAULT_SOURCE = DEFAULT_SOURCE + + +class SourceNotFound(KeyError): + pass + + class Project(object): """docstring for Project""" @@ -490,35 +511,20 @@ class Project(object): config_parser = ConfigOptionParser(name=self.name) install = dict(config_parser.get_config_section('install')) indexes = install.get('extra-index-url', '').lstrip('\n').split('\n') - if PIPENV_TEST_INDEX: - sources = [ - { - u'url': PIPENV_TEST_INDEX, - u'verify_ssl': True, - u'name': u'custom', - } - ] - else: - # Default source. - pypi_source = { - u'url': u'https://pypi.python.org/simple', - u'verify_ssl': True, - u'name': 'pypi', - } - sources = [pypi_source] - for i, index in enumerate(indexes): - if not index: - continue + sources = [DEFAULT_SOURCE] + for i, index in enumerate(indexes): + if not index: + continue - source_name = 'pip_index_{}'.format(i) - verify_ssl = index.startswith('https') - sources.append( - { - u'url': index, - u'verify_ssl': verify_ssl, - u'name': source_name, - } - ) + source_name = 'pip_index_{}'.format(i) + verify_ssl = index.startswith('https') + sources.append( + { + u'url': index, + u'verify_ssl': verify_ssl, + u'name': source_name, + } + ) data = { u'source': sources, # Default packages. @@ -565,15 +571,8 @@ class Project(object): if 'source' in self.parsed_pipfile: return self.parsed_pipfile['source'] - else: - return [ - { - u'url': u'https://pypi.python.org/simple', - u'verify_ssl': True, - 'name': 'pypi', - } - ] + return [DEFAULT_SOURCE] def get_source(self, name=None, url=None): for source in self.sources: @@ -584,6 +583,7 @@ class Project(object): elif url: if source.get('url') in url: return source + raise SourceNotFound(name or url) def destroy_lockfile(self): """Deletes the lockfile.""" diff --git a/pipenv/utils.py b/pipenv/utils.py index 741826f3..7d60e440 100644 --- a/pipenv/utils.py +++ b/pipenv/utils.py @@ -1207,7 +1207,7 @@ class TemporaryDirectory(object): in it are removed. """ - def __init__(self, suffix=None, prefix=None, dir=None): + def __init__(self, suffix, prefix, dir=None): if 'RAM_DISK' in os.environ: import uuid diff --git a/tests/pypi/multidict/multidict-4.1.0.tar.gz b/tests/pypi/multidict/multidict-4.1.0.tar.gz new file mode 100644 index 00000000..89adbb22 Binary files /dev/null and b/tests/pypi/multidict/multidict-4.1.0.tar.gz differ diff --git a/tests/pypi/tablib/tablib-0.11.5.tar.gz b/tests/pypi/tablib/tablib-0.11.5.tar.gz new file mode 100644 index 00000000..460cd0a7 Binary files /dev/null and b/tests/pypi/tablib/tablib-0.11.5.tar.gz differ diff --git a/tests/pypi/tablib/tablib-0.12.0.tar.gz b/tests/pypi/tablib/tablib-0.12.0.tar.gz new file mode 100644 index 00000000..36ab89f2 Binary files /dev/null and b/tests/pypi/tablib/tablib-0.12.0.tar.gz differ diff --git a/tests/pypi/vcrpy/vcrpy-1.11.0.tar.gz b/tests/pypi/vcrpy/vcrpy-1.11.0.tar.gz new file mode 100644 index 00000000..605bce97 Binary files /dev/null and b/tests/pypi/vcrpy/vcrpy-1.11.0.tar.gz differ diff --git a/tests/pypi/wrapt/wrapt-1.10.11.tar.gz b/tests/pypi/wrapt/wrapt-1.10.11.tar.gz new file mode 100644 index 00000000..7f6870e2 Binary files /dev/null and b/tests/pypi/wrapt/wrapt-1.10.11.tar.gz differ diff --git a/tests/pytest-pypi/pytest_pypi.egg-info/PKG-INFO b/tests/pytest-pypi/pytest_pypi.egg-info/PKG-INFO index 966cd7ad..50322734 100644 --- a/tests/pytest-pypi/pytest_pypi.egg-info/PKG-INFO +++ b/tests/pytest-pypi/pytest_pypi.egg-info/PKG-INFO @@ -6,7 +6,6 @@ Home-page: https://github.com/kennethreitz/pytest-pypi Author: Kenneth Reitz Author-email: me@kennethreitz.org License: MIT -Description-Content-Type: UNKNOWN Description: pytest-httpbin ============== diff --git a/tests/test_pipenv.py b/tests/test_pipenv.py index 13978589..5c12a9b4 100644 --- a/tests/test_pipenv.py +++ b/tests/test_pipenv.py @@ -11,6 +11,7 @@ from pipenv.utils import ( ) 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 @@ -23,15 +24,31 @@ try: except ImportError: from pipenv.vendor.pathlib2 import Path -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") - 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']) +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() + +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.fixture(scope='module') def pip_src_dir(request): old_src_dir = os.environ.get('PIP_SRC', '') @@ -46,13 +63,16 @@ def pip_src_dir(request): return request -class PipenvInstance(): +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 = TemporaryDirectory(suffix='-project', prefix='pipenv-') self.path = self._path.name # set file creation perms self.pipfile_path = None @@ -91,7 +111,11 @@ class PipenvInstance(): if self.pipfile_path: os.environ['PIPENV_PIPFILE'] = self.pipfile_path - c = delegator.run('pipenv {0}'.format(cmd), block=block) + 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'] @@ -182,6 +206,7 @@ class TestPipenv: 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') @@ -354,7 +379,8 @@ tablib = "*" 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)) - c = p.pipenv('uninstall --all --verbose') + 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'] @@ -442,7 +468,8 @@ setup( @pytest.mark.vcs @pytest.mark.install - def test_basic_vcs_install(self, pypi): + @needs_internet + def test_basic_vcs_install(self, pip_src_dir, pypi): with PipenvInstance(pypi=pypi) as p: c = p.pipenv('install git+https://github.com/requests/requests.git#egg=requests') assert c.return_code == 0 @@ -457,6 +484,7 @@ setup( @pytest.mark.e @pytest.mark.vcs @pytest.mark.install + @needs_internet 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') @@ -546,8 +574,9 @@ tpfd = "*" @pytest.mark.install @pytest.mark.resolver @pytest.mark.backup_resolver - def test_backup_resolver(self, pypi): - with PipenvInstance(pypi=pypi) as p: + @needs_internet + def test_backup_resolver(self): + with PipenvInstance() as p: with open(p.pipfile_path, 'w') as f: contents = """ [packages] @@ -561,8 +590,6 @@ tpfd = "*" @pytest.mark.run @pytest.mark.markers - @pytest.mark.install - @pytest.mark.failed def test_package_environment_markers(self, pypi): with PipenvInstance(pypi=pypi) as p: @@ -605,10 +632,10 @@ requests = {version = "*", os_name = "== 'splashwear'"} @pytest.mark.markers @pytest.mark.install - def test_top_level_overrides_environment_markers(self): + def test_top_level_overrides_environment_markers(self, pypi): """Top-level environment markers should take precedence. """ - with PipenvInstance() as p: + with PipenvInstance(pypi=pypi) as p: with open(p.pipfile_path, 'w') as f: contents = """ [packages] @@ -624,7 +651,7 @@ funcsigs = {version = "*", os_name = "== 'splashwear'"} @pytest.mark.markers @pytest.mark.install - def test_global_overrides_environment_markers(self): + def test_global_overrides_environment_markers(self, pypi): """Empty (unconditional) dependency should take precedence. If a dependency is specified without environment markers, it should @@ -632,7 +659,7 @@ funcsigs = {version = "*", os_name = "== 'splashwear'"} APScheduler requires funcsigs only on Python 2, but since funcsigs is also specified as an unconditional dep, its markers should be empty. """ - with PipenvInstance() as p: + with PipenvInstance(pypi=pypi) as p: with open(p.pipfile_path, 'w') as f: contents = """ [packages] @@ -649,8 +676,11 @@ funcsigs = "*" @pytest.mark.install @pytest.mark.vcs @pytest.mark.tablib - def test_install_editable_git_tag(self, pip_src_dir, pypi): - with PipenvInstance(pypi=pypi) as p: + @needs_internet + 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/kennethreitz/tablib.git@v0.12.1#egg=tablib') assert c.return_code == 0 assert 'tablib' in p.pipfile['packages'] @@ -923,9 +953,11 @@ flask = "==0.12.2" @pytest.mark.lock @pytest.mark.complex - def test_complex_lock_with_vcs_deps(self, pip_src_dir, pypi): - - with PipenvInstance(pypi=pypi) as p: + @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] @@ -976,9 +1008,10 @@ allow_prereleases = true @pytest.mark.requirements @pytest.mark.complex @pytest.mark.maya - def test_complex_deps_lock_and_install_properly(self, pypi): - - with PipenvInstance(pypi=pypi) as p: + @needs_internet + def test_complex_deps_lock_and_install_properly(self): + # This uses the real PyPI because Maya has too many dependencies... + with PipenvInstance() as p: with open(p.pipfile_path, 'w') as f: contents = """ [packages] @@ -994,12 +1027,13 @@ maya = "*" @pytest.mark.extras @pytest.mark.lock - @pytest.mark.requirements @pytest.mark.complex - def test_complex_lock_deep_extras(self, pypi): + @needs_internet + def test_complex_lock_deep_extras(self): # 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 PipenvInstance() as p: with open(p.pipfile_path, 'w') as f: contents = """ [packages] @@ -1007,12 +1041,12 @@ 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'] - c = p.pipenv('install') - assert c.return_code == 0 @pytest.mark.lock @pytest.mark.deploy @@ -1028,9 +1062,10 @@ flask = "==0.12.2" pytest = "==3.1.1" """.strip() f.write(contents) - - p.pipenv('lock') - + 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] @@ -1044,6 +1079,7 @@ requests = "==2.14.0" @pytest.mark.install @pytest.mark.files @pytest.mark.urls + @needs_internet def test_urls_work(self, pypi): with PipenvInstance(pypi=pypi) as p: @@ -1096,6 +1132,7 @@ requests = "==2.14.0" 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] @@ -1110,6 +1147,7 @@ requests = "==2.14.0" @pytest.mark.install @pytest.mark.files @pytest.mark.urls + @needs_internet def test_install_remote_requirements(self, pypi): with PipenvInstance(pypi=pypi) as p: # using a github hosted requirements.txt file @@ -1177,10 +1215,8 @@ requests = "==2.14.0" 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" """)