Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions gradient/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from gradient import version_checker
from gradient_statsd import Client as StatsdClient

from .config import config
Expand All @@ -9,4 +10,5 @@


def main():
version_checker.GradientVersionChecker.look_for_new_version_with_timeout()
_cli_entry_point()
2 changes: 1 addition & 1 deletion gradient/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def cli():


@cli.command("version", help="Show the version and exit")
def version():
def get_version():
command = login_commands.ShowVersionCommand()
command.execute()

Expand Down
4 changes: 2 additions & 2 deletions gradient/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ def log_error_response(data):
error(str(message))


def debug(messages):
def debug(message):
if config.DEBUG:
log("DEBUG: {}".format(messages))
log("DEBUG: {}".format(message))


def log_response(response, success_msg, error_msg):
Expand Down
86 changes: 86 additions & 0 deletions gradient/version_checker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import platform
import sys
from distutils.version import StrictVersion

import six

from gradient import logger
from gradient.version import version

if six.PY2:
import xmlrpclib
else:
import xmlrpc.client as xmlrpclib


class PackageNotFoundError(Exception):
pass


class VersionChecker(object):
def is_up_to_date(self, module_name, current_version):
version_in_repository = self.get_version_from_repository(module_name)

up_to_date = StrictVersion(current_version) >= StrictVersion(version_in_repository)
return up_to_date, version_in_repository

def get_version_from_repository(self, module_name, repository_url="http://pypi.python.org/pypi"):
pypi = xmlrpclib.ServerProxy(repository_url)
versions = pypi.package_releases(module_name)
if not versions:
raise PackageNotFoundError("Package {} not found".format(module_name))

return versions[0]


class GradientVersionChecker(object):
@classmethod
def look_for_new_version_with_timeout(cls):
if not cls._should_check_version():
return

if not platform.system() == "Linux":
cls.look_for_new_version()
return

import signal

class TimeoutError(Exception):
pass

def handler(signum, frame):
raise TimeoutError

signal.signal(signal.SIGALRM, handler)
signal.alarm(1)

try:
cls.look_for_new_version()
except TimeoutError:
pass

signal.alarm(0)

@staticmethod
def look_for_new_version():
vc = VersionChecker()
try:
up_to_date, version_from_repository = vc.is_up_to_date("gradient", version)
except Exception as e:
logger.debug(e)
return

if not up_to_date:
msg = "Warning: this version of the Gradient CLI ({current_version}) is out of date. " \
"Some functionality might not be supported until you upgrade. \n\n" \
"Run `pip install -U gradient` to upgrade\n".format(current_version=version)
logger.warning(msg)

@staticmethod
def _should_check_version():
if not hasattr(sys.stdin, "isatty"):
return False
if not sys.stdin.isatty() or not sys.stdout.isatty():
return False

return True
2 changes: 1 addition & 1 deletion tests/functional/test_machines.py
Original file line number Diff line number Diff line change
Expand Up @@ -820,7 +820,7 @@ def test_should_send_valid_post_request_and_print_table_when_machines_list_was_u
assert result.exit_code == 0

@mock.patch("gradient.client.requests.get")
def test_should_send_valid_post_request_when_machines_list_was_used_with_api_key_option(self, get_patched):
def test_should_send_valid_post_request_when_machines_show_was_used_with_api_key_option(self, get_patched):
get_patched.return_value = MockResponse(json_data=self.EXPECTED_RESPONSE_JSON, status_code=200)

cli_runner = CliRunner()
Expand Down
95 changes: 95 additions & 0 deletions tests/unit/test_version_checker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import mock
import pytest

from gradient import version_checker
from gradient.version import version


@mock.patch("gradient.version_checker.GradientVersionChecker._should_check_version")
@mock.patch("gradient.version_checker.VersionChecker.is_up_to_date")
@mock.patch("gradient.version_checker.logger")
def test_should_check_for_new_gradient_version_and_print_proper_warning_when_current_version_is_old(
logger_patched, is_up_to_date_patched, should_check_patched):
should_check_patched.return_value = True
is_up_to_date_patched.return_value = (False, "1.2.3")

version_checker.GradientVersionChecker.look_for_new_version()

is_up_to_date_patched.assert_called_once()
logger_patched.warning.assert_called_once_with(
"Warning: this version of the Gradient CLI ({}) is out of date. "
"Some functionality might not be supported until you upgrade. \n\n"
"Run `pip install -U gradient` to upgrade\n".format(version))


@mock.patch("gradient.version_checker.GradientVersionChecker._should_check_version")
@mock.patch("gradient.version_checker.VersionChecker.is_up_to_date")
@mock.patch("gradient.version_checker.logger")
def test_should_check_for_new_gradient_version_and_not_print_anything_when_current_version_is_latest(
logger_patched, is_up_to_date_patched, should_check_patched):
should_check_patched.return_value = True
is_up_to_date_patched.return_value = (True, "1.2.3")

version_checker.GradientVersionChecker.look_for_new_version()

is_up_to_date_patched.assert_called_once()
logger_patched.warning.assert_not_called()


@mock.patch("gradient.version_checker.xmlrpclib.ServerProxy")
def test_should_return_package_version_when_get_version_was_run(sever_proxy_class_patched):
pypi_patched = mock.MagicMock()
pypi_patched.package_releases.return_value = ["1.2.3"]
sever_proxy_class_patched.return_value = pypi_patched

vc = version_checker.VersionChecker()
latest_version = vc.get_version_from_repository("some_module_name")

sever_proxy_class_patched.assert_called_with("http://pypi.python.org/pypi")
assert latest_version == "1.2.3"


@mock.patch("gradient.version_checker.xmlrpclib.ServerProxy")
def test_should_raise_proper_exception_when_get_version_was_run_and_package_was_not_found(sever_proxy_class_patched):
pypi_patched = mock.MagicMock()
pypi_patched.package_releases.return_value = []
sever_proxy_class_patched.return_value = pypi_patched

vc = version_checker.VersionChecker()
with pytest.raises(version_checker.PackageNotFoundError):
vc.get_version_from_repository("some_module_name")

sever_proxy_class_patched.assert_called_with("http://pypi.python.org/pypi")


@mock.patch("gradient.version_checker.VersionChecker.get_version_from_repository")
def test_should_return_true_when_current_version_equals_latest_from_pypi(get_version_patched):
get_version_patched.return_value = "1.2.3"

vc = version_checker.VersionChecker()
up_to_date, version_in_repository = vc.is_up_to_date("some_module_name", "1.2.3")

assert up_to_date
assert version_in_repository == "1.2.3"


@mock.patch("gradient.version_checker.VersionChecker.get_version_from_repository")
def test_should_return_true_when_current_version_is_higher_than_latest_from_pypi(get_version_patched):
get_version_patched.return_value = "1.2.3"

vc = version_checker.VersionChecker()
up_to_date, version_in_repository = vc.is_up_to_date("some_module_name", "1.2.4a0")

assert up_to_date
assert version_in_repository == "1.2.3"


@mock.patch("gradient.version_checker.VersionChecker.get_version_from_repository")
def test_should_return_false_when_current_version_is_lower_than_latest_from_pypi(get_version_patched):
get_version_patched.return_value = "1.2.3"

vc = version_checker.VersionChecker()
up_to_date, version_in_repository = vc.is_up_to_date("some_module_name", "1.2.1")

assert not up_to_date
assert version_in_repository == "1.2.3"