From 6ce565c229586d2d810b5f077df0a67755cef019 Mon Sep 17 00:00:00 2001 From: Donald Stufft Date: Wed, 25 Sep 2013 01:44:34 -0400 Subject: [PATCH 1/6] Once a day check to see what the latest version of pip is --- pip/basecommand.py | 13 ++++++++++++- pip/util.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/pip/basecommand.py b/pip/basecommand.py index 2f081253089..d32187155ba 100644 --- a/pip/basecommand.py +++ b/pip/basecommand.py @@ -16,7 +16,7 @@ from pip.baseparser import ConfigOptionParser, UpdatingDefaultsHelpFormatter from pip.status_codes import (SUCCESS, ERROR, UNKNOWN_ERROR, VIRTUALENV_NOT_FOUND, PREVIOUS_BUILD_DIR_ERROR) -from pip.util import get_prog +from pip.util import get_prog, latest_version_check __all__ = ['Command'] @@ -115,6 +115,17 @@ def main(self, args): else: log_fp = None + # Check if we're using the latest version of pip available + try: + vers = latest_version_check(session=self._build_session(options)) + if vers is not None: + logger.warn("You are using pip version %s, however version %s " + "is available. You should consider upgrading." + % vers) + except Exception as exc: + logger.info("There was an error checking the latest version of " + "pip: %s" % str(exc)) + exit = SUCCESS store_log = False try: diff --git a/pip/util.py b/pip/util.py index 58336e59e4e..6c59efa2ba2 100644 --- a/pip/util.py +++ b/pip/util.py @@ -1,3 +1,4 @@ +import datetime import sys import shutil import os @@ -717,3 +718,44 @@ def is_prerelease(vers): parsed = version._normalized_key(normalized) return any([any([y in set(["a", "b", "c", "rc", "dev"]) for y in x]) for x in parsed]) + + +def latest_version_check(session, frequency=24 * 60 * 60): + """ + Attempt to determine if we're using the latest version of pip or not. Keeps + a small state file stored in ~/.pip/ to store when the last time we checked + was. This enables us to only display the warning every so often. + """ + import pip # imported here to prevent circular imports + + storage_format = "%Y-%m-%dT%H:%M:%S" + statefile_path = os.path.expanduser(os.path.join("~/.pip/versioncheck")) + current_time = datetime.datetime.utcnow() + try: + with open(statefile_path) as statefile: + last_check = datetime.datetime.strptime( + statefile.read().strip(), + storage_format, + ) + + if (current_time - last_check).total_seconds() < frequency: + return + except (IOError, ValueError): + pass + + # If we've made it this far it means we need to check the version + resp = session.get("https://pypi.python.org/pypi/pip/json", + headers={"Accept": "application/json"}, + ) + resp.raise_for_status() + latest_pip = resp.json()["info"]["version"] + + need_update = (pkg_resources.parse_version(pip.__version__) + < pkg_resources.parse_version(latest_pip)) + + try: + with open(statefile_path, "w") as statefile: + statefile.write(current_time.strftime(storage_format)) + finally: + if need_update: + return (pip.__version__, latest_pip) From d5d8974e857d3445371b97a304957d29a95b2e02 Mon Sep 17 00:00:00 2001 From: Donald Stufft Date: Sat, 28 Sep 2013 12:14:19 -0400 Subject: [PATCH 2/6] Add an option to disable the version check --- pip/basecommand.py | 21 ++++++++++++--------- pip/cmdoptions.py | 8 ++++++++ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/pip/basecommand.py b/pip/basecommand.py index d32187155ba..a30e868d4fb 100644 --- a/pip/basecommand.py +++ b/pip/basecommand.py @@ -116,15 +116,18 @@ def main(self, args): log_fp = None # Check if we're using the latest version of pip available - try: - vers = latest_version_check(session=self._build_session(options)) - if vers is not None: - logger.warn("You are using pip version %s, however version %s " - "is available. You should consider upgrading." - % vers) - except Exception as exc: - logger.info("There was an error checking the latest version of " - "pip: %s" % str(exc)) + if not options.disable_version_check: + try: + vers = latest_version_check( + session=self._build_session(options) + ) + if vers is not None: + logger.warn("You are using pip version %s, however version" + " %s is available. You should consider " + "upgrading." % vers) + except Exception as exc: + logger.info("There was an error checking the latest version of" + " pip: %s" % str(exc)) exit = SUCCESS store_log = False diff --git a/pip/cmdoptions.py b/pip/cmdoptions.py index 1cc4811a18f..bffd90dec97 100644 --- a/pip/cmdoptions.py +++ b/pip/cmdoptions.py @@ -304,6 +304,13 @@ def make(self): default=False, help="Don't clean up build directories.") +disable_version_check = OptionMaker( + "--disable-version-check", + dest="disable_version_check", + action="store_true", + default=False, + help="Don't check PyPI for new versions of pip.") + ########## # groups # @@ -327,6 +334,7 @@ def make(self): skip_requirements_regex, exists_action, cert, + disable_version_check, ] } From df89155428a0b1e4b833757afcaa51c06843b4cd Mon Sep 17 00:00:00 2001 From: Donald Stufft Date: Sat, 28 Sep 2013 12:23:30 -0400 Subject: [PATCH 3/6] Fix the logging tests --- tests/unit/test_basecommand.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/unit/test_basecommand.py b/tests/unit/test_basecommand.py index 864510f9283..67e07fdac2f 100644 --- a/tests/unit/test_basecommand.py +++ b/tests/unit/test_basecommand.py @@ -6,9 +6,15 @@ class FakeCommand(Command): name = 'fake' summary = name + def __init__(self, error=False): self.error = error super(FakeCommand, self).__init__() + + def main(self, args): + args.append("--disable-version-check") + return super(FakeCommand, self).main(args) + def run(self, options, args): logger.info("fake") if self.error: From 60c8687d95b441ba26d393a00b28e3d79454c317 Mon Sep 17 00:00:00 2001 From: Donald Stufft Date: Sun, 29 Sep 2013 20:02:34 -0400 Subject: [PATCH 4/6] Shuffle around some logic to make things a bit easier to understand --- pip/basecommand.py | 12 +------- pip/util.py | 70 +++++++++++++++++++++++++++------------------- 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/pip/basecommand.py b/pip/basecommand.py index a30e868d4fb..4508963b929 100644 --- a/pip/basecommand.py +++ b/pip/basecommand.py @@ -117,17 +117,7 @@ def main(self, args): # Check if we're using the latest version of pip available if not options.disable_version_check: - try: - vers = latest_version_check( - session=self._build_session(options) - ) - if vers is not None: - logger.warn("You are using pip version %s, however version" - " %s is available. You should consider " - "upgrading." % vers) - except Exception as exc: - logger.info("There was an error checking the latest version of" - " pip: %s" % str(exc)) + latest_version_check(session=self._build_session(options)) exit = SUCCESS store_log = False diff --git a/pip/util.py b/pip/util.py index 6c59efa2ba2..9b9e5f9babc 100644 --- a/pip/util.py +++ b/pip/util.py @@ -726,36 +726,48 @@ def latest_version_check(session, frequency=24 * 60 * 60): a small state file stored in ~/.pip/ to store when the last time we checked was. This enables us to only display the warning every so often. """ - import pip # imported here to prevent circular imports - - storage_format = "%Y-%m-%dT%H:%M:%S" - statefile_path = os.path.expanduser(os.path.join("~/.pip/versioncheck")) - current_time = datetime.datetime.utcnow() try: - with open(statefile_path) as statefile: - last_check = datetime.datetime.strptime( - statefile.read().strip(), - storage_format, - ) - - if (current_time - last_check).total_seconds() < frequency: - return - except (IOError, ValueError): - pass + import pip # imported here to prevent circular imports - # If we've made it this far it means we need to check the version - resp = session.get("https://pypi.python.org/pypi/pip/json", - headers={"Accept": "application/json"}, - ) - resp.raise_for_status() - latest_pip = resp.json()["info"]["version"] + storage_format = "%Y-%m-%dT%H:%M:%S" + statefile_path = os.path.expanduser( + os.path.join("~/.pip/versioncheck"), + ) + current_time = datetime.datetime.utcnow() + try: + with open(statefile_path) as statefile: + last_check = datetime.datetime.strptime( + statefile.read().strip(), + storage_format, + ) + + if (current_time - last_check).total_seconds() < frequency: + return + except (IOError, ValueError): + pass - need_update = (pkg_resources.parse_version(pip.__version__) - < pkg_resources.parse_version(latest_pip)) + # If we've made it this far it means we need to check the version + resp = session.get("https://pypi.python.org/pypi/pip/json", + headers={"Accept": "application/json"}, + ) + resp.raise_for_status() + latest_pip = resp.json()["info"]["version"] - try: - with open(statefile_path, "w") as statefile: - statefile.write(current_time.strftime(storage_format)) - finally: - if need_update: - return (pip.__version__, latest_pip) + need_update = (pkg_resources.parse_version(pip.__version__) + < pkg_resources.parse_version(latest_pip)) + + try: + with open(statefile_path, "w") as statefile: + statefile.write(current_time.strftime(storage_format)) + finally: + if need_update: + logger.warn( + "You are using pip version %s, however version" + " %s is available. You should consider upgrading via the " + "pip install --upgrade pip command." % + (pip.__version__, latest_pip)) + except Exception: + logger.debug( + "There was an error checking the latest version of pip", + exc_info=True, + ) From 98cfeb6a3c41dfec408b58911ba20f2ec0cb895c Mon Sep 17 00:00:00 2001 From: Donald Stufft Date: Sat, 26 Oct 2013 23:29:28 -0400 Subject: [PATCH 5/6] Make This work a bit saner --- pip/util.py | 70 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/pip/util.py b/pip/util.py index 9b9e5f9babc..b5557fcef8b 100644 --- a/pip/util.py +++ b/pip/util.py @@ -1,4 +1,5 @@ import datetime +import json import sys import shutil import os @@ -724,48 +725,57 @@ def latest_version_check(session, frequency=24 * 60 * 60): """ Attempt to determine if we're using the latest version of pip or not. Keeps a small state file stored in ~/.pip/ to store when the last time we checked - was. This enables us to only display the warning every so often. + was and what the version was. """ + pypi_version = None + try: import pip # imported here to prevent circular imports - storage_format = "%Y-%m-%dT%H:%M:%S" - statefile_path = os.path.expanduser( - os.path.join("~/.pip/versioncheck"), - ) current_time = datetime.datetime.utcnow() + + fmt = "%Y-%m-%dT%H:%M:%S" + statefile_path = os.path.expanduser("~/.pip/versioncheck") + + # Load the existing state try: with open(statefile_path) as statefile: - last_check = datetime.datetime.strptime( - statefile.read().strip(), - storage_format, - ) + state = json.load(statefile) + except (IOError, ValueError): + state = {} + # Determine if we need to refresh the state + if "last-check" in state and "version" in state: + last_check = datetime.datetime.strptime(state["last-check"], fmt) if (current_time - last_check).total_seconds() < frequency: - return - except (IOError, ValueError): - pass + pypi_version = state["version"] - # If we've made it this far it means we need to check the version - resp = session.get("https://pypi.python.org/pypi/pip/json", - headers={"Accept": "application/json"}, - ) - resp.raise_for_status() - latest_pip = resp.json()["info"]["version"] + # Refresh the version if we need to + if pypi_version is None: + resp = session.get("https://pypi.python.org/pypi/pip/json", + headers={"Accept": "application/json"}, + ) + resp.raise_for_status() + pypi_version = resp.json()["info"]["version"] + # Determine if our pypi_version is older need_update = (pkg_resources.parse_version(pip.__version__) - < pkg_resources.parse_version(latest_pip)) - - try: - with open(statefile_path, "w") as statefile: - statefile.write(current_time.strftime(storage_format)) - finally: - if need_update: - logger.warn( - "You are using pip version %s, however version" - " %s is available. You should consider upgrading via the " - "pip install --upgrade pip command." % - (pip.__version__, latest_pip)) + < pkg_resources.parse_version(pypi_version)) + if need_update: + logger.warn( + "You are using pip version %s, however version %s is " + "available. You should consider upgrading via the pip install " + "--upgrade pip command." % (pip.__version__, pypi_version)) + + # Attempt to write out our version check file + with open(statefile_path, "w") as statefile: + json.dump( + { + "last-check": current_time.strftime(fmt), + "pypi_version": pypi_version, + }, + statefile, + ) except Exception: logger.debug( "There was an error checking the latest version of pip", From 2f966e117672c0284b10b3622af171ee96e04651 Mon Sep 17 00:00:00 2001 From: Donald Stufft Date: Sat, 26 Oct 2013 23:29:50 -0400 Subject: [PATCH 6/6] We only need to check once a week --- pip/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pip/util.py b/pip/util.py index b5557fcef8b..2f751a3ee10 100644 --- a/pip/util.py +++ b/pip/util.py @@ -721,7 +721,7 @@ def is_prerelease(vers): return any([any([y in set(["a", "b", "c", "rc", "dev"]) for y in x]) for x in parsed]) -def latest_version_check(session, frequency=24 * 60 * 60): +def latest_version_check(session, frequency=7 * 24 * 60 * 60): """ Attempt to determine if we're using the latest version of pip or not. Keeps a small state file stored in ~/.pip/ to store when the last time we checked