7

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

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

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

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

谢谢,

4

5 回答 5

4

试试 ActiveState 食谱中的这段代码:http: //code.activestate.com/recipes/572213/

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

import savestate, pickle, __main__
pickle.dump(__main__, open('savestate.pickle', 'wb'), 2)
于 2009-01-10T21:19:39.193 回答
1

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

“应用程序需要很长时间才能启动,我无法在不重新启动整个应用程序的情况下重新启动解释器”

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

于 2009-01-10T21:46:02.277 回答
1

将所有模块的名称存储在 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 的代价。

于 2009-01-11T00:24:43.283 回答
0

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

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

于 2009-01-10T18:11:07.250 回答
0

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

于 2009-01-10T20:28:26.440 回答