jpayne@7: """ jpayne@7: certifi.py jpayne@7: ~~~~~~~~~~ jpayne@7: jpayne@7: This module returns the installation location of cacert.pem or its contents. jpayne@7: """ jpayne@7: import sys jpayne@7: import atexit jpayne@7: jpayne@7: def exit_cacert_ctx() -> None: jpayne@7: _CACERT_CTX.__exit__(None, None, None) # type: ignore[union-attr] jpayne@7: jpayne@7: jpayne@7: if sys.version_info >= (3, 11): jpayne@7: jpayne@7: from importlib.resources import as_file, files jpayne@7: jpayne@7: _CACERT_CTX = None jpayne@7: _CACERT_PATH = None jpayne@7: jpayne@7: def where() -> str: jpayne@7: # This is slightly terrible, but we want to delay extracting the file jpayne@7: # in cases where we're inside of a zipimport situation until someone jpayne@7: # actually calls where(), but we don't want to re-extract the file jpayne@7: # on every call of where(), so we'll do it once then store it in a jpayne@7: # global variable. jpayne@7: global _CACERT_CTX jpayne@7: global _CACERT_PATH jpayne@7: if _CACERT_PATH is None: jpayne@7: # This is slightly janky, the importlib.resources API wants you to jpayne@7: # manage the cleanup of this file, so it doesn't actually return a jpayne@7: # path, it returns a context manager that will give you the path jpayne@7: # when you enter it and will do any cleanup when you leave it. In jpayne@7: # the common case of not needing a temporary file, it will just jpayne@7: # return the file system location and the __exit__() is a no-op. jpayne@7: # jpayne@7: # We also have to hold onto the actual context manager, because jpayne@7: # it will do the cleanup whenever it gets garbage collected, so jpayne@7: # we will also store that at the global level as well. jpayne@7: _CACERT_CTX = as_file(files("certifi").joinpath("cacert.pem")) jpayne@7: _CACERT_PATH = str(_CACERT_CTX.__enter__()) jpayne@7: atexit.register(exit_cacert_ctx) jpayne@7: jpayne@7: return _CACERT_PATH jpayne@7: jpayne@7: def contents() -> str: jpayne@7: return files("certifi").joinpath("cacert.pem").read_text(encoding="ascii") jpayne@7: jpayne@7: elif sys.version_info >= (3, 7): jpayne@7: jpayne@7: from importlib.resources import path as get_path, read_text jpayne@7: jpayne@7: _CACERT_CTX = None jpayne@7: _CACERT_PATH = None jpayne@7: jpayne@7: def where() -> str: jpayne@7: # This is slightly terrible, but we want to delay extracting the jpayne@7: # file in cases where we're inside of a zipimport situation until jpayne@7: # someone actually calls where(), but we don't want to re-extract jpayne@7: # the file on every call of where(), so we'll do it once then store jpayne@7: # it in a global variable. jpayne@7: global _CACERT_CTX jpayne@7: global _CACERT_PATH jpayne@7: if _CACERT_PATH is None: jpayne@7: # This is slightly janky, the importlib.resources API wants you jpayne@7: # to manage the cleanup of this file, so it doesn't actually jpayne@7: # return a path, it returns a context manager that will give jpayne@7: # you the path when you enter it and will do any cleanup when jpayne@7: # you leave it. In the common case of not needing a temporary jpayne@7: # file, it will just return the file system location and the jpayne@7: # __exit__() is a no-op. jpayne@7: # jpayne@7: # We also have to hold onto the actual context manager, because jpayne@7: # it will do the cleanup whenever it gets garbage collected, so jpayne@7: # we will also store that at the global level as well. jpayne@7: _CACERT_CTX = get_path("certifi", "cacert.pem") jpayne@7: _CACERT_PATH = str(_CACERT_CTX.__enter__()) jpayne@7: atexit.register(exit_cacert_ctx) jpayne@7: jpayne@7: return _CACERT_PATH jpayne@7: jpayne@7: def contents() -> str: jpayne@7: return read_text("certifi", "cacert.pem", encoding="ascii") jpayne@7: jpayne@7: else: jpayne@7: import os jpayne@7: import types jpayne@7: from typing import Union jpayne@7: jpayne@7: Package = Union[types.ModuleType, str] jpayne@7: Resource = Union[str, "os.PathLike"] jpayne@7: jpayne@7: # This fallback will work for Python versions prior to 3.7 that lack the jpayne@7: # importlib.resources module but relies on the existing `where` function jpayne@7: # so won't address issues with environments like PyOxidizer that don't set jpayne@7: # __file__ on modules. jpayne@7: def read_text( jpayne@7: package: Package, jpayne@7: resource: Resource, jpayne@7: encoding: str = 'utf-8', jpayne@7: errors: str = 'strict' jpayne@7: ) -> str: jpayne@7: with open(where(), encoding=encoding) as data: jpayne@7: return data.read() jpayne@7: jpayne@7: # If we don't have importlib.resources, then we will just do the old logic jpayne@7: # of assuming we're on the filesystem and munge the path directly. jpayne@7: def where() -> str: jpayne@7: f = os.path.dirname(__file__) jpayne@7: jpayne@7: return os.path.join(f, "cacert.pem") jpayne@7: jpayne@7: def contents() -> str: jpayne@7: return read_text("certifi", "cacert.pem", encoding="ascii")