Ganchos de importación específicos del paquete en Python

Estoy trabajando en la creación de un módulo de Python que asigna la API proporcionada por un lenguaje / marco diferente a Python. Idealmente, me gustaría que esto se presente como un paquete raíz único que exponga los métodos de ayuda, y que asigne todos los espacios de nombres en ese otro marco a los paquetes / módulos de Python. Para mayor comodidad, tomemos CLR como ejemplo:

import clr.System.Data import clr.System.Windows.Forms 

Aquí clr es el paquete mágico de nivel superior que expone los espacios de nombres CLR System.Data y System.Windows.Forms subpaquetes / submódulos (por lo que puedo ver, un paquete es solo un módulo con módulos / paquetes secundarios; todavía es válido para tener otros tipos de miembros en el mismo).

He leído el PEP-302 y escribí un progtwig prototipo simple que logra un efecto similar instalando un meta_path personalizado. El módulo clr sí es un módulo Python adecuado que, cuando se importa, establece __path__ = [] (lo que lo convierte en un paquete, de modo que la import incluso intenta buscar submódulos) y registra el gancho. El propio gancho intercepta cualquier carga de paquete donde el nombre completo del paquete comienza con "clr." , crea dinámicamente el nuevo módulo usando imp.new_module() , lo registra en sys.modules , y usa polvo pixie y arco iris para llenarlo con clases y métodos de la API original. Aquí está el código:

cliente

 import sys import imp class MyLoader: def load_module(self, fullname): try: return sys.modules[fullname] except KeyError: pass print("--- load ---") print(fullname) m = imp.new_module(fullname) m.__file__ = "clr:" + fullname m.__path__ = [] m.__loader__ = self m.speak = lambda: print("I'm " + fullname) sys.modules.setdefault(fullname, m) return m class MyFinder: def find_module(self, fullname, path = None): print("--- find ---") print(fullname) print(path) if fullname.startswith("clr."): return MyLoader() return None print("--- init ---") __path__ = [] sys.meta_path.append(MyFinder()) 

test.py

 import clr.Foo.Bar.Baz clr.Foo.speak() clr.Foo.Bar.speak() clr.Foo.Bar.Baz.speak() 

En general, todo esto parece funcionar bien. Python garantiza que los módulos en la cadena se importan de izquierda a derecha, por lo que clr siempre se importa primero, y configura el gancho que permite importar el rest de la cadena.

Sin embargo, me pregunto si lo que estoy haciendo aquí es una exageración. Después de todo, estoy instalando un enlace global, al que se solicitará la importación de cualquier módulo, aunque filtre los que no me importan. ¿Hay, quizás, alguna forma de instalar un gancho que solo se llamará para importar de mi paquete particular, y no de otros? ¿O es el Camino correcto más arriba para hacer este tipo de cosas en Python?

En general, creo que su enfoque se ve bien. No me preocuparía que fuera “global”, ya que el objective principal es especificar qué rutas debe manejar usted. Mover esta prueba dentro de la lógica de importación simplemente la complicaría innecesariamente, por lo que queda en manos del implementador del gancho la decisión.

Solo una pequeña preocupación, tal vez podrías usar sys.path_hooks ? Parece ser un poco menos “poderoso” que sys.meta_path

sys.path_hooks es una lista de callables, que se verificará en secuencia para determinar si pueden manejar un elemento de ruta determinado. La llamada se llama con un argumento, el elemento de ruta de acceso. El invocable debe generar ImportError si no puede manejar el elemento de la ruta y devolver un objeto importador si puede manejar el elemento de la ruta.