Контейнеры импорта пакетов в Python

Я работаю над созданием модуля Python, который отображает API, предоставляемый другим языком / картой в Python. В идеале я бы хотел, чтобы это было представлено как один корневой пакет, который предоставляет вспомогательные методы и который сопоставляет все пространства имен в этой другой структуре с пакетами / модулями Python. Для удобства возьмем CLR в качестве примера:

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

Здесь clr представляет собой магический пакет верхнего уровня, который предоставляет пространства имен CLR подсистемы System.Data и System.Windows.Forms subpackages / subodules (насколько я вижу, пакет представляет собой всего лишь модуль с дочерними модулями / пакетами, он по-прежнему действителен для есть другие члены).

Я читал PEP-302 и написал простую прототипную программу, которая добилась аналогичного эффекта, установив собственный meta_path . Сам модуль __path__ = [] является правильным модулем Python, который при импорте устанавливает __path__ = [] (делает его пакетом, так что import даже попытки поиска подмодулей вообще) и регистрирует hook. Сам крючок перехватывает любую загрузку пакета, где полное имя пакета начинается с "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?

One Solution collect form web for “Контейнеры импорта пакетов в Python”

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

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

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

Python - лучший язык программирования в мире.