He escrito un bot IRC usando Twisted y ahora he llegado al punto en el que quiero poder recargar dinámicamente la funcionalidad.
En mi progtwig principal, from bots.google import GoogleBot
y miré cómo usar reload
para recargar módulos , pero todavía no puedo averiguar cómo hacer una reimportación dinámica de clases.
Entonces, dada una clase de Python, ¿cómo recargo dinámicamente la definición de clase?
La recarga no es confiable y tiene muchos casos de esquina donde puede fallar. Es adecuado para recargar scripts simples y autocontenidos. Si desea recargar dinámicamente su código sin reiniciar, considere usar forkloop en su lugar:
http://opensourcehacker.com/2011/11/08/sauna-reload-the-most-awesomely-named-python-package-ever/
Lo descubrí, aquí está el código que uso:
def reimport_class(self, cls): """ Reload and reimport class "cls". Return the new definition of the class. """ # Get the fully qualified name of the class. from twisted.python import reflect full_path = reflect.qual(cls) # Naively parse the module name and class name. # Can be done much better... match = re.match(r'(.*)\.([^\.]+)', full_path) module_name = match.group(1) class_name = match.group(2) # This is where the good stuff happens. mod = __import__(module_name, fromlist=[class_name]) reload(mod) # The (reloaded definition of the) class itself is returned. return getattr(mod, class_name)
Mejor aún, subprocese los complementos, luego hipervise el subproceso, cuando los archivos cambien, vuelva a cargar el proceso de los complementos.
Edición: limpiado.
No puede volver a cargar el módulo usando reload(module)
cuando usa el formulario from X import Y
Tendrías que hacer algo como reload(sys.modules['module'])
en ese caso.
Esta podría no ser necesariamente la mejor manera de hacer lo que quiere, ¡pero funciona!
import bots.google class BotClass(irc.IRCClient): def __init__(self): global plugins plugins = [bots.google.GoogleBot()] def privmsg(self, user, channel, msg): global plugins parts = msg.split(' ') trigger = parts[0] if trigger == '!reload': reload(bots.google) plugins = [bots.google.GoogleBot()] print "Successfully reloaded plugins"
Puede usar los sys.modules
para recargar dinámicamente los módulos según la entrada del usuario.
Digamos que tienes una carpeta con múltiples complementos como:
module/ cmdtest.py urltitle.py ...
Puede usar sys.modules
de esta forma para cargar / recargar módulos en función de la entrada de usuario:
import sys if sys.modules['module.' + userinput]: reload(sys.modules['module.' + userinput]) else: ' Module not loaded. Cannot reload ' try: module = __import__("module." + userinput) module = sys.modules["module." + userinput] except: ' error when trying to load %s ' % userinput
Cuando realiza una from ... import ...
, vincula el objeto al espacio de nombres local, por lo que todo lo que necesita es volver a importarlo. Sin embargo, como el módulo ya está cargado, solo volverá a importar la misma versión de la clase, por lo que también deberá volver a cargar el módulo. Así que esto debería hacerlo:
from bots.google import GoogleBot ... # do stuff ... reload(bots.google) from bots.google import GoogleBot
Si por alguna razón no sabe el nombre del módulo, puede obtenerlo de GoogleBot. módulo .
def reload_class(class_obj): module_name = class_obj.__module__ module = sys.modules[module_name] pycfile = module.__file__ modulepath = string.replace(pycfile, ".pyc", ".py") code=open(modulepath, 'rU').read() compile(code, module_name, "exec") module = reload(module) return getattr(module,class_obj.__name__)
Hay muchas comprobaciones de errores que puede hacer al respecto. Si utiliza variables globales, probablemente tendrá que averiguar qué sucede en ese momento.