diff --git a/README.md b/README.md index e9b639b..fef218b 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ Arguments: : --skip-package-installation Only upgrade the version in requirements files, don't install the new package. --skip-virtualenv-check Disable virtualenv check. Allows installing the new packages outside the virtualenv. --use-default-index Skip searching for custom index-url in pip configuration file(s). + --timeout Set the pypi timeout in seconds [default: 15]. Examples: diff --git a/pip_upgrader/cli.py b/pip_upgrader/cli.py index 7b99e54..06222b7 100755 --- a/pip_upgrader/cli.py +++ b/pip_upgrader/cli.py @@ -2,7 +2,7 @@ pip-upgrade Usage: - pip-upgrade [] ... [--prerelease] [-p=...] [--dry-run] [--check-greater-equal] [--skip-virtualenv-check] [--skip-package-installation] [--use-default-index] + pip-upgrade [] ... [--prerelease] [-p=...] [--dry-run] [--check-greater-equal] [--skip-virtualenv-check] [--skip-package-installation] [--use-default-index] [--timeout ] Arguments: requirements_file The requirement FILE, or WILDCARD PATH to multiple files. @@ -13,6 +13,7 @@ --skip-package-installation Only upgrade the version in requirements files, don't install the new package. --skip-virtualenv-check Disable virtualenv check. Allows installing the new packages outside the virtualenv. --use-default-index Skip searching for custom index-url in pip configuration file(s). + --timeout Set the pypi timeout in seconds [default: 15]. Examples: pip-upgrade # auto discovers requirements file diff --git a/pip_upgrader/packages_status_detector.py b/pip_upgrader/packages_status_detector.py index ccd2ea7..6918c21 100644 --- a/pip_upgrader/packages_status_detector.py +++ b/pip_upgrader/packages_status_detector.py @@ -47,6 +47,11 @@ def __init__(self, packages, options): self.check_gte = options['--check-greater-equal'] self._prerelease = False + self.pypi_timeout = 15 + if options['timeout']: + if options['timeout'].isdecimal(): + self.pypi_timeout = int(options['timeout']) + def _update_index_url_from_configs(self): """ Checks for alternative index-url in pip.conf """ @@ -160,7 +165,7 @@ def _fetch_index_package_info(self, package_name, current_version): package_canonical_name = package_name if self.PYPI_API_TYPE == 'simple_html': package_canonical_name = canonicalize_name(package_name) - response = requests.get(self.PYPI_API_URL.format(package=package_canonical_name), timeout=15) + response = requests.get(self.PYPI_API_URL.format(package=package_canonical_name), timeout=self.pypi_timeout) except HTTPError as e: # pragma: nocover return False, e.message diff --git a/pip_upgrader/packages_upgrader.py b/pip_upgrader/packages_upgrader.py index ceaeed6..a692081 100644 --- a/pip_upgrader/packages_upgrader.py +++ b/pip_upgrader/packages_upgrader.py @@ -26,6 +26,11 @@ def __init__(self, selected_packages, requirements_files, options): skip_pkg_install = True # pragma: nocover self.skip_package_installation = skip_pkg_install + self.pypi_timeout = 15 + if options['timeout']: + if options['timeout'].isdecimal(): + self.pypi_timeout = int(options['timeout']) + def do_upgrade(self): for package in self.selected_packages: self._update_package(package) @@ -39,7 +44,7 @@ def _update_package(self, package): if not self.dry_run and not self.skip_package_installation: # pragma: nocover pinned = '{}=={}'.format(package['name'], package['latest_version']) - subprocess.check_call(['pip', 'install', pinned]) + subprocess.check_call(['pip', 'install', pinned, '--timeout', self.pypi_timeout]) else: # dry run has priority in messages if self.dry_run: diff --git a/tests/test_cli.py b/tests/test_cli.py index 4fe418c..1461651 100755 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -320,3 +320,16 @@ def test_command_not_interactive_not_virtualenv_skip(self, options_mock, is_virt self.assertNotIn('ipdb', output) self.assertNotIn('celery ... upgrade available: 3.1.1 ==>', output) self.assertIn('Successfully upgraded', output) + + @responses.activate + @patch('pip_upgrader.cli.get_options', return_value={'--timeout': '60', '-p': ['ipython']}) + def test_command_not_interactive_all_packages_up_to_date(self, options_mock, is_virtualenv_mock, user_input_mock): + with patch('sys.stdout', new_callable=StringIO) as stdout_mock: + cli.main() + output = stdout_mock.getvalue() + print(options_mock) + + # no user_input should be called + self.assertFalse(user_input_mock.called) + self.assertNotIn('Setting API url', output) + self.assertIn('All packages are up-to-date.', output)