Ability to include other files from adapter.py
This enhancement may be blocked by #7.
Allow the storage of multiple .py files that could then be included by adapter.py so that all the code is not in one big file.
This won't impact performance as once a module is loaded to memory, import something has no effect so it will be loaded only once.
Import hooks should be able to solve this issue and also enable finer control on what is imported (and blocked) by the non-pythia scripts. They could even allow storing and loading python modules directly from pbo files!
This is definitely worth investigating.
https://www.python.org/dev/peps/pep-0302/
import sys
import importlib.abc
import importlib.machinery
import textwrap
class InterceptDummyWrapper(object):
data = {
'intercept_modules/__init__.py': textwrap.dedent('''\
# This should be __init__py
print('Executing intercept_modules.__init__.py')
Foo = 43
''').encode('latin-1'),
'intercept_modules/submodule.py': textwrap.dedent('''\
print('Executing intercept_modules.submodule.py')
print('Trying to import another module from within a submodule')
import other_modules
''').encode('latin-1'),
'other_modules/__init__.py': textwrap.dedent('''\
print('Executing other_modules/__init__.py')
''').encode('latin-1'),
'other_modules/raiser.py': textwrap.dedent('''\
print('Executing other_modules/raiser.py')
print('This will raise an exception. Notice the right file name in the calltrace!')
1/0
''').encode('latin-1')
}
@staticmethod
def get_filename(fullname):
pathname = fullname.replace('.', '/')
for suffix in ('/__init__.py', '.py'):
if pathname + suffix in InterceptDummyWrapper.data:
return pathname + suffix
raise KeyError('{} not found'.format(fullname))
@staticmethod
def is_package(fullname):
return InterceptDummyWrapper.get_filename(fullname).endswith('/__init__.py')
class PBOModuleFinder(importlib.abc.MetaPathFinder):
def install(self):
if self not in sys.meta_path:
sys.meta_path.insert(0, self)
def find_spec(self, name, path, target = None):
print('Test_Finder: Trying to load: {}'.format(name))
try:
is_package = InterceptDummyWrapper.is_package(name)
return importlib.machinery.ModuleSpec(name, PBOLoader(), is_package=is_package)
except KeyError:
return None
class PBOLoader(importlib.abc.SourceLoader):
def get_filename(self, fullname):
print('PBOLoader: Requesting filename for {}'.format(fullname))
return InterceptDummyWrapper.get_filename(fullname)
def get_data(self, filename):
print('PBOLoader: Fetching {} from pbo file'.format(filename))
return InterceptDummyWrapper.data[filename]
def test():
# Install the finder
finder = PBOModuleFinder()
finder.install()
# Check if the finder is installed (should be the first there)
print('meta_path:', sys.meta_path)
# Import the package
import intercept_modules
print('intercept_modules.Foo:', intercept_modules.Foo)
# Import a submodule (which imports another provided package)
import intercept_modules.submodule
from intercept_modules import submodule
# Try raising an exception when inside a virtual file
try:
import other_modules.raiser
except:
import traceback
# traceback.print_exc()
print(traceback.format_exc())
print('Exception printed. All is good :)')
if __name__ == '__main__':
test()
I think we may consider just using zipimport for storing those files inside the dll. The easiest (and cross-platform) way would be to just append the zip at the end of the file and read the dll as a zip (making it a dll-zip polyglot) but this may stop working as soon as the file is digitally signed.
So it may be possible to just append the zip in linux (if we ever get a linux version) and store the zip as a dll ressource on Windows. Now the question is whether it will be possible to automatically pack the files with Visual Studio, prior to building and then dynamically include it in the resources as part of the building process.