outdated.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. from __future__ import absolute_import
  2. import datetime
  3. import json
  4. import logging
  5. import os.path
  6. import sys
  7. from pip._vendor import lockfile
  8. from pip._vendor.packaging import version as packaging_version
  9. from pip._internal.compat import WINDOWS
  10. from pip._internal.index import PackageFinder
  11. from pip._internal.locations import USER_CACHE_DIR, running_under_virtualenv
  12. from pip._internal.utils.filesystem import check_path_owner
  13. from pip._internal.utils.misc import ensure_dir, get_installed_version
  14. SELFCHECK_DATE_FMT = "%Y-%m-%dT%H:%M:%SZ"
  15. logger = logging.getLogger(__name__)
  16. class VirtualenvSelfCheckState(object):
  17. def __init__(self):
  18. self.statefile_path = os.path.join(sys.prefix, "pip-selfcheck.json")
  19. # Load the existing state
  20. try:
  21. with open(self.statefile_path) as statefile:
  22. self.state = json.load(statefile)
  23. except (IOError, ValueError):
  24. self.state = {}
  25. def save(self, pypi_version, current_time):
  26. # Attempt to write out our version check file
  27. with open(self.statefile_path, "w") as statefile:
  28. json.dump(
  29. {
  30. "last_check": current_time.strftime(SELFCHECK_DATE_FMT),
  31. "pypi_version": pypi_version,
  32. },
  33. statefile,
  34. sort_keys=True,
  35. separators=(",", ":")
  36. )
  37. class GlobalSelfCheckState(object):
  38. def __init__(self):
  39. self.statefile_path = os.path.join(USER_CACHE_DIR, "selfcheck.json")
  40. # Load the existing state
  41. try:
  42. with open(self.statefile_path) as statefile:
  43. self.state = json.load(statefile)[sys.prefix]
  44. except (IOError, ValueError, KeyError):
  45. self.state = {}
  46. def save(self, pypi_version, current_time):
  47. # Check to make sure that we own the directory
  48. if not check_path_owner(os.path.dirname(self.statefile_path)):
  49. return
  50. # Now that we've ensured the directory is owned by this user, we'll go
  51. # ahead and make sure that all our directories are created.
  52. ensure_dir(os.path.dirname(self.statefile_path))
  53. # Attempt to write out our version check file
  54. with lockfile.LockFile(self.statefile_path):
  55. if os.path.exists(self.statefile_path):
  56. with open(self.statefile_path) as statefile:
  57. state = json.load(statefile)
  58. else:
  59. state = {}
  60. state[sys.prefix] = {
  61. "last_check": current_time.strftime(SELFCHECK_DATE_FMT),
  62. "pypi_version": pypi_version,
  63. }
  64. with open(self.statefile_path, "w") as statefile:
  65. json.dump(state, statefile, sort_keys=True,
  66. separators=(",", ":"))
  67. def load_selfcheck_statefile():
  68. if running_under_virtualenv():
  69. return VirtualenvSelfCheckState()
  70. else:
  71. return GlobalSelfCheckState()
  72. def pip_version_check(session, options):
  73. """Check for an update for pip.
  74. Limit the frequency of checks to once per week. State is stored either in
  75. the active virtualenv or in the user's USER_CACHE_DIR keyed off the prefix
  76. of the pip script path.
  77. """
  78. installed_version = get_installed_version("pip")
  79. if not installed_version:
  80. return
  81. pip_version = packaging_version.parse(installed_version)
  82. pypi_version = None
  83. try:
  84. state = load_selfcheck_statefile()
  85. current_time = datetime.datetime.utcnow()
  86. # Determine if we need to refresh the state
  87. if "last_check" in state.state and "pypi_version" in state.state:
  88. last_check = datetime.datetime.strptime(
  89. state.state["last_check"],
  90. SELFCHECK_DATE_FMT
  91. )
  92. if (current_time - last_check).total_seconds() < 7 * 24 * 60 * 60:
  93. pypi_version = state.state["pypi_version"]
  94. # Refresh the version if we need to or just see if we need to warn
  95. if pypi_version is None:
  96. # Lets use PackageFinder to see what the latest pip version is
  97. finder = PackageFinder(
  98. find_links=options.find_links,
  99. index_urls=[options.index_url] + options.extra_index_urls,
  100. allow_all_prereleases=False, # Explicitly set to False
  101. trusted_hosts=options.trusted_hosts,
  102. process_dependency_links=options.process_dependency_links,
  103. session=session,
  104. )
  105. all_candidates = finder.find_all_candidates("pip")
  106. if not all_candidates:
  107. return
  108. pypi_version = str(
  109. max(all_candidates, key=lambda c: c.version).version
  110. )
  111. # save that we've performed a check
  112. state.save(pypi_version, current_time)
  113. remote_version = packaging_version.parse(pypi_version)
  114. # Determine if our pypi_version is older
  115. if (pip_version < remote_version and
  116. pip_version.base_version != remote_version.base_version):
  117. # Advise "python -m pip" on Windows to avoid issues
  118. # with overwriting pip.exe.
  119. if WINDOWS:
  120. pip_cmd = "python -m pip"
  121. else:
  122. pip_cmd = "pip"
  123. logger.warning(
  124. "You are using pip version %s, however version %s is "
  125. "available.\nYou should consider upgrading via the "
  126. "'%s install --upgrade pip' command.",
  127. pip_version, pypi_version, pip_cmd
  128. )
  129. except Exception:
  130. logger.debug(
  131. "There was an error checking the latest version of pip",
  132. exc_info=True,
  133. )