【问题标题】:IronPython - Is there a way to load a python script from the database or embedded from a string resource?IronPython - 有没有办法从数据库加载 python 脚本或从字符串资源嵌入?
【发布时间】:2016-05-02 12:36:05
【问题描述】:

当 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 的嵌入式资源中查找该模块,并将该模块的字符串发回。

我该怎么做?

【问题讨论】:

标签: python ironpython


【解决方案1】:

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

PEP 0302详细描述了机制。

本质上,您需要创建两个对象——一个 Finder 对象和一个 Loader 对象。

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

此方法将使用完全限定名称调用 模块。如果 finder 安装在 sys.meta_path 上,它将收到 第二个参数,对于顶级模块是 None ,或者 package.__path__ 用于子模块或子包 [5]。它应该返回 如果找到模块,则为加载器对象,否则为 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

【讨论】:

  • 谢谢!我刚刚对你的样本做了一些研究。我遇到了import imp(已被弃用)而支持importlib,这是一个仅限Python3的东西。有什么指导吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-05-14
  • 2018-11-25
  • 1970-01-01
  • 2012-08-07
  • 2021-10-04
  • 1970-01-01
  • 2019-03-26
相关资源
最近更新 更多