Skip to content

Commit

Permalink
UPD: use single temporary directory for dynamic libraries
Browse files Browse the repository at this point in the history
ADD: automatically clean up temporary files on exit
ADD: module cache for tempimporter
  • Loading branch information
desty2k committed Jul 9, 2021
1 parent d56655a commit ece4fe9
Showing 1 changed file with 38 additions and 5 deletions.
43 changes: 38 additions & 5 deletions paker/_tempimporter.py
@@ -1,10 +1,17 @@
import os
import sys
import atexit
import ctypes
import shutil
import logging
import tempfile
import importlib.util
import importlib.machinery

module_cache = {}
logger = logging.getLogger("tempimporter")
paker_tempdir = os.path.join(tempfile.gettempdir(), "paker")


def load_dynamic(name, path, file=None):
"""Replaces deprecated imp.load_dynamic."""
Expand All @@ -16,18 +23,26 @@ def load_dynamic(name, path, file=None):


def import_module(data, initfuncname, fullname, filename):
"""Import module from temporary directory. Used when _memimporter is not available."""
"""Import module from temporary file. Used when _memimporter is not available."""
return load_library(data, fullname, filename, dlopen=False, initfuncname=initfuncname)


def load_library(data, fullname, filename, dlopen=True, initfuncname=None):
"""Create temporary directory and import module. Used for '.pyd', '.dll' and '.so' files."""
tempdir = tempfile.TemporaryDirectory()
tempdir_path = tempdir.name
filename = os.path.join(tempdir_path, filename)
"""Create temporary file on disk and import module. Used for '.pyd', '.dll' and '.so' files."""
logger.info("importing {} from temporary file".format(fullname))
if fullname in module_cache:
return module_cache[fullname][1]

if os.path.isfile(paker_tempdir):
os.remove(paker_tempdir)
if not os.path.isdir(paker_tempdir):
os.makedirs(paker_tempdir, exist_ok=True)

filename = os.path.join(paker_tempdir, filename)
try:
with open(filename, "wb+") as f:
f.write(data)
logger.debug("created temporary file {} for module {}".format(filename, fullname))

if dlopen:
result = ctypes.CDLL(filename)
Expand All @@ -38,4 +53,22 @@ def load_library(data, fullname, filename, dlopen=True, initfuncname=None):
result = load_dynamic(fullname, filename)
except Exception as e:
raise e

if len(module_cache) == 0:
logger.debug("registering cleanup atexit function")
atexit.register(_cleanup)
module_cache[fullname] = (filename, result)
return result


def _cleanup():
"""Python does not support unloading dynamic libraries in runtime, so
cleanup is ran once at exit."""
for module in module_cache.values():
info = "module '{}' from '{}'".format(module[1].__name__, module[0])
try:
logger.debug("removing {}".format(info))
os.remove(module[0])
except Exception as e:
logger.error("failed to remove {}: {}".format(info, e))
shutil.rmtree(paker_tempdir, ignore_errors=True)

0 comments on commit ece4fe9

Please sign in to comment.