from __future__ import absolute_import
from pip._vendor import lockfile
from pip._vendor.packaging import version as packaging_version
from pip.compat import total_seconds, WINDOWS
from pip.models import PyPI
from pip.locations import USER_CACHE_DIR, running_under_virtualenv
from pip.utils import ensure_dir, get_installed_version
from pip.utils.filesystem import check_path_owner
SELFCHECK_DATE_FMT = "%Y-%m-%dT%H:%M:%SZ"
logger = logging.getLogger(__name__)
class VirtualenvSelfCheckState(object):
self.statefile_path = os.path.join(sys.prefix, "pip-selfcheck.json")
# Load the existing state
with open(self.statefile_path) as statefile:
self.state = json.load(statefile)
except (IOError, ValueError):
def save(self, pypi_version, current_time):
# Attempt to write out our version check file
with open(self.statefile_path, "w") as statefile:
"last_check": current_time.strftime(SELFCHECK_DATE_FMT),
"pypi_version": pypi_version,
class GlobalSelfCheckState(object):
self.statefile_path = os.path.join(USER_CACHE_DIR, "selfcheck.json")
# Load the existing state
with open(self.statefile_path) as statefile:
self.state = json.load(statefile)[sys.prefix]
except (IOError, ValueError, KeyError):
def save(self, pypi_version, current_time):
# Check to make sure that we own the directory
if not check_path_owner(os.path.dirname(self.statefile_path)):
# Now that we've ensured the directory is owned by this user, we'll go
# ahead and make sure that all our directories are created.
ensure_dir(os.path.dirname(self.statefile_path))
# Attempt to write out our version check file
with lockfile.LockFile(self.statefile_path):
if os.path.exists(self.statefile_path):
with open(self.statefile_path) as statefile:
state = json.load(statefile)
"last_check": current_time.strftime(SELFCHECK_DATE_FMT),
"pypi_version": pypi_version,
with open(self.statefile_path, "w") as statefile:
json.dump(state, statefile, sort_keys=True,
def load_selfcheck_statefile():
if running_under_virtualenv():
return VirtualenvSelfCheckState()
return GlobalSelfCheckState()
def pip_installed_by_pip():
"""Checks whether pip was installed by pip
This is used not to display the upgrade message when pip is in fact
installed by system package manager, such as dnf on Fedora.
dist = pkg_resources.get_distribution('pip')
return (dist.has_metadata('INSTALLER') and
'pip' in dist.get_metadata_lines('INSTALLER'))
except pkg_resources.DistributionNotFound:
def pip_version_check(session):
"""Check for an update for pip.
Limit the frequency of checks to once per week. State is stored either in
the active virtualenv or in the user's USER_CACHE_DIR keyed off the prefix
installed_version = get_installed_version("pip")
if installed_version is None:
pip_version = packaging_version.parse(installed_version)
state = load_selfcheck_statefile()
current_time = datetime.datetime.utcnow()
# Determine if we need to refresh the state
if "last_check" in state.state and "pypi_version" in state.state:
last_check = datetime.datetime.strptime(
state.state["last_check"],
if total_seconds(current_time - last_check) < 7 * 24 * 60 * 60:
pypi_version = state.state["pypi_version"]
# Refresh the version if we need to or just see if we need to warn
headers={"Accept": "application/json"},
list(resp.json()["releases"]),
key=packaging_version.parse,
if not packaging_version.parse(v).is_prerelease
# save that we've performed a check
state.save(pypi_version, current_time)
remote_version = packaging_version.parse(pypi_version)
# Determine if our pypi_version is older
if (pip_version < remote_version and
pip_version.base_version != remote_version.base_version and
# Advise "python -m pip" on Windows to avoid issues
# with overwriting pip.exe.
pip_cmd = "python -m pip"
"You are using pip version %s, however version %s is "
"available.\nYou should consider upgrading via the "
"'%s install --upgrade pip' command.",
pip_version, pypi_version, pip_cmd
"There was an error checking the latest version of pip",