Publicar ganchos de importación en Python 3

Me gustaría que se ejecuten algunas devoluciones de llamada cada vez que se importa un módulo en particular. Por ejemplo (utilizando una función falsa @imp.when_imported que realmente no existe):

 @imp.when_imported('numpy') def set_linewidth(numpy): import shutil numpy.set_printoptions(linewidth=shutil.get_terminal_size()[0]) 

Esta característica se diseñó en PEP 369: Publicar ganchos de importación, pero se retiró por el motivo:

Este PEP ha sido retirado por su autor, ya que gran parte del diseño detallado ya no es válido después de la migración a importlib en Python 3.3.

Pero Importlib no tiene una solución clara. ¿Cómo se usa importlib para implementar un importlib posterior a la importación?

El módulo de wrapt proporciona una implementación de esto.

Mira este video sobre wrapt , incluyendo esta característica:

No creas que la documentación para wrapt menciona todavía.

Algunas de las publicaciones de blogs al final de:

aunque hablar de ello.

Hay un módulo complementario para el autowrapt llamado autowrapt que le permite realizar parches de mono utilizando este mecanismo sin necesidad de cambiar el código de la aplicación para activarlo.

Me sorprendería saber que esta es la mejor manera de hacer esto … Sin embargo, desde las primeras versiones de python2.x, el parche de mono __import__ ha sido compatible. Podemos aprovechar eso aquí:

 try: import builtins # python3.x except ImportError: import __builtin__ as builtins # python2.x import sys import collections _builtin_import = builtins.__import__ def _my_import(name, globals=None, locals=None, fromlist=(), level=0): already_imported = name in sys.modules mod = _builtin_import( name, globals=globals, locals=locals, fromlist=fromlist, level=level) if not already_imported and name in _post_import_hooks: for hook in _post_import_hooks[name]: hook() return mod builtins.__import__ = _my_import _post_import_hooks = collections.defaultdict(list) def on_import(name): def decorator(func): _post_import_hooks[name].append(func) return func return decorator @on_import('numpy') def print_hi(): print('Hello Numpy') print('before numpy') import numpy print('after numpy') 

Esta respuesta hace un registro super simple para el registro de devoluciones de llamada. El decorador simplemente registra la función y luego la devuelve. No realiza ninguna comprobación sofisticada (para ver si el módulo ya está cargado, por ejemplo), pero podría extenderse fácilmente para hacerlo.

Obviamente, el inconveniente es que si algún otro módulo decide __import__ parche __import__ , entonces no tiene suerte. O este módulo o el otro probablemente terminarán rotos.

He probado esto y parece funcionar tanto en python2.x como en python3.x.

¿Esto funciona?

 import importlib class ImportHook: def __init__(self, func): self.func = func self.module = None def __enter__(self): return self def get_module(self, module_name): self.module = importlib.import_module(module_name) return self.module def __exit__(self, exc_type, exc_val, exc_tb): if self.module is not None: self.func(self.module) def set_linewidth(module): import shutil module.set_printoptions(linewidth=shutil.get_terminal_size()[0]) with ImportHook(set_linewidth) as hook: numpy = hook.get_module('numpy')