以下代码允许您在运行时修改runtime.py
的内容。换句话说,您不必中断runner.py
。
#runner.py
import time
import imp
def main():
while True:
mod = imp.load_source("runtime", "./runtime.py")
mod.function()
time.sleep(1)
if __name__ == "__main__":
main()
在运行时导入的模块是:
# runtime.py
def function():
print("I am version one of runtime.py")
这种原始机制允许你“如何交换”Python代码(la Erlang)。还有更好的选择吗?
请注意,这只是一个学术问题,因为我没有必要做这样的事情。但是,我有兴趣了解有关Python运行时的更多信息。
修改:
我创建了以下解决方案:Engine
对象提供模块中包含的函数的接口(在本例中,模块称为engine.py
)。 Engine
对象还生成一个线程,用于监视源文件中的更改,如果检测到更改,则会调用引擎上的notify()
方法,该方法会重新加载源文件。
在我的实现中,更改检测基于每隔frequency
秒轮询检查文件的SHA1校验和,但其他实现也是可能的。
在此示例中,检测到的每个更改都会记录到名为hotswap.log
的文件中,其中会记录校验和。
检测更改的其他机制可能是服务器或inotify
线程中Monitor
的使用。
import imp
import time
import hashlib
import threading
import logging
logger = logging.getLogger("")
class MonitorThread(threading.Thread):
def __init__(self, engine, frequency=1):
super(MonitorThread, self).__init__()
self.engine = engine
self.frequency = frequency
# daemonize the thread so that it ends with the master program
self.daemon = True
def run(self):
while True:
with open(self.engine.source, "rb") as fp:
fingerprint = hashlib.sha1(fp.read()).hexdigest()
if not fingerprint == self.engine.fingerprint:
self.engine.notify(fingerprint)
time.sleep(self.frequency)
class Engine(object):
def __init__(self, source):
# store the path to the engine source
self.source = source
# load the module for the first time and create a fingerprint
# for the file
self.mod = imp.load_source("source", self.source)
with open(self.source, "rb") as fp:
self.fingerprint = hashlib.sha1(fp.read()).hexdigest()
# turn on monitoring thread
monitor = MonitorThread(self)
monitor.start()
def notify(self, fingerprint):
logger.info("received notification of fingerprint change ({0})".\
format(fingerprint))
self.fingerprint = fingerprint
self.mod = imp.load_source("source", self.source)
def __getattr__(self, attr):
return getattr(self.mod, attr)
def main():
logging.basicConfig(level=logging.INFO,
filename="hotswap.log")
engine = Engine("engine.py")
# this silly loop is a sample of how the program can be running in
# one thread and the monitoring is performed in another.
while True:
engine.f1()
engine.f2()
time.sleep(1)
if __name__ == "__main__":
main()
engine.py
文件:
# this is "engine.py"
def f1():
print("call to f1")
def f2():
print("call to f2")
记录样本:
INFO:root:received notification of fingerprint change (be1c56097992e2a414e94c98cd6a88d162c96956)
INFO:root:received notification of fingerprint change (dcb434869aa94897529d365803bf2b48be665897)
INFO:root:received notification of fingerprint change (36a0a4b20ee9ca6901842a30aab5eb52796649bd)
INFO:root:received notification of fingerprint change (2e96b05bbb8dbe8716c4dd37b74e9f58c6a925f2)
INFO:root:received notification of fingerprint change (baac96c2d37f169536c8c20fe5935c197425ed40)
INFO:root:received notification of fingerprint change (be1c56097992e2a414e94c98cd6a88d162c96956)
INFO:root:received notification of fingerprint change (dcb434869aa94897529d365803bf2b48be665897)
再次 - 这是学术讨论,因为我现在不需要热插拔Python代码。但是,我喜欢能够理解一点运行时并意识到什么是可能的,什么不是。请注意,如果模块未成功加载,则加载机制可以在使用资源时添加锁,并进行异常处理。
评论
答案 0 :(得分:7)
您可以轮询runtime.py文件,等待它更改。一旦改变,只需致电
reload(runtime)
每当我调试python模块时,我都会在交互式python命令提示符中使用这种方法(除了我手动调用reload(),我不会轮询任何东西)。
编辑: 要检测文件中的更改,请查看this SO question。轮询可能是最可靠的选项,但我只会在更新修改时间时重新加载文件,而不是在每次轮询时重新加载。您还应该考虑在重新加载时捕获异常,尤其是语法错误。您可能会遇到也可能不会遇到线程安全问题。
答案 1 :(得分:1)
globe = __import__('copy').copy(globals())
while True:
with open('runtime.py', 'r') as mod:
exec mod in globe
__import__('time').sleep(1)
将使用几乎未受污染的runtime.py
和globals()
重复读取和运行locals()
,并且不会污染全局范围,但{{1}中的所有运行时名称空间都将可用1}}
答案 2 :(得分:0)
如果您需要在使用函数导入时找到的热插拔代码,则需要覆盖全局变量模块,例如,如果您使用:
import mylib
当代码中的加载模块与新模块的mylib对齐时,您需要。其他的猜测是在程序中尝试使用线程来知道线程是否安全,当使用多处理这只在一个进程中找到时,对于所有进程中的更改代码都需要加载新代码,如果在多进程中是安全的,则需要尝试。
并且,如果没有加载相同代码的新代码,则首先进行检查。并且在Python中只考虑你可以加载一个新模块并替换模块的变量名,但如果你真的需要一个好的热变更代码,请看 Erlang 语言和OTP,这是非常好的。