IronPython - 有没有办法从数据库加载python脚本或从字符串资源嵌入?

时间:2016-01-26 01:19:20

标签: python ironpython

当它试图解析/加载无法找到的模块时,有没有办法从IronPython中捕获某种东西?

我们希望将模块存储在数据库中或将它们嵌入到DLL中,然后通过调用import mymodule来导入它们。我们根本不想涉及文件系统。当我们说import something时,我们不希望它在\ Lib或任何文件系统中查找。

以下代码适用于加载嵌入DLL中的一大块Python。它仅在没有导入时才有效。

    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);

和python代码:

print('hello world')

如果有导入,则事情不起作用。它不知道如何解决模块的位置。

我能使它工作的唯一方法是确保我们需要的任何模块都可以从文件系统中看到引擎。这涉及使用engine.GetSearchPaths()(查看它正在查找的路径)并使用engine.SetSearchPaths()附加任何其他搜索路径。但这些来自文件系统,这不是我想要的。

我想一个好方法可能是从引擎接收某种类型的事件,例如" OnLookingForModuleNamedXYZ(moduleName)"。然后我可以在数据库或DLL的嵌入式资源中查找模块,并发回模块的字符串。

我该怎么做?

1 个答案:

答案 0 :(得分:1)

您需要添加自定义导入挂钩。 Python添加了一种注册自定义导入钩子的方法。在此机制之前,您必须覆盖内置的__import__函数。但它特别容易出错,特别是如果多个库都试图添加自定义导入行为。

PEP 0302详细描述了该机制。

基本上,您需要创建两个对象 - Finder对象和Loader对象。

Finder对象应该定义一个名为find_module

的函数
  

将使用完全限定名称调用此方法   模块。如果finder安装在sys.meta_path上,它将收到   第二个参数,对于顶级模块是None,或者   package.__path__用于子模块或子包[5]。它应该回来   如果找到模块,则为loader对象;如果不是,则为None。

如果find_module函数找到模块并返回Loader对象,则Loader对象应定义load_module函数

  

此方法返回已加载的模块或引发异常,   如果没有现有的异常,最好是ImportError   传播。如果要求load_module()加载它不能的模块,   将引发ImportError。

这是一个简短的例子,详细介绍了它的工作原理:

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('.')[0]
        exec(code, mod.__dict__)
        return mod

sys.meta_path.append(CustomImportFinder)

import blah
print(blah)
# <module 'blah' (built-in)>
print(blah.VAR)
# 1