From b3af3f06e8295c45716187cd34e9ac96160ee270 Mon Sep 17 00:00:00 2001 From: Felipe Arruda Pontes Date: Sat, 11 Feb 2017 19:23:33 -0200 Subject: [PATCH 1/5] changing project.source to project.sources and making it return a list as default instead of just the first source present --- pipenv/project.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pipenv/project.py b/pipenv/project.py index 12886000..3ca986c2 100644 --- a/pipenv/project.py +++ b/pipenv/project.py @@ -127,16 +127,16 @@ class Project(object): f.write(format_toml(toml.dumps(data))) @property - def source(self): + def sources(self): if self.lockfile_exists: meta_ = self.lockfile_content['_meta'] sources_ = meta_.get('sources') if sources_: - return sources_[0] + return sources_ if 'source' in self.parsed_pipfile: - return self.parsed_pipfile['source'][0] + return self.parsed_pipfile['source'] else: - return {u'url': u'https://pypi.python.org/simple', u'verify_ssl': True} + return [{u'url': u'https://pypi.python.org/simple', u'verify_ssl': True}] def remove_package_from_pipfile(self, package_name, dev=False): From dc02663c6689d91ee0d2c73623196160c213e47e Mon Sep 17 00:00:00 2001 From: Felipe Arruda Pontes Date: Sat, 11 Feb 2017 19:24:39 -0200 Subject: [PATCH 2/5] making `cli.proper_case` use a new method that runs through all sources in project.sources, and return the Request's return for the first one that hit OK, or neither did ok, then raise an exception. Also doing something similar to `cli.pip_install` so that it will try to pip install for all the sources and return the first cmd that worked or the last that didn't work. --- pipenv/cli.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pipenv/cli.py b/pipenv/cli.py index 3d0ba661..c0c5bd81 100644 --- a/pipenv/cli.py +++ b/pipenv/cli.py @@ -551,10 +551,16 @@ def do_init(dev=False, requirements=False, skip_virtualenv=False, allow_global=F def pip_install(package_name=None, r=None, allow_global=False): - if r: - c = delegator.run('{0} install -r {1} --require-hashes -i {2}'.format(which_pip(allow_global=allow_global), r, project.source['url'])) - else: - c = delegator.run('{0} install "{1}" -i {2}'.format(which_pip(allow_global=allow_global), package_name, project.source['url'])) + # try installing for each source in project.sources + for source in project.sources: + if r: + c = delegator.run('{0} install -r {1} --require-hashes -i {2}'.format(which_pip(allow_global=allow_global), r, source['url'])) + else: + c = delegator.run('{0} install "{1}" -i {2}'.format(which_pip(allow_global=allow_global), package_name, source['url'])) + + if c.return_code == 0: + break + # return the result of the first one that runs ok or the last one that didn't work return c From 4dd3cccafabe001462369393e45e2d4fee1d2af7 Mon Sep 17 00:00:00 2001 From: Felipe Arruda Pontes Date: Sat, 11 Feb 2017 19:28:08 -0200 Subject: [PATCH 3/5] adding mock to test the methods --- Pipfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Pipfile b/Pipfile index dd8a3a48..496b040e 100644 --- a/Pipfile +++ b/Pipfile @@ -1,6 +1,7 @@ [dev-packages] pytest = "*" sphinx = "*" +mock = "*" [packages] click = "*" From a79843107e29d1b5a1bd9f0d4b3c417c0edb7846 Mon Sep 17 00:00:00 2001 From: Felipe Arruda Pontes Date: Sat, 11 Feb 2017 23:06:28 -0200 Subject: [PATCH 4/5] adding tests for pip_install --- tests/test_pipenv.py | 58 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/tests/test_pipenv.py b/tests/test_pipenv.py index b894e0df..27cef36a 100644 --- a/tests/test_pipenv.py +++ b/tests/test_pipenv.py @@ -1,13 +1,16 @@ import os +from mock import patch, Mock, PropertyMock + import pytest import delegator import toml from pipenv.cli import (activate_virtualenv, ensure_proper_casing, - parse_download_fname, parse_install_output) + parse_download_fname, parse_install_output, pip_install) from pipenv.project import Project + class TestPipenv(): @pytest.mark.parametrize('fname, name, expected', [ @@ -131,3 +134,56 @@ class TestPipenv(): venv = Project().virtualenv_location assert command == '{0}/bin/activate'.format(venv) + + @patch('pipenv.project.Project.virtualenv_location', new_callable=PropertyMock(return_value='foo')) + @patch('pipenv.project.Project.sources', new_callable=PropertyMock) + @patch('delegator.run') + def test_pip_install_should_try_every_possible_source(self, mocked_delegator, mockec_sources, mocked_venv_location): + sources = [ + {'url': 'http://dontexistis.in.pypi/simple'}, + {'url': 'http://existis.in.pypi/simple'} + ] + mockec_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_install('package') + assert c.return_code == 0 + + @patch('pipenv.project.Project.virtualenv_location', new_callable=PropertyMock(return_value='foo')) + @patch('pipenv.project.Project.sources', new_callable=PropertyMock) + @patch('delegator.run') + def test_pip_install_should_return_the_last_error_if_no_cmd_worked(self, mocked_delegator, mockec_sources, mocked_venv_location): + sources = [ + {'url': 'http://dontexistis.in.pypi/simple'}, + {'url': 'http://dontexistis.in.pypi/simple'} + ] + mockec_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_install('package') + assert c.return_code == 1 + assert c == second_cmd_return + + @patch('pipenv.project.Project.virtualenv_location', new_callable=PropertyMock(return_value='foo')) + @patch('pipenv.project.Project.sources', new_callable=PropertyMock) + @patch('delegator.run') + def test_pip_install_should_return_the_first_cmd_that_worked(self, mocked_delegator, mockec_sources, mocked_venv_location): + sources = [ + {'url': 'http://dontexistis.in.pypi/simple'}, + {'url': 'http://dontexistis.in.pypi/simple'} + ] + mockec_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_install('package') + assert c.return_code == 0 + assert c == first_cmd_return From 97426bf1456bd390648a89e99fc0b214dfbf7e1a Mon Sep 17 00:00:00 2001 From: Felipe Arruda Pontes Date: Mon, 13 Feb 2017 21:19:36 -0200 Subject: [PATCH 5/5] rm mock for project.virtualenv_location in tests, fixing some url in tests and other typos as well --- tests/test_pipenv.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/tests/test_pipenv.py b/tests/test_pipenv.py index 27cef36a..219376e7 100644 --- a/tests/test_pipenv.py +++ b/tests/test_pipenv.py @@ -135,15 +135,14 @@ class TestPipenv(): assert command == '{0}/bin/activate'.format(venv) - @patch('pipenv.project.Project.virtualenv_location', new_callable=PropertyMock(return_value='foo')) @patch('pipenv.project.Project.sources', new_callable=PropertyMock) @patch('delegator.run') - def test_pip_install_should_try_every_possible_source(self, mocked_delegator, mockec_sources, mocked_venv_location): + def test_pip_install_should_try_every_possible_source(self, mocked_delegator, mocked_sources): sources = [ {'url': 'http://dontexistis.in.pypi/simple'}, {'url': 'http://existis.in.pypi/simple'} ] - mockec_sources.return_value = sources + mocked_sources.return_value = sources first_cmd_return = Mock() first_cmd_return.return_code = 1 second_cmd_return = Mock() @@ -152,15 +151,14 @@ class TestPipenv(): c = pip_install('package') assert c.return_code == 0 - @patch('pipenv.project.Project.virtualenv_location', new_callable=PropertyMock(return_value='foo')) @patch('pipenv.project.Project.sources', new_callable=PropertyMock) @patch('delegator.run') - def test_pip_install_should_return_the_last_error_if_no_cmd_worked(self, mocked_delegator, mockec_sources, mocked_venv_location): + def test_pip_install_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'} ] - mockec_sources.return_value = sources + mocked_sources.return_value = sources first_cmd_return = Mock() first_cmd_return.return_code = 1 second_cmd_return = Mock() @@ -170,15 +168,14 @@ class TestPipenv(): assert c.return_code == 1 assert c == second_cmd_return - @patch('pipenv.project.Project.virtualenv_location', new_callable=PropertyMock(return_value='foo')) @patch('pipenv.project.Project.sources', new_callable=PropertyMock) @patch('delegator.run') - def test_pip_install_should_return_the_first_cmd_that_worked(self, mocked_delegator, mockec_sources, mocked_venv_location): + def test_pip_install_should_return_the_first_cmd_that_worked(self, mocked_delegator, mocked_sources): sources = [ - {'url': 'http://dontexistis.in.pypi/simple'}, - {'url': 'http://dontexistis.in.pypi/simple'} + {'url': 'http://existis.in.pypi/simple'}, + {'url': 'http://existis.in.pypi/simple'} ] - mockec_sources.return_value = sources + mocked_sources.return_value = sources first_cmd_return = Mock() first_cmd_return.return_code = 0 second_cmd_return = Mock()