¿Cómo se descarga (recarga) un módulo?

Tengo un servidor Python que funciona desde hace tiempo y me gustaría poder actualizar un servicio sin reiniciar el servidor. ¿Cuál es la mejor manera de hacerlo?

if foo.py has changed:
    unimport foo  <-- How do I do this?
    import foo
    myfoo = foo.Foo()
Solución

Puedes recargar un módulo cuando ya ha sido importado utilizando la función incorporada reload:

from importlib import reload  # Python 3.4+ only.
import foo

while True:
    # Do some things.
    if is_changed(foo):
        foo = reload(foo)

En Python 3, reload se trasladó al módulo imp. En 3.4, imp quedó obsoleto en favor de importlib, y reload se añadió a este último. Al apuntar a la versión 3 o posterior, se debe hacer referencia al módulo apropiado al llamar a reload o importarlo.

Creo que esto es lo que quieres. Los servidores web como el servidor de desarrollo de Django utilizan esto para que puedas ver los efectos de tus cambios de código sin reiniciar el proceso del servidor mismo.

Citando los documentos:

El código de los módulos de Python se recompila y el código a nivel de módulo se reejecuta, definiendo un nuevo conjunto de objetos que están ligados a nombres en el diccionario del módulo. diccionario del módulo. La función init de los módulos de extensión no se llama una segunda vez. Como con todos los demás objetos en Python los objetos antiguos sólo son se recuperan después de que su número de referencias caen a cero. Los nombres en el espacio de nombres del módulo espacio de nombres del módulo se actualizan para apuntar a cualquier objetos nuevos o modificados. Otros referencias a los objetos antiguos (como nombres externos al módulo) no se rebote para referirse a los nuevos objetos y deben ser actualizadas en cada espacio de nombres en el que aparezcan si así se desea.

Como has señalado en tu pregunta, tendrás que reconstruir los objetos Foo si la clase Foo reside en el módulo foo.

Comentarios (17)

Puede ser especialmente difícil eliminar un módulo si no es Python puro.

Aquí hay algo de información de: ¿Cómo borro realmente un módulo importado?

Puedes usar sys.getrefcount() para averiguar el número real de referencias.

>>> import sys, empty, os
>>> sys.getrefcount(sys)
9
>>> sys.getrefcount(os)
6
>>> sys.getrefcount(empty)
3

Los números superiores a 3 indican que será difícil deshacerse del módulo. El módulo casero "vacío" (que no contiene nada) debe ser recogido de la basura después de

>>> del sys.modules["empty"]
>>> del empty

ya que la tercera referencia es un artefacto de la función getrefcount().

Comentarios (2)

Reload(module), pero sólo si es completamente independiente. Si cualquier otra cosa tiene una referencia al módulo (o a cualquier objeto que pertenezca al módulo), entonces obtendrás sutiles y curiosos errores causados por el código antiguo que permanece más tiempo del que esperabas, y cosas comoisinstance` que no funcionan en diferentes versiones del mismo código.

Si tiene dependencias unidireccionales, también debe recargar todos los módulos que dependen del módulo recargado para deshacerse de todas las referencias al código antiguo. Y luego recargar los módulos que dependen de los módulos recargados, recursivamente.

Si tienes dependencias circulares, lo que es muy común por ejemplo cuando se trata de recargar un paquete, debes descargar todos los módulos del grupo de una sola vez. No puede hacer esto con reload() porque reimportará cada módulo antes de que sus dependencias hayan sido refrescadas, permitiendo que las viejas referencias se cuelen en los nuevos módulos.

La única manera de hacerlo en este caso es hackear sys.modules, lo cual no está soportado. Tendrías que ir y borrar cada entrada de sys.modules que quisieras que se recargara en la siguiente importación, y también borrar las entradas cuyos valores son None para lidiar con un problema de implementación que tiene que ver con el almacenamiento en caché de las importaciones relativas fallidas. No es muy agradable, pero mientras tengas un conjunto de dependencias totalmente autocontenido que no deje referencias fuera de su base de código, es factible.

Probablemente sea mejor reiniciar el servidor. :-)

Comentarios (7)