core.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. """
  2. certifi.py
  3. ~~~~~~~~~~
  4. This module returns the installation location of cacert.pem or its contents.
  5. """
  6. import sys
  7. import atexit
  8. def exit_cacert_ctx() -> None:
  9. _CACERT_CTX.__exit__(None, None, None) # type: ignore[union-attr]
  10. if sys.version_info >= (3, 11):
  11. from importlib.resources import as_file, files
  12. _CACERT_CTX = None
  13. _CACERT_PATH = None
  14. def where() -> str:
  15. # This is slightly terrible, but we want to delay extracting the file
  16. # in cases where we're inside of a zipimport situation until someone
  17. # actually calls where(), but we don't want to re-extract the file
  18. # on every call of where(), so we'll do it once then store it in a
  19. # global variable.
  20. global _CACERT_CTX
  21. global _CACERT_PATH
  22. if _CACERT_PATH is None:
  23. # This is slightly janky, the importlib.resources API wants you to
  24. # manage the cleanup of this file, so it doesn't actually return a
  25. # path, it returns a context manager that will give you the path
  26. # when you enter it and will do any cleanup when you leave it. In
  27. # the common case of not needing a temporary file, it will just
  28. # return the file system location and the __exit__() is a no-op.
  29. #
  30. # We also have to hold onto the actual context manager, because
  31. # it will do the cleanup whenever it gets garbage collected, so
  32. # we will also store that at the global level as well.
  33. _CACERT_CTX = as_file(files("certifi").joinpath("cacert.pem"))
  34. _CACERT_PATH = str(_CACERT_CTX.__enter__())
  35. atexit.register(exit_cacert_ctx)
  36. return _CACERT_PATH
  37. def contents() -> str:
  38. return files("certifi").joinpath("cacert.pem").read_text(encoding="ascii")
  39. elif sys.version_info >= (3, 7):
  40. from importlib.resources import path as get_path, read_text
  41. _CACERT_CTX = None
  42. _CACERT_PATH = None
  43. def where() -> str:
  44. # This is slightly terrible, but we want to delay extracting the
  45. # file in cases where we're inside of a zipimport situation until
  46. # someone actually calls where(), but we don't want to re-extract
  47. # the file on every call of where(), so we'll do it once then store
  48. # it in a global variable.
  49. global _CACERT_CTX
  50. global _CACERT_PATH
  51. if _CACERT_PATH is None:
  52. # This is slightly janky, the importlib.resources API wants you
  53. # to manage the cleanup of this file, so it doesn't actually
  54. # return a path, it returns a context manager that will give
  55. # you the path when you enter it and will do any cleanup when
  56. # you leave it. In the common case of not needing a temporary
  57. # file, it will just return the file system location and the
  58. # __exit__() is a no-op.
  59. #
  60. # We also have to hold onto the actual context manager, because
  61. # it will do the cleanup whenever it gets garbage collected, so
  62. # we will also store that at the global level as well.
  63. _CACERT_CTX = get_path("certifi", "cacert.pem")
  64. _CACERT_PATH = str(_CACERT_CTX.__enter__())
  65. atexit.register(exit_cacert_ctx)
  66. return _CACERT_PATH
  67. def contents() -> str:
  68. return read_text("certifi", "cacert.pem", encoding="ascii")
  69. else:
  70. import os
  71. import types
  72. from typing import Union
  73. Package = Union[types.ModuleType, str]
  74. Resource = Union[str, "os.PathLike"]
  75. # This fallback will work for Python versions prior to 3.7 that lack the
  76. # importlib.resources module but relies on the existing `where` function
  77. # so won't address issues with environments like PyOxidizer that don't set
  78. # __file__ on modules.
  79. def read_text(
  80. package: Package,
  81. resource: Resource,
  82. encoding: str = 'utf-8',
  83. errors: str = 'strict'
  84. ) -> str:
  85. with open(where(), encoding=encoding) as data:
  86. return data.read()
  87. # If we don't have importlib.resources, then we will just do the old logic
  88. # of assuming we're on the filesystem and munge the path directly.
  89. def where() -> str:
  90. f = os.path.dirname(__file__)
  91. return os.path.join(f, "cacert.pem")
  92. def contents() -> str:
  93. return read_text("certifi", "cacert.pem", encoding="ascii")