cache.py 2.2 KB

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