Carga dinámica de código fuente Python
Pregunta
Actualmente estoy jugando con el frasco y no puedo averiguar cómo funciona el mecanismo de depuración está trabajando. Para ser más precisos al guardar el archivo de pitón con mi solicitud no tenga que reiniciar el servidor, se carga automáticamente cuando hago una petición. Así que mi pregunta es ¿cómo sabe el programa que se ejecuta fue cambiado y responder a que los cambios?
Solución
Frasco está utilizando la función de Werkzug subyacente run_with_reloader
(que se encuentra en serving.py
) ... lo cual es en sí mismo utilizando la función restart_with_reloader
y reloader_loop
creado anteriormente en el mismo archivo.
desova run_with_reloader
otro proceso pitón (correr Werkzug de nuevo con todos los mismos argumentos que se pasan a la primera de ellas) y este nuevo proceso utiliza el módulo thread
a generar un nuevo subproceso o subproceso que ejecuta la función de servidor. A continuación, ejecuta el reloader_loop
y espera.
reloader_loop
simplemente recorre todos los módulos que se han importado y obtiene sus fechas de última modificación. Luego, a intervalos especificados (que por defecto es 1 s) que comprueba todos los archivos de nuevo para ver si han sido modificados. Si es así, el actualmente en ejecución ( esclavo ) termina el proceso Werkzug (termina) con el código de salida 3. Una vez que sale, el hilo o subproceso que comenzó (que en realidad está haciendo el trabajo) se termina también. Los principal cheques proceso para ver si el código de salida fue de 3. Si lo fuera, se genera un nuevo esclavo subproceso, tal como lo hizo antes. De lo contrario, se sale con el mismo código de salida.
Este es el código de referencia:
def reloader_loop(extra_files=None, interval=1):
"""When this function is run from the main thread, it will force other
threads to exit when any modules currently loaded change.
Copyright notice. This function is based on the autoreload.py from
the CherryPy trac which originated from WSGIKit which is now dead.
:param extra_files: a list of additional files it should watch.
"""
def iter_module_files():
for module in sys.modules.values():
filename = getattr(module, '__file__', None)
if filename:
old = None
while not os.path.isfile(filename):
old = filename
filename = os.path.dirname(filename)
if filename == old:
break
else:
if filename[-4:] in ('.pyc', '.pyo'):
filename = filename[:-1]
yield filename
mtimes = {}
while 1:
for filename in chain(iter_module_files(), extra_files or ()):
try:
mtime = os.stat(filename).st_mtime
except OSError:
continue
old_time = mtimes.get(filename)
if old_time is None:
mtimes[filename] = mtime
continue
elif mtime > old_time:
_log('info', ' * Detected change in %r, reloading' % filename)
sys.exit(3)
time.sleep(interval)
def restart_with_reloader():
"""Spawn a new Python interpreter with the same arguments as this one,
but running the reloader thread.
"""
while 1:
_log('info', ' * Restarting with reloader...')
args = [sys.executable] + sys.argv
new_environ = os.environ.copy()
new_environ['WERKZEUG_RUN_MAIN'] = 'true'
# a weird bug on windows. sometimes unicode strings end up in the
# environment and subprocess.call does not like this, encode them
# to latin1 and continue.
if os.name == 'nt':
for key, value in new_environ.iteritems():
if isinstance(value, unicode):
new_environ[key] = value.encode('iso-8859-1')
exit_code = subprocess.call(args, env=new_environ)
if exit_code != 3:
return exit_code
def run_with_reloader(main_func, extra_files=None, interval=1):
"""Run the given function in an independent python interpreter."""
if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
thread.start_new_thread(main_func, ())
try:
reloader_loop(extra_files, interval)
except KeyboardInterrupt:
return
try:
sys.exit(restart_with_reloader())
except KeyboardInterrupt:
pass
Otros consejos
El built-in reload()
función puede hacer esto para usted. Es probable que esta función está detrás de lo que hace a Frasco código de recarga (después de darse cuenta de que ha cambiado en el disco de alguna manera).
La pregunta ¿Cómo se descarga (recarga) un módulo de Python? tiene más información al respecto.