Я работаю над созданием модуля Python, который отображает API, предоставляемый другим языком / фреймворком, в Python.В идеале я хотел бы, чтобы это было представлено в виде единого корневого пакета, который предоставляет вспомогательные методы и который отображает все пространства имен в этой другой структуре в пакеты / модули Python.Для удобства возьмем в качестве примера CLR:
import clr.System.Data
import clr.System.Windows.Forms
Здесь clr
- это волшебный пакет верхнего уровня, который предоставляет пространства имен CLR System.Data
и System.Windows.Forms
подпакетов / подмодулей (пока что).как я вижу, пакет - это просто модуль с дочерними модулями / пакетами; все еще допустимо иметь в нем другие виды элементов).
Я прочитал PEP-302 инаписал простую программу-прототип, которая достигает аналогичного эффекта, устанавливая пользовательский хук meta_path
.Модуль clr
сам по себе является правильным модулем Python, который при импорте устанавливает __path__ = []
(что делает его пакетом, так что import
вообще пытается даже искать подмодули) и регистрирует ловушку.Сама ловушка перехватывает любую загрузку пакета, где полное имя пакета начинается с "clr."
, динамически создает новый модуль, используя imp.new_module()
, регистрирует его в sys.modules
и использует пиковую пыль и радуги, чтобы заполнить его классами и методами изоригинальный API.Вот код:
clr.py
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()
В целом все работает нормально.Python гарантирует, что модули в цепочке импортируются слева направо, поэтому clr
всегда импортируется первым, и он устанавливает ловушку, которая позволяет импортировать оставшуюся часть цепочки.
Однако яинтересно, что я здесь делаю - это перебор.В конце концов, я устанавливаю глобальный хук, который будет вызываться для любого импорта модуля, даже если я отфильтрую те, которые меня не интересуют.Возможно, есть какой-нибудь способ установить хук, который будет вызываться только для импорта из моего конкретного пакета, а не другие?Или вышеприведенный Правильный способ делать подобные вещи в Python?