Следующий код позволяет вам изменять содержимое 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 (a la Erlang). Есть ли лучшая альтернатива?
Обратите внимание, что это просто академический вопрос, так как у меня нет необходимости делать что-либо подобное. Тем не менее, мне интересно узнать больше о времени выполнения Python.
Edit
Я создал следующее решение: объект 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 с горячей заменой. Однако мне нравится понимать немного времени исполнения и понимать, что возможно, а что нет. Обратите внимание, что механизм загрузки может добавить блокировку, если он использует ресурсы и обработку исключений, если модуль не загружен успешно.
Комментарии?