Skip to content

Commit 02ca547

Browse files
authored
Merge pull request #18 from Paperspace/PS-9860-Check-for-new-version-on-execution
Add checking for new version
2 parents 5a20f9e + de86bef commit 02ca547

File tree

6 files changed

+187
-4
lines changed

6 files changed

+187
-4
lines changed

gradient/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from gradient import version_checker
12
from gradient_statsd import Client as StatsdClient
23

34
from .config import config
@@ -9,4 +10,5 @@
910

1011

1112
def main():
13+
version_checker.GradientVersionChecker.look_for_new_version_with_timeout()
1214
_cli_entry_point()

gradient/cli/cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def cli():
1919

2020

2121
@cli.command("version", help="Show the version and exit")
22-
def version():
22+
def get_version():
2323
command = login_commands.ShowVersionCommand()
2424
command.execute()
2525

gradient/logger.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ def log_error_response(data):
5454
error(str(message))
5555

5656

57-
def debug(messages):
57+
def debug(message):
5858
if config.DEBUG:
59-
log("DEBUG: {}".format(messages))
59+
log("DEBUG: {}".format(message))
6060

6161

6262
def log_response(response, success_msg, error_msg):

gradient/version_checker.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import platform
2+
import sys
3+
from distutils.version import StrictVersion
4+
5+
import six
6+
7+
from gradient import logger
8+
from gradient.version import version
9+
10+
if six.PY2:
11+
import xmlrpclib
12+
else:
13+
import xmlrpc.client as xmlrpclib
14+
15+
16+
class PackageNotFoundError(Exception):
17+
pass
18+
19+
20+
class VersionChecker(object):
21+
def is_up_to_date(self, module_name, current_version):
22+
version_in_repository = self.get_version_from_repository(module_name)
23+
24+
up_to_date = StrictVersion(current_version) >= StrictVersion(version_in_repository)
25+
return up_to_date, version_in_repository
26+
27+
def get_version_from_repository(self, module_name, repository_url="http://pypi.python.org/pypi"):
28+
pypi = xmlrpclib.ServerProxy(repository_url)
29+
versions = pypi.package_releases(module_name)
30+
if not versions:
31+
raise PackageNotFoundError("Package {} not found".format(module_name))
32+
33+
return versions[0]
34+
35+
36+
class GradientVersionChecker(object):
37+
@classmethod
38+
def look_for_new_version_with_timeout(cls):
39+
if not cls._should_check_version():
40+
return
41+
42+
if not platform.system() == "Linux":
43+
cls.look_for_new_version()
44+
return
45+
46+
import signal
47+
48+
class TimeoutError(Exception):
49+
pass
50+
51+
def handler(signum, frame):
52+
raise TimeoutError
53+
54+
signal.signal(signal.SIGALRM, handler)
55+
signal.alarm(1)
56+
57+
try:
58+
cls.look_for_new_version()
59+
except TimeoutError:
60+
pass
61+
62+
signal.alarm(0)
63+
64+
@staticmethod
65+
def look_for_new_version():
66+
vc = VersionChecker()
67+
try:
68+
up_to_date, version_from_repository = vc.is_up_to_date("gradient", version)
69+
except Exception as e:
70+
logger.debug(e)
71+
return
72+
73+
if not up_to_date:
74+
msg = "Warning: this version of the Gradient CLI ({current_version}) is out of date. " \
75+
"Some functionality might not be supported until you upgrade. \n\n" \
76+
"Run `pip install -U gradient` to upgrade\n".format(current_version=version)
77+
logger.warning(msg)
78+
79+
@staticmethod
80+
def _should_check_version():
81+
if not hasattr(sys.stdin, "isatty"):
82+
return False
83+
if not sys.stdin.isatty() or not sys.stdout.isatty():
84+
return False
85+
86+
return True

tests/functional/test_machines.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -820,7 +820,7 @@ def test_should_send_valid_post_request_and_print_table_when_machines_list_was_u
820820
assert result.exit_code == 0
821821

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

826826
cli_runner = CliRunner()

tests/unit/test_version_checker.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import mock
2+
import pytest
3+
4+
from gradient import version_checker
5+
from gradient.version import version
6+
7+
8+
@mock.patch("gradient.version_checker.GradientVersionChecker._should_check_version")
9+
@mock.patch("gradient.version_checker.VersionChecker.is_up_to_date")
10+
@mock.patch("gradient.version_checker.logger")
11+
def test_should_check_for_new_gradient_version_and_print_proper_warning_when_current_version_is_old(
12+
logger_patched, is_up_to_date_patched, should_check_patched):
13+
should_check_patched.return_value = True
14+
is_up_to_date_patched.return_value = (False, "1.2.3")
15+
16+
version_checker.GradientVersionChecker.look_for_new_version()
17+
18+
is_up_to_date_patched.assert_called_once()
19+
logger_patched.warning.assert_called_once_with(
20+
"Warning: this version of the Gradient CLI ({}) is out of date. "
21+
"Some functionality might not be supported until you upgrade. \n\n"
22+
"Run `pip install -U gradient` to upgrade\n".format(version))
23+
24+
25+
@mock.patch("gradient.version_checker.GradientVersionChecker._should_check_version")
26+
@mock.patch("gradient.version_checker.VersionChecker.is_up_to_date")
27+
@mock.patch("gradient.version_checker.logger")
28+
def test_should_check_for_new_gradient_version_and_not_print_anything_when_current_version_is_latest(
29+
logger_patched, is_up_to_date_patched, should_check_patched):
30+
should_check_patched.return_value = True
31+
is_up_to_date_patched.return_value = (True, "1.2.3")
32+
33+
version_checker.GradientVersionChecker.look_for_new_version()
34+
35+
is_up_to_date_patched.assert_called_once()
36+
logger_patched.warning.assert_not_called()
37+
38+
39+
@mock.patch("gradient.version_checker.xmlrpclib.ServerProxy")
40+
def test_should_return_package_version_when_get_version_was_run(sever_proxy_class_patched):
41+
pypi_patched = mock.MagicMock()
42+
pypi_patched.package_releases.return_value = ["1.2.3"]
43+
sever_proxy_class_patched.return_value = pypi_patched
44+
45+
vc = version_checker.VersionChecker()
46+
latest_version = vc.get_version_from_repository("some_module_name")
47+
48+
sever_proxy_class_patched.assert_called_with("http://pypi.python.org/pypi")
49+
assert latest_version == "1.2.3"
50+
51+
52+
@mock.patch("gradient.version_checker.xmlrpclib.ServerProxy")
53+
def test_should_raise_proper_exception_when_get_version_was_run_and_package_was_not_found(sever_proxy_class_patched):
54+
pypi_patched = mock.MagicMock()
55+
pypi_patched.package_releases.return_value = []
56+
sever_proxy_class_patched.return_value = pypi_patched
57+
58+
vc = version_checker.VersionChecker()
59+
with pytest.raises(version_checker.PackageNotFoundError):
60+
vc.get_version_from_repository("some_module_name")
61+
62+
sever_proxy_class_patched.assert_called_with("http://pypi.python.org/pypi")
63+
64+
65+
@mock.patch("gradient.version_checker.VersionChecker.get_version_from_repository")
66+
def test_should_return_true_when_current_version_equals_latest_from_pypi(get_version_patched):
67+
get_version_patched.return_value = "1.2.3"
68+
69+
vc = version_checker.VersionChecker()
70+
up_to_date, version_in_repository = vc.is_up_to_date("some_module_name", "1.2.3")
71+
72+
assert up_to_date
73+
assert version_in_repository == "1.2.3"
74+
75+
76+
@mock.patch("gradient.version_checker.VersionChecker.get_version_from_repository")
77+
def test_should_return_true_when_current_version_is_higher_than_latest_from_pypi(get_version_patched):
78+
get_version_patched.return_value = "1.2.3"
79+
80+
vc = version_checker.VersionChecker()
81+
up_to_date, version_in_repository = vc.is_up_to_date("some_module_name", "1.2.4a0")
82+
83+
assert up_to_date
84+
assert version_in_repository == "1.2.3"
85+
86+
87+
@mock.patch("gradient.version_checker.VersionChecker.get_version_from_repository")
88+
def test_should_return_false_when_current_version_is_lower_than_latest_from_pypi(get_version_patched):
89+
get_version_patched.return_value = "1.2.3"
90+
91+
vc = version_checker.VersionChecker()
92+
up_to_date, version_in_repository = vc.is_up_to_date("some_module_name", "1.2.1")
93+
94+
assert not up_to_date
95+
assert version_in_repository == "1.2.3"

0 commit comments

Comments
 (0)