Make included certifi and requests use same tmp file code and store under calibre tmp dir for cleanup.

This commit is contained in:
Jim Miller 2020-12-30 13:50:14 -06:00
parent 13fbf31f2c
commit 9f668e2653
2 changed files with 72 additions and 41 deletions

View file

@ -8,49 +8,55 @@ This module returns the installation location of cacert.pem or its contents.
""" """
import os import os
try: ## FFF change--`requests` already uses it's utils.extract_zipped_paths()
from importlib.resources import path as get_path, read_text ## on this, so the importlib.resources below is both redundant and
## sub-optimal--it apparently creates a new temp copy *every* time
## where() is called...
_CACERT_CTX = None # try:
_CACERT_PATH = None # from importlib.resources import path as get_path, read_text
def where(): # _CACERT_CTX = None
# This is slightly terrible, but we want to delay extracting the file # _CACERT_PATH = None
# in cases where we're inside of a zipimport situation until someone
# actually calls where(), but we don't want to re-extract the file
# on every call of where(), so we'll do it once then store it in a
# global variable.
global _CACERT_CTX
global _CACERT_PATH
if _CACERT_PATH is None:
# This is slightly janky, the importlib.resources API wants you to
# manage the cleanup of this file, so it doesn't actually return a
# path, it returns a context manager that will give you the path
# when you enter it and will do any cleanup when you leave it. In
# the common case of not needing a temporary file, it will just
# return the file system location and the __exit__() is a no-op.
#
# We also have to hold onto the actual context manager, because
# it will do the cleanup whenever it gets garbage collected, so
# we will also store that at the global level as well.
_CACERT_CTX = get_path("certifi", "cacert.pem")
_CACERT_PATH = str(_CACERT_CTX.__enter__())
return _CACERT_PATH # def where():
# # This is slightly terrible, but we want to delay extracting the file
# # in cases where we're inside of a zipimport situation until someone
# # actually calls where(), but we don't want to re-extract the file
# # on every call of where(), so we'll do it once then store it in a
# # global variable.
# global _CACERT_CTX
# global _CACERT_PATH
# if _CACERT_PATH is None:
# # This is slightly janky, the importlib.resources API wants you to
# # manage the cleanup of this file, so it doesn't actually return a
# # path, it returns a context manager that will give you the path
# # when you enter it and will do any cleanup when you leave it. In
# # the common case of not needing a temporary file, it will just
# # return the file system location and the __exit__() is a no-op.
# #
# # We also have to hold onto the actual context manager, because
# # it will do the cleanup whenever it gets garbage collected, so
# # we will also store that at the global level as well.
# _CACERT_CTX = get_path("certifi", "cacert.pem")
# _CACERT_PATH = str(_CACERT_CTX.__enter__())
# return _CACERT_PATH
except ImportError: # except ImportError:
# This fallback will work for Python versions prior to 3.7 that lack the
# importlib.resources module but relies on the existing `where` function # This fallback will work for Python versions prior to 3.7 that lack the
# so won't address issues with environments like PyOxidizer that don't set # importlib.resources module but relies on the existing `where` function
# __file__ on modules. # so won't address issues with environments like PyOxidizer that don't set
def read_text(_module, _path, encoding="ascii"): # __file__ on modules.
def read_text(_module, _path, encoding="ascii"):
with open(where(), "r", encoding=encoding) as data: with open(where(), "r", encoding=encoding) as data:
return data.read() return data.read()
# If we don't have importlib.resources, then we will just do the old logic # If we don't have importlib.resources, then we will just do the old logic
# of assuming we're on the filesystem and munge the path directly. # of assuming we're on the filesystem and munge the path directly.
def where(): def where():
f = os.path.dirname(__file__) f = os.path.dirname(__file__)
return os.path.join(f, "cacert.pem") return os.path.join(f, "cacert.pem")

View file

@ -230,8 +230,32 @@ def guess_filename(obj):
name[-1] != '>'): name[-1] != '>'):
return os.path.basename(name) return os.path.basename(name)
def extract_zipped_paths(path): def extract_zipped_paths(path):
'''
FanFicFare calibre plugin code should extract only one copy of
each file and always to <calibre temp dir>/fanficfare_files/...
orig_extract_zipped_paths() extracts to <tmp>/package/file, which
is *not* compared for age or anything. So it would be a hard bug
to track down when the bundled file changes but the code continues
to use the old tmp version anyway. Putting it under <calibre temp
dir> means it will be renewed each Calibre restart.
orig_extract_zipped_paths is a bit inefficient in the way it
checks inside the zip file each time, but it's not bad enough to
completely re-do.
'''
try:
from calibre.ptempfile import base_dir as calibre_base_dir
caltmpdir = os.path.join(calibre_base_dir(),"fanficfare_files")
if not os.path.isdir(caltmpdir):
os.mkdir(caltmpdir)
except:
caltmpdir = None
return orig_extract_zipped_paths(path,tmp=caltmpdir)
def orig_extract_zipped_paths(path,tmp=None):
"""Replace nonexistent paths that look like they refer to a member of a zip """Replace nonexistent paths that look like they refer to a member of a zip
archive with the location of an extracted copy of the target, or else archive with the location of an extracted copy of the target, or else
just return the provided path unchanged. just return the provided path unchanged.
@ -255,6 +279,7 @@ def extract_zipped_paths(path):
return path return path
# we have a valid zip archive and a valid member of that archive # we have a valid zip archive and a valid member of that archive
if not tmp:
tmp = tempfile.gettempdir() tmp = tempfile.gettempdir()
extracted_path = os.path.join(tmp, *member.split('/')) extracted_path = os.path.join(tmp, *member.split('/'))
if not os.path.exists(extracted_path): if not os.path.exists(extracted_path):