cache.py 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. """HTTP cache implementation.
  2. """
  3. # The following comment should be removed at some point in the future.
  4. # mypy: disallow-untyped-defs=False
  5. import os
  6. from contextlib import contextmanager
  7. from pip._vendor.cachecontrol.cache import BaseCache
  8. from pip._vendor.cachecontrol.caches import FileCache
  9. from pip._vendor.requests.models import Response
  10. from pip._internal.utils.filesystem import adjacent_tmp_file, replace
  11. from pip._internal.utils.misc import ensure_dir
  12. from pip._internal.utils.typing import MYPY_CHECK_RUNNING
  13. if MYPY_CHECK_RUNNING:
  14. from typing import Optional
  15. def is_from_cache(response):
  16. # type: (Response) -> bool
  17. return getattr(response, "from_cache", False)
  18. @contextmanager
  19. def suppressed_cache_errors():
  20. """If we can't access the cache then we can just skip caching and process
  21. requests as if caching wasn't enabled.
  22. """
  23. try:
  24. yield
  25. except (OSError, IOError):
  26. pass
  27. class SafeFileCache(BaseCache):
  28. """
  29. A file based cache which is safe to use even when the target directory may
  30. not be accessible or writable.
  31. """
  32. def __init__(self, directory):
  33. # type: (str) -> None
  34. assert directory is not None, "Cache directory must not be None."
  35. super(SafeFileCache, self).__init__()
  36. self.directory = directory
  37. def _get_cache_path(self, name):
  38. # type: (str) -> str
  39. # From cachecontrol.caches.file_cache.FileCache._fn, brought into our
  40. # class for backwards-compatibility and to avoid using a non-public
  41. # method.
  42. hashed = FileCache.encode(name)
  43. parts = list(hashed[:5]) + [hashed]
  44. return os.path.join(self.directory, *parts)
  45. def get(self, key):
  46. # type: (str) -> Optional[bytes]
  47. path = self._get_cache_path(key)
  48. with suppressed_cache_errors():
  49. with open(path, 'rb') as f:
  50. return f.read()
  51. def set(self, key, value):
  52. # type: (str, bytes) -> None
  53. path = self._get_cache_path(key)
  54. with suppressed_cache_errors():
  55. ensure_dir(os.path.dirname(path))
  56. with adjacent_tmp_file(path) as f:
  57. f.write(value)
  58. replace(f.name, path)
  59. def delete(self, key):
  60. # type: (str) -> None
  61. path = self._get_cache_path(key)
  62. with suppressed_cache_errors():
  63. os.remove(path)