Repository: pypa/pipenv

Issue:
Pipenv lock don't respect multiple sources
Just saw now that the lock uses `pip_download` that don't use anything other than pip's default configuration on index-url.

One approach that seems reasonable would be to use pip_download like pip_install function: with `-i` and trying for each project.source available.


Discussion:
@arruda that seems sensical since we'll need a file to hash. If you'd like to put together a PR for this, I'd be happy to take a look at it.
Yeap, I'll do PR for this, having so much fun with this project =D.

Resolving patch:
diff --git a/pipenv/cli.py b/pipenv/cli.py
--- a/pipenv/cli.py
+++ b/pipenv/cli.py
@@ -565,7 +565,17 @@ def pip_install(package_name=None, r=None, allow_global=False):
 
 
 def pip_download(package_name):
-    c = delegator.run('{0} download "{1}" -d {2}'.format(which_pip(), package_name, project.download_location))
+    for source in project.sources:
+        cmd = '{0} download "{1}"  -i {2} -d {3}'.format(
+            which_pip(),
+            package_name,
+            source['url'],
+            project.download_location
+        )
+        c = delegator.run(cmd)
+        if c.return_code == 0:
+            break
+
     return c
 
 


Test patch:
diff --git a/tests/test_pipenv.py b/tests/test_pipenv.py
--- a/tests/test_pipenv.py
+++ b/tests/test_pipenv.py
@@ -7,7 +7,7 @@
 import toml
 
 from pipenv.cli import (activate_virtualenv, ensure_proper_casing,
-    parse_download_fname, parse_install_output, pip_install)
+    parse_download_fname, parse_install_output, pip_install, pip_download, which_pip)
 from pipenv.project import Project
 
 
@@ -184,3 +184,53 @@ def test_pip_install_should_return_the_first_cmd_that_worked(self, mocked_delega
         c = pip_install('package')
         assert c.return_code == 0
         assert c == first_cmd_return
+
+    @patch('pipenv.project.Project.sources', new_callable=PropertyMock)
+    @patch('delegator.run')
+    def test_pip_download_should_try_every_possible_source(self, mocked_delegator, mocked_sources):
+        sources = [
+            {'url': 'http://dontexistis.in.pypi/simple'},
+            {'url': 'http://existis.in.pypi/simple'}
+        ]
+        mocked_sources.return_value = sources
+        first_cmd_return = Mock()
+        first_cmd_return.return_code = 1
+        second_cmd_return = Mock()
+        second_cmd_return.return_code = 0
+        mocked_delegator.side_effect = [first_cmd_return, second_cmd_return]
+        c = pip_download('package')
+        assert c.return_code == 0
+
+    @patch('pipenv.project.Project.sources', new_callable=PropertyMock)
+    @patch('delegator.run')
+    def test_pip_download_should_return_the_last_error_if_no_cmd_worked(self, mocked_delegator, mocked_sources):
+        sources = [
+            {'url': 'http://dontexistis.in.pypi/simple'},
+            {'url': 'http://dontexistis.in.pypi/simple'}
+        ]
+        mocked_sources.return_value = sources
+        first_cmd_return = Mock()
+        first_cmd_return.return_code = 1
+        second_cmd_return = Mock()
+        second_cmd_return.return_code = 1
+        mocked_delegator.side_effect = [first_cmd_return, second_cmd_return]
+        c = pip_download('package')
+        assert c.return_code == 1
+        assert c == second_cmd_return
+
+    @patch('pipenv.project.Project.sources', new_callable=PropertyMock)
+    @patch('delegator.run')
+    def test_pip_download_should_return_the_first_cmd_that_worked(self, mocked_delegator, mocked_sources):
+        sources = [
+            {'url': 'http://existis.in.pypi/simple'},
+            {'url': 'http://existis.in.pypi/simple'}
+        ]
+        mocked_sources.return_value = sources
+        first_cmd_return = Mock()
+        first_cmd_return.return_code = 0
+        second_cmd_return = Mock()
+        second_cmd_return.return_code = 0
+        mocked_delegator.side_effect = [first_cmd_return, second_cmd_return]
+        c = pip_download('package')
+        assert c.return_code == 0
+        assert c == first_cmd_return