Специфичные для пакета хуки импорта в Python - PullRequest
14 голосов
/ 01 сентября 2011

Я работаю над созданием модуля 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?

1 Ответ

5 голосов
/ 01 сентября 2011

В целом, я думаю, что ваш подход выглядит хорошо. Я не стал бы беспокоиться о том, что он «глобальный», поскольку весь смысл в том, чтобы указать, какие пути должны обрабатываться вами. Перемещение этого теста в логику импорта просто излишне усложнит его, поэтому решение остается за разработчиком хука.

Всего одна маленькая проблема, может быть, вы могли бы использовать sys.path_hooks? Кажется, он немного менее «мощный», чем sys.meta_path

sys.path_hooks - список вызываемых объектов, который будет проверен в последовательность, чтобы определить, могут ли они обрабатывать данный элемент пути. Вызываемый вызывается с одним аргументом, элементом пути. Вызываемый должен поднять ImportError, если он не может обработать элемент пути, и вернуть объект импортера, если он может обработать элемент пути.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...