背景:开发一个 Web 应用程序,允许用户将 python 脚本上传到服务器(Twisted Web 服务器)。 UI 在这些 python 脚本上提供完整的 CRUD 功能。上传脚本后,用户可以选择该脚本并在服务器上运行它,并在 UI 上返回结果。一切正常...
问题: ...除非用户内联编辑 python 代码(通过 UI)或通过上传新脚本覆盖已存在的脚本来更新脚本。看起来,twisted 缓存了代码(旧的和新的),并且有时运行新代码,有时运行旧代码。
示例: 我在服务器上上传了一个脚本 hello.py
,该脚本有一个名为 run()
的函数,该函数执行以下操作: 打印“ Hello World ”
。其他人上传了另一个名为 hello.py
的脚本,该脚本执行以下操作:打印“再见世界”
。然后,我返回并在脚本上执行 run()
函数 10 次。一半的时间它会说“ Hello World ”,一半的时间它会说“再见世界”。
到目前为止尝试过:在执行脚本之前将脚本重新加载到内存中的几种不同方法,包括:
python 内置的 reload():
module = __import__('hello') reload(module) module.run()
imp 模块重新加载():
import imp module = __import__('hello') imp.reload(module) module.run()
twisted.python.rebuild()
from twisted.python.rebuild import rebuild module = __import__('hello') rebuild(module) module.run()
认为如果我们强制 python 不写入字节码,也许就能解决这个问题:
sys.dont_write_bytecode = True
重新启动扭曲服务器
一些我不记得的其他事情
确保执行最新的Python代码的唯一方法是手动重新启动Twisted服务器。我已经研究了相当长一段时间了,还没有找到更好的方法,而且 100% 有效。这让我相信扭曲弹跳是唯一的方法。
问题:是否有更好的方法来实现此目的(即始终执行最新的代码)而不必扭曲反弹?也许是通过防止twisted将脚本缓存到内存中,或者在导入/重新加载模块之前清除twisted缓存。
我对扭曲的网络服务器相当陌生,因此我可能忽略了解决此问题的明显方法,或者可能采用完全错误的方法来解决此问题。对于解决这个问题的一些见解将不胜感激。
谢谢
T
最佳答案
Twisted 不会在内存中缓存 Python 代码。 Python 的模块系统的工作原理是对源文件进行一次评估,然后将模块对象放入 sys.modules 中。模块的 future 导入不会重新评估源文件 - 它们只是从 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
还有更微妙的规则。
由于您的问题不包含应用程序中的任何实际代码,因此无法知道您遇到了哪些情况(导致无法可靠地更新您的对象以反射(reflect)其最新版本)源代码)。
这里没有任何伟大解决方案。最可靠的解决方案是重新启动该过程。这当然会清除所有旧的代码/对象,并让程序使用最新版本运行(尽管不是 100% 的时间 - 例如,.py
和 .pyc
上的时间戳问题> 文件可能会导致使用旧的 .pyc
文件而不是新的 .py
文件 - 但这是非常罕见的)。
另一种方法是使用execfile
(或exec
)而不是import
。这绕过了整个模块系统(因此绕过了它的“缓存”层)。它将管理由您加载的源定义的对象的生命周期的全部负担交给您。这是更多的工作,但这也意味着运行时的其他级别不会有什么意外。
当然,如果您愿意检查所有与用户交互的代码,可以使用 reload
或 twisted.python.rebuild
来完成此操作模块并仔细审核它是否有对旧对象的剩余引用。哦,您正在使用的任何库代码也可能能够获取对这些对象的引用。
关于Python 扭曲了 Web 服务器缓存并执行过时的代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26008037/