【问题标题】:Restarting a Python Interpreter Quietly安静地重启 Python 解释器
【发布时间】:2009-01-10 17:56:27
【问题描述】:

我在应用程序中嵌入了一个 python 解释器。该应用程序需要很长时间才能启动,并且我无法在不重新启动整个应用程序的情况下重新启动解释器。我想要做的是本质上保存解释器的状态并轻松返回该状态。

我首先将所有模块的名称存储在 python 解释器开始使用的 sys.modules 中,然后在请求时从 sys.modules 中删除所有新模块。这似乎使解释器准备重新导入相同的模块,即使它之前已经导入了它们。但是,这似乎并不适用于所有情况,例如使用单例类和静态方法等。

如果可以避免的话,我宁愿不要在这个解释器中嵌入另一个解释器,因为会失去使用应用程序 API 的便利性(以及包括我想象的轻微的速度影响)。

那么,有谁知道我可以存储解释器的状态然后返回到这个状态以便它可以应对所有情况的方法?

谢谢,

【问题讨论】:

    标签: python interpreter


    【解决方案1】:

    试试这个来自 ActiveState 配方的代码:http://code.activestate.com/recipes/572213/

    它扩展了 pickle,因此它支持对 shell 控制台中定义的任何内容进行酸洗。根据他们的文档,理论上你应该能够腌制 ma​​in 模块:

    import savestate, pickle, __main__
    pickle.dump(__main__, open('savestate.pickle', 'wb'), 2)
    

    【讨论】:

    • 这看起来很有希望,谢谢,我会更深入地研究一下。
    • 不适用于 Python 3,主要是因为 pickle 模块更改,例如pickle.Pickler.dispatch 是不同的东西,pickle.Pickler.dispatch_table[...] 会抛出一个关于分配给内置函数和东西的错误。可悲的是,还没有找到 Py3 的解决方案。 :(
    【解决方案2】:

    我建议解决根本原因问题。

    "应用程序需要很长时间才能 启动,我没有能力 重新启动解释器 重新启动整个应用程序”

    我怀疑这实际上是 100% 正确的。如果整个申请是国会法案的结果,好吧,它不能改变。但如果整个应用程序是由真人编写的,那么查找并移动代码以重新启动 Python 解释器应该是可能的。它比任何你可以解决问题的方法更便宜、更简单、更可靠。

    【讨论】:

    • 在这种情况下不是 - 该应用程序是一个主要软件,一年只发布一次。在引入此功能之前,hack 是唯一可用于加速开发的解决方案。
    • 我希望我的时间值那么多!这是我将不得不在一两个小时内实施的事情,以尝试让我的生活更轻松一些。如果效果不好,我们会继续使用现有的系统...
    【解决方案3】:

    将所有模块的名称存储在 python 解释器开始使用的 sys.modules 中,然后在请求时从 sys.modules 中删除所有新模块。这似乎使解释器准备重新导入相同的模块,即使它之前已经导入了它们。

    在某些情况下可以使模块重新加载强制方法起作用,但它有点麻烦。总结:

    • 您需要确保所有相互依赖的模块都立即重新加载。因此,任何执行 'import y' 或 'from y import ...' 的模块 'x' 必须与模块 'y' 同时从 sys.modules 中删除。

    • 如果您的应用或任何其他活动模块正在使用线程,则此进程需要使用锁进行保护。

    • 任何在其他模块中留下指向自身的钩子的模块都无法有效地重新加载,因为对旧模块的引用将保留在未重新加载/不可重新加载的代码中。这包括异常钩子、信号、警告过滤器、编码、猴子补丁等。如果您开始愉快地重新加载包含其他人代码的模块,您可能会惊讶于他们这样做的频率,这可能会导致微妙和奇怪的错误。

    所以要让它工作,你需要在相互依赖的模块之间有明确定义的边界——“它是在初始启动时导入的”可能还不够好——并确保它们被很好地封装而没有意外的依赖项,例如猴子补丁。

    这可以基于文件夹,因此例如 /home/me/myapp/lib 中的任何内容都可以作为一个单元重新加载,同时不理会其他模块 - 尤其是 stdlib 中的内容,例如。 /usr/lib/python2.x/ 通常不可靠地重新加载。如果您需要,我在尚未发布的 webapp 重新加载包装器中提供了此代码。

    最后:

    • 您需要了解 sys.modules 的内部结构,特别是它会留下一堆“无”值来表示相对导入失败。如果您没有在删除其他模块值的同时删除它们,则后续导入模块的尝试可能(有时)最终导入“无”,从而导致令人困惑的错误。

    这是一个令人讨厌的实现细节,可能会在未来的某个 Python 版本中改变和破坏您的应用,但这是以不受支持的方式使用 sys.modules 的代价。

    【讨论】:

    • 我认为这种方法的问题在于保持状态。一个模块有它自己的命名空间(就像它里面的函数和类一样),所以任何函数调用都可以改变那个状态。如果模块在其全局命名空间中运行代码(在导入时),那么即使导入的顺序也可能很重要。
    【解决方案4】:

    一个非常 hacky 和容易出错的方法可能是一个 c 模块,它只是将内存复制到一个文件中,以便下次可以重新加载。但既然我无法想象这总是能正常工作,那么酸洗会是另一种选择吗?

    如果您能够使所有模块都可腌制,那么您应该能够腌制 globals() 中的所有内容,以便可以再次重新加载。

    【讨论】:

      【解决方案5】:

      如果您事先知道正在使用的模块、类、函数、变量等...,您可以将它们腌制到磁盘并重新加载。如果您的环境包含许多未知数,我不确定解决该问题的最佳方法。不过,腌制全局变量和本地变量可能就足够了。

      【讨论】:

        猜你喜欢
        • 2010-11-12
        • 2021-02-16
        • 1970-01-01
        • 1970-01-01
        • 2020-11-23
        • 2018-12-28
        • 2017-03-16
        • 2022-01-24
        • 1970-01-01
        相关资源
        最近更新 更多