first breadth python hotswap

breadth - Hot-swapping de Python ejecutando programa



8 puzzle breadth first search python (3)

Podría sondear el archivo runtime.py, esperando que cambie. Una vez que cambie, solo llame

reload(runtime)

Cada vez que estoy depurando un módulo de Python, uso este enfoque en el símbolo del sistema interactivo de python (excepto que llamo a reload () manualmente, no sondeo nada).

EDITAR: Para detectar cambios en un archivo, consulte esta pregunta SO . El sondeo puede ser la opción más confiable, pero solo volvería a cargar el archivo si se actualiza la hora modificada, en lugar de volver a cargarlo en cada sondeo. También debe considerar la captura de excepciones al recargar, especialmente los errores de sintaxis. Y puede o no tener problemas con la seguridad de los hilos.

El siguiente código le permite modificar el contenido de runtime.py en tiempo de ejecución. En otras palabras, no tienes que interrumpir 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()

El módulo importado en tiempo de ejecución es:

# runtime.py def function(): print("I am version one of runtime.py")

Este mecanismo primitivo te permite "intercambiar" código de Python (a la Erlang). ¿Hay una mejor alternativa?

Tenga en cuenta que esta es una pregunta meramente académica, ya que no tengo la necesidad de hacer algo como esto. Sin embargo, estoy interesado en aprender más sobre el tiempo de ejecución de Python.

Editar :

Creé la siguiente solución: un objeto Engine proporciona una interfaz para las funciones contenidas en un módulo (en este caso, el módulo se llama engine.py ). El objeto Engine también genera un subproceso que supervisa los cambios en el archivo de origen y, si se detectan cambios, llama al método de notify() en el motor, que recarga el archivo de origen.

En mi implementación, la detección de cambios se basa en el sondeo cada segundo de frequency verifica la suma de comprobación SHA1 del archivo, pero son posibles otras implementaciones.

En este ejemplo, todos los cambios detectados se registran en un archivo llamado hotswap.log , donde se registra la suma de comprobación.

Otros mecanismos para detectar cambios podrían ser un servidor o el uso de inotify en el hilo del 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()

El archivo engine.py :

# this is "engine.py" def f1(): print("call to f1") def f2(): print("call to f2")

Muestra de registro:

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)

De nuevo, esta es una discusión académica porque no tengo necesidad en este momento de intercambiar en caliente el código Python. Sin embargo, me gusta poder entender un poco el tiempo de ejecución y darme cuenta de lo que es posible y de lo que no. Observe que el mecanismo de carga podría agregar un bloqueo, en caso de que esté utilizando recursos, y el manejo de excepciones, en caso de que el módulo no se cargue correctamente.

¿Comentarios?


Si desea un código de intercambio en caliente que se encuentra cuando utiliza la importación de funciones, etc., necesita sobrescribir una var. Global de módulo, si por ejemplo usa:

import mylib

Necesitas cuando cargues el módulo en el código asigna a mylib el nuevo módulo. Otra cuestión es probar esto en un programa que use hilos para saber si está seguro con hilos y, cuando se use el multiprocesamiento, esto solo se encuentra en un proceso, para cambiar el código en todo el proceso, todos necesitan cargar código nuevo, es necesario intentarlo si es seguro en multiproces.

Y, es interesante comprobar primero si tiene un código nuevo o no para no cargar el mismo código. Y piense que solo en Python puede cargar un nuevo módulo y reemplazar el nombre de variable del módulo, pero si realmente necesita un buen código de cambio, consulte el lenguaje Erlang y OTP, es muy bueno.


globe = __import__(''copy'').copy(globals()) while True: with open(''runtime.py'', ''r'') as mod: exec mod in globe __import__(''time'').sleep(1)

Will repetidamente leerá y ejecutará runtime.py con un runtime.py casi no contaminado globals() y sin locals() , y no contaminará el alcance global, pero todo el espacio de nombres del tiempo de ejecución estará disponible en el globe