Python twisted web服务器缓存和执行过时代码

Python twisted web服务器缓存和执行过时代码,python,caching,module,twisted,reload,Python,Caching,Module,Twisted,Reload,背景:处理允许用户将python脚本上载到服务器(Twisted web服务器)的web应用程序。UI在这些python脚本上提供了完整的CRUD功能。上传脚本后,用户可以选择脚本并在服务器上运行,然后在UI上返回结果。一切正常 问题:…除非用户内联编辑python代码(通过UI)或通过上载新脚本覆盖已存在的脚本来更新脚本。twisted似乎缓存代码(新旧代码),有时运行新代码,有时运行旧代码 示例:我在服务器上上传了一个脚本hello.py,该服务器有一个名为run()的函数,该函数的作用是:

背景:处理允许用户将python脚本上载到服务器(Twisted web服务器)的web应用程序。UI在这些python脚本上提供了完整的CRUD功能。上传脚本后,用户可以选择脚本并在服务器上运行,然后在UI上返回结果。一切正常

问题:…除非用户内联编辑python代码(通过UI)或通过上载新脚本覆盖已存在的脚本来更新脚本。twisted似乎缓存代码(新旧代码),有时运行新代码,有时运行旧代码

示例:我在服务器上上传了一个脚本
hello.py
,该服务器有一个名为
run()
的函数,该函数的作用是:
打印“hello world”
。其他人过来上传另一个名为
hello.py
的脚本,该脚本的作用是:
打印“再见世界”
。然后,我返回并对脚本执行
run()
函数10次。一半的时候它会说“你好,世界”,一半的时候它会说“再见,世界”

目前已尝试:在执行脚本之前,有几种不同的方法可以将脚本重新加载到内存中,包括:

  • python的内置重载():

  • imp模块重新加载():

  • twisted.python.rebuild()

  • 我想,如果我们强制python不写字节码,也许可以解决这个问题:
    sys.dont\u write\u bytecode=True

  • 重新启动twisted服务器

  • 还有一些我不记得的事情

确保执行最新python代码的唯一方法是手动重新启动twisted服务器。我已经研究了很长一段时间,还没有找到任何更好的方法来做这件事,它的工作100%的时间。这让我相信弹跳扭曲是唯一的方法

问题:是否有更好的方法来实现这一点(即始终执行最新的代码),而不必反弹?可能是通过防止twisted将脚本缓存到内存中,或者在导入/重新加载模块之前清除twisted缓存

我对TwistedWeb服务器还相当陌生,所以我可能忽略了解决此问题的明显方法,或者可能有完全错误的方法。对解决这个问题有一些见解将不胜感激

谢谢


Twisted不会在内存中缓存Python代码。Python的模块系统的工作原理是对源文件进行一次评估,然后将模块对象放入
sys.modules
。模块的未来导入不会重新评估源文件-它们只是从
sys.modules
中提取模块对象

Twisted的一部分将做的是保留对它正在使用的对象的引用。这就是编写Python程序的方式。如果没有对对象的引用,则无法使用它们。Twisted Web服务器无法调用
run
函数,除非它引用了定义该函数的模块

reload
的问题在于,它重新计算定义模块的源文件,但无法跟踪并替换对模块定义的对象的旧版本的所有引用,例如,
run
函数。
imp.reload
功能基本相同

twisted.python.rebuild
尝试解决此问题,但正确使用它需要一些注意(而且很可能存在边缘情况,它仍然无法正确处理)

这些代码重新加载工具中的任何一个是否能在您的应用程序中工作,对于您的应用程序是如何编写的这一微小的、看似无关的细节都非常敏感

比如说,

import somemodule
reload(somemodule)
somemodule.foo()
可以运行最新版本的
somemodule.foo
。但是

from somemodule import foo
import somemodule
reload(somemodule)
foo()
预计不会运行最新版本的
somemodule.foo
。成功使用
twisted.python.rebuild
还有更微妙的规则

由于您的问题不包括应用程序中的任何实际代码,因此无法知道您遇到了哪些情况(导致无法可靠地更新对象以反映其源代码的最新版本)

这里没有任何好的解决方案。最可靠的解决方案是重新启动流程。这当然会清除所有旧的代码/对象,并允许使用最新版本运行(虽然不是100%的时间-例如,
.py
.pyc
文件上的时间戳问题可能导致使用较旧的
.pyc
文件,而不是新的
.py
文件-但这种情况非常罕见)

另一种方法是使用
execfile
(或
exec
)而不是
import
。这绕过了整个模块系统(因此它的“缓存”层)。它将管理由正在加载的源定义的对象的生命周期的全部负担都放在了您的身上。这需要更多的工作,但也意味着运行时的其他级别几乎并没有什么惊喜


当然,如果您愿意检查与用户模块交互的所有代码,并仔细检查对旧对象的剩余引用,则可以使用
reload
twisted.python.rebuild
。哦,您正在使用的任何库代码可能也能够获得这些对象的引用。

感谢Jean Paul及时、详细的回复。在阅读您的响应之后,我尝试使用execfile以及在子流程调用中执行脚本。然而,这对脚本的执行速度有很大的影响。曾经花了几分钟的剧本
from twisted.python.rebuild import rebuild
module = __import__('hello')
rebuild(module)
module.run()
import somemodule
reload(somemodule)
somemodule.foo()
from somemodule import foo
import somemodule
reload(somemodule)
foo()