IronPython - Is There A Way To Load A Python Script From The Database Or Embedded From A String Resource?
Is there a way to catch some kind of even from IronPython when it attempts to resolve/load a module that it cannot find?
We want to store modules in a database or embed them in a DLL and import them by calling
import mymodule. We don't want to involve the filesystem at all. When we say
import something, we do not want it looking in \Lib or any filesystem.
The following code works fine for loading a chunk of Python embedded in a DLL. It works only when there are no imports.
var myScript = new StreamReader(Assembly.GetAssembly(this.GetType()).GetManifestResourceStream("Resource.Name.To.My.Script")).ReadToEnd() var engine = IronPython.Hosting.Python.CreateEngine(); var scope = engine.CreateScope(); var result = engine.Execute(myScript, scope);
And the python code:
If there is an import, things do not work. It doesn't know how to resolve the location of the module.
The only way I can make it work is by making sure that any modules that we need are visible to the engine from the filesystem. This involves using engine.GetSearchPaths() (to see what paths it is looking for) and using engine.SetSearchPaths() to append any additional search paths. But these are from the file system and that is not what I want.
I imagine a nice way might be to receive some kind of an event from the engine like "OnLookingForModuleNamedXYZ(moduleName)". And then I can look up the module in the database or in the DLL's embedded resources and send back the string for the module.
How can I do this?
You'll need to add a custom import hook. Python added a way to register custom import hooks. Prior to this mechanism, you would have to override the built-in
__import__ function. But it was particularly error-prone, especially if multiple libraries were both trying to add custom import behavior.
PEP 0302 describes the mechanism in detail.
Essentially, you need to create two objects -- a Finder object and a Loader object.
The Finder object should define a function called
This method will be called with the fully qualified name of the module. If the finder is installed on sys.meta_path , it will receive a second argument, which is None for a top-level module, or
package.__path__for submodules or subpackages  . It should return a loader object if the module was found, or None if it wasn't.
find_module function finds the module and returns a Loader object, the Loader object should define a
This method returns the loaded module or raises an exception, preferably ImportError if an existing exception is not being propagated. If load_module() is asked to load a module that it cannot, ImportError is to be raised.
Here is a short example detailing how it would work:
import sys import imp class CustomImportFinder(object): @staticmethod def find_module(fullname, path=None): # Do your custom stuff here, look in database, etc. code_from_database = "VAR = 1" return CustomImportLoader(fullname, path, code_from_database) class CustomImportLoader(object): def __init__(self, fullname, path, code_from_database): super(CustomImportLoader, self).__init__() self.fullname = fullname self.path = path self.code_from_database = code_from_database def is_package(fullname): # is this a package? return False def load_module(self, fullname): code = self.code_from_database ispkg = self.is_package(fullname) mod = sys.modules.setdefault(fullname, imp.new_module(fullname)) mod.__file__ = "<%s>" % self.__class__.__name__ mod.__loader__ = self if ispkg: mod.__path__ =  mod.__package__ = fullname else: mod.__package__ = fullname.rpartition('.') exec(code, mod.__dict__) return mod sys.meta_path.append(CustomImportFinder) import blah print(blah) # <module 'blah' (built-in)> print(blah.VAR) # 1
- → What are the pluses/minuses of different ways to configure GPIOs on the Beaglebone Black?
- → Django, code inside <script> tag doesn't work in a template
- → React - Django webpack config with dynamic 'output'
- → GAE Python app - Does URL matter for SEO?
- → Put a Rendered Django Template in Json along with some other items
- → session disappears when request is sent from fetch
- → Python Shopify API output formatted datetime string in django template
- → Shopify app: adding a new shipping address via webhook
- → Shopify + Python library: how to create new shipping address
- → shopify python api: how do add new assets to published theme?
- → Access 'HTTP_X_SHOPIFY_SHOP_API_CALL_LIMIT' with Python Shopify Module