python - from - Versión recursiva de ''recargar''
python import module from path (6)
Cuando estoy desarrollando el código Python, normalmente lo pruebo de forma ad hoc en el intérprete. import some_module
, lo import some_module
, encontraré un error, lo arreglaré y lo guardaré, y luego reload(some_module)
función de reload
incorporada para reload(some_module)
y probar de nuevo.
Sin embargo, supongamos que en some_module
he import some_other_module
, y mientras some_module
, descubro un error en some_other_module
y lo soluciono. Ahora, llamar a reload(some_module)
no volverá a importar recursivamente some_other_module
. Tengo que reimportar manualmente la dependencia (haciendo algo como reload(some_module.some_other_module)
, o import some_other_module; reload(some_other_module)
, o, si he cambiado un montón de dependencias y he perdido la pista de lo que necesito volver a cargar , Necesito reiniciar todo el intérprete.
Lo que sería más conveniente es si existiera alguna función recursive_reload
, y podría hacer recursive_reload(some_module)
y hacer que Python no solo recargue some_module
, sino que también recursivamente recargue todos los módulos que some_module
importa (y cada módulo que importan) , y así sucesivamente) para poder estar seguro de que no estaba usando una versión antigua de ninguno de los otros módulos de los que depende some_module
.
No creo que haya algo incorporado en Python que se comporte como la función recursive_reload
que describo aquí, pero ¿hay una manera fácil de hackear algo así juntos?
¿No sería más sencillo escribir algunos casos de prueba y ejecutarlos cada vez que termine de modificar su módulo?
Lo que estás haciendo es genial (en esencia estás usando TDD (desarrollo guiado por pruebas) pero lo estás haciendo mal.
Tenga en cuenta que con las pruebas unitarias escritas (utilizando el módulo de prueba de unidad de Python predeterminado, o mejor aún, la nose ) puede tener pruebas que sean reutilizables , estables y que le ayuden a detectar las inconsitencias en su código mucho más rápido y mejor que con la prueba de su módulo en el interactivo. ambiente.
Es una cosa difícil de hacer: tengo un ejemplo práctico en esta respuesta: cómo encontrar una lista de módulos que dependen de un módulo específico en Python
He encontrado la respuesta de redsk muy útil. Propongo una versión simplificada (para el usuario, no como código) donde la ruta al módulo se recopila automáticamente y la recursión funciona para un número arbitrario de niveles. Todo es autocontenido en una sola función. Probado en Python 3.4. Supongo que para Python 3.3 uno debe import reload from imp
lugar de ... from importlib
. También verifica si el archivo __file__
está presente, lo que podría ser falso si el codificador se olvida de definir un archivo __init__.py
en un submódulo. En tal caso, se plantea una excepción.
def rreload(module):
"""
Recursive reload of the specified module and (recursively) the used ones.
Mandatory! Every submodule must have an __init__.py file
Usage:
import mymodule
rreload(mymodule)
:param module: the module to load (the module itself, not a string)
:return: nothing
"""
import os.path
import sys
def rreload_deep_scan(module, rootpath, mdict=None):
from types import ModuleType
from importlib import reload
if mdict is None:
mdict = {}
if module not in mdict:
# modules reloaded from this module
mdict[module] = []
# print("RReloading " + str(module))
reload(module)
for attribute_name in dir(module):
attribute = getattr(module, attribute_name)
# print ("for attr "+attribute_name)
if type(attribute) is ModuleType:
# print ("typeok")
if attribute not in mdict[module]:
# print ("not int mdict")
if attribute.__name__ not in sys.builtin_module_names:
# print ("not a builtin")
# If the submodule is a python file, it will have a __file__ attribute
if not hasattr(attribute, ''__file__''):
raise BaseException("Could not find attribute __file__ for module ''"+str(attribute)+"''. Maybe a missing __init__.py file?")
attribute_path = os.path.dirname(attribute.__file__)
if attribute_path.startswith(rootpath):
# print ("in path")
mdict[module].append(attribute)
rreload_deep_scan(attribute, rootpath, mdict)
rreload_deep_scan(module, rootpath=os.path.dirname(module.__file__))
Me he topado con el mismo problema y he creado las respuestas de @Mattew y @osa.
from types import ModuleType
import os, sys
def rreload(module, paths=None, mdict=None):
"""Recursively reload modules."""
if paths is None:
paths = ['''']
if mdict is None:
mdict = {}
if module not in mdict:
# modules reloaded from this module
mdict[module] = []
reload(module)
for attribute_name in dir(module):
attribute = getattr(module, attribute_name)
if type(attribute) is ModuleType:
if attribute not in mdict[module]:
if attribute.__name__ not in sys.builtin_module_names:
if os.path.dirname(attribute.__file__) in paths:
mdict[module].append(attribute)
rreload(attribute, paths, mdict)
reload(module)
#return mdict
Hay tres diferencias:
- En el caso general, se debe llamar a recargar (módulo) al final de la función también, como se señaló @osa.
- Con las dependencias de importación circular, el código publicado anteriormente se repetirá para siempre, así que he agregado un diccionario de listas para realizar un seguimiento del conjunto de módulos cargados por otros módulos. Si bien las dependencias circulares no son geniales, Python las permite, por lo que esta función de recarga también las trata.
- He agregado una lista de rutas (la predeterminada es ['''']) desde la cual se permite la recarga. A algunos módulos no les gusta haber sido recargados de la forma normal, (como se muestra here ).
Me he topado con el mismo problema y me inspiraste para resolver el problema.
from types import ModuleType
def rreload(module):
"""Recursively reload modules."""
reload(module)
for attribute_name in dir(module):
attribute = getattr(module, attribute_name)
if type(attribute) is ModuleType:
rreload(attribute)
O, si está usando IPython, simplemente use dreload
o pass --deep-reload
en el inicio.
Técnicamente, en cada archivo puede poner un comando de recarga, para asegurarse de que se recarga cada vez que se importa.
a.py:
def testa():
print ''hi!''
b.py:
import a
reload(a)
def testb():
a.testa()
Ahora, interactivamente:
import b
b.testb()
#hi!
#<modify a.py>
reload(b)
b.testb()
#hello again!