I am trying to load python wheels dynamically at runtime from memory without any temporary files touching the filesystem.
The solution below seems to be working for on Python3.10, but it is failing on Python3.8. Does anyone have any idea why or have a possible solution or fix that works for Python3.8?
<code>import sys
import zipfile
import argparse
from io import BytesIO
import importlib.util
import importlib.abc
import importlib.machinery
class InMemoryZipImporter(importlib.abc.MetaPathFinder, importlib.abc.Loader):
def __init__(self, wheel_data, wheel_name):
self.zip_file = zipfile.ZipFile(BytesIO(wheel_data))
self.wheel_name = wheel_name
self.module_cache = {}
def find_spec(self, fullname, path=None, target=None):
module_path = fullname.replace('.', '/') + '.py'
if module_path in self.zip_file.namelist():
return importlib.util.spec_from_loader(fullname, self)
return None
def create_module(self, spec):
return None
def exec_module(self, module):
module_path = module.__spec__.name.replace('.', '/') + '.py'
source = self.zip_file.read(module_path)
code = compile(source, module_path, 'exec')
exec(code, module.__dict__)
def load_wheel(wheel_path):
with open(wheel_path, 'rb') as f:
wheel_data = f.read()
wheel_name = wheel_path.split('/')[-1]
importer = InMemoryZipImporter(wheel_data, wheel_name)
sys.meta_path.insert(0, importer)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Dynamically load a Python wheel file.")
parser.add_argument('wheel_path', help="Path to the .whl file")
args = parser.parse_args()
load_wheel(args.wheel_path)
</code>
<code>import sys
import zipfile
import argparse
from io import BytesIO
import importlib.util
import importlib.abc
import importlib.machinery
class InMemoryZipImporter(importlib.abc.MetaPathFinder, importlib.abc.Loader):
def __init__(self, wheel_data, wheel_name):
self.zip_file = zipfile.ZipFile(BytesIO(wheel_data))
self.wheel_name = wheel_name
self.module_cache = {}
def find_spec(self, fullname, path=None, target=None):
module_path = fullname.replace('.', '/') + '.py'
if module_path in self.zip_file.namelist():
return importlib.util.spec_from_loader(fullname, self)
return None
def create_module(self, spec):
return None
def exec_module(self, module):
module_path = module.__spec__.name.replace('.', '/') + '.py'
source = self.zip_file.read(module_path)
code = compile(source, module_path, 'exec')
exec(code, module.__dict__)
def load_wheel(wheel_path):
with open(wheel_path, 'rb') as f:
wheel_data = f.read()
wheel_name = wheel_path.split('/')[-1]
importer = InMemoryZipImporter(wheel_data, wheel_name)
sys.meta_path.insert(0, importer)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Dynamically load a Python wheel file.")
parser.add_argument('wheel_path', help="Path to the .whl file")
args = parser.parse_args()
load_wheel(args.wheel_path)
</code>
import sys
import zipfile
import argparse
from io import BytesIO
import importlib.util
import importlib.abc
import importlib.machinery
class InMemoryZipImporter(importlib.abc.MetaPathFinder, importlib.abc.Loader):
def __init__(self, wheel_data, wheel_name):
self.zip_file = zipfile.ZipFile(BytesIO(wheel_data))
self.wheel_name = wheel_name
self.module_cache = {}
def find_spec(self, fullname, path=None, target=None):
module_path = fullname.replace('.', '/') + '.py'
if module_path in self.zip_file.namelist():
return importlib.util.spec_from_loader(fullname, self)
return None
def create_module(self, spec):
return None
def exec_module(self, module):
module_path = module.__spec__.name.replace('.', '/') + '.py'
source = self.zip_file.read(module_path)
code = compile(source, module_path, 'exec')
exec(code, module.__dict__)
def load_wheel(wheel_path):
with open(wheel_path, 'rb') as f:
wheel_data = f.read()
wheel_name = wheel_path.split('/')[-1]
importer = InMemoryZipImporter(wheel_data, wheel_name)
sys.meta_path.insert(0, importer)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Dynamically load a Python wheel file.")
parser.add_argument('wheel_path', help="Path to the .whl file")
args = parser.parse_args()
load_wheel(args.wheel_path)
New contributor
Ryan Hope is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.