Подтвердить что ты не робот

Помещение отдельных пакетов python в одно и то же пространство имен?

Я разрабатываю структуру python, которая будет иметь "аддоны", написанные как отдельные пакеты. То есть:.

import myframework
from myframework.addons import foo, bar

Теперь то, что я пытаюсь организовать, так это то, что эти аддоны могут быть распределены отдельно от основной структуры и введены в пространство имен myframework.addons.

В настоящее время моим лучшим решением является следующее. Надстройка будет развернута (скорее всего, в {python_version}/site-packages/ так:

fooext/
fooext/__init__.py
fooext/myframework/
fooext/myframework/__init__.py
fooext/myframework/addons/
fooext/myframework/addons/__init__.py
fooext/myframework/addons/foo.py

fooext/myframework/addons/__init__.py будет иметь код расширения пути pkgutil:

import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)

Проблема заключается в том, что для этого необходимо, чтобы PYTHONPATH содержал fooext/, но единственное, что у него было бы, это родительский каталог установки (скорее всего, упомянутый выше site-packages).

Решением этого является наличие дополнительного кода в myframework/addons/__init__.py, который преобразует sys.path и ищет любые модули с подпакелем myframework, и в этом случае он добавляет его в sys.path, и все работает.

Еще одна идея, которую я написал, - это написать файлы аддонов непосредственно в myframework/addons/ место установки, но тогда это создаст разницу между развертыванием и развертыванием пространства имен.

Есть ли лучший способ выполнить этот или, возможно, другой подход к вышеуказанной проблеме распространения?

4b9b3361

Ответ 1

Есть ли лучший способ выполнить этот или, возможно, другой подход к вышеуказанной проблеме распространения?

Возможно. Настройка модуля/пакета Python, как правило, сложна для динамического изменения этого типа, но его система объектов/классов открыта и расширяема четко определенным образом. Когда модули и пакеты не имеют достаточных возможностей для инкапсуляции вашего проекта, вы можете использовать классы вместо этого.

Например, вы могли бы использовать функциональность расширения в совершенно другом пакете, но позволить ему вводить классы в базовую инфраструктуру через определенный интерфейс. например. myframework/_ _ init _ _.py, содержащий базовую оболочку приложения:

class MyFramework(object):
    """A bare MyFramework, I only hold a person name
    """
    _addons= {}
    @staticmethod
    def addAddon(name, addon):
        MyFramework._addons[name]= addon

    def __init__(self, person):
        self.person= person
        for name, addon in MyFramework._addons.items():
            setattr(self, name, addon(self))

Тогда у вас могут быть функции расширения в файле myexts/helloer.py, который содержит ссылку на экземпляр класса "владелец" или "внешний" MyFramework:

class Helloer(object):
    def __init__(self, owner):
        self.owner= owner
    def hello(self):
        print 'hello '+self.owner.person

import myframework
myframework.MyFramework.addAddon('helloer', Helloer)

Итак, теперь, если вы просто "импортируете myframework", вы получаете только базовые функции. Но если вы также "импортируете myexts.helloer", вы также получаете возможность вызвать MyFramework.helloer.hello(). Естественно, вы также можете определять протоколы для аддонов для взаимодействия с основным поведением структуры и друг с другом. Вы также можете делать такие вещи, как внутренние классы, подкласс структуры может переопределяться для настройки без использования классов обезьян-патчей, которые могут повлиять на другие приложения, если вам нужен этот уровень сложности.

Инкапсулирующее поведение, подобное этому, может быть полезно, но обычно это раздражает работу по адаптации кода уровня модуля, который у вас уже есть, чтобы соответствовать этой модели.

Ответ 3

Setuptools имеет возможность искать "точки входа" (функции, объекты, что угодно) по имени. Trac использует этот механизм для загружать свои плагины, и он работает хорошо.

Ответ 4

Похоже, что то, что вам нужно, можно выполнить довольно аккуратно с помощью захватов импорта.

Это способ написания пользовательского кода загрузки, который может быть связан с пакетом (или в вашем случае с каркасом) для выполнения загрузки всех подпакетов и модулей, а не с использованием механизма загрузки по умолчанию python. Затем вы можете установить загрузчик в сайт-пакеты как базовый пакет или под свою структуру.

Когда обнаружен пакет, связанный с загрузчиком (который может быть просто жестко привязан к относительному пути, если это необходимо), он всегда будет использовать загрузчик для загрузки всех надстроек, например. Это имеет то преимущество, что не требует каких-либо действий PYTHONPATH, который обычно стоит как можно короче.

Альтернативой этому является использование файлов init для перенаправления вызова на импорт для подмодуля к тому, который вы хотите его поднять, но это немного беспорядочно. p >

Более подробную информацию о крюках импорта можно найти здесь:

http://www.python.org/dev/peps/pep-0302/

Ответ 5

Для пространств имен существует совершенно новая настройка. Посмотрите Упаковочные пакеты пространства имен. Короче говоря, у вас есть три варианта, основанный на том, насколько ты совместим с тем, что нужно для вашего кода. Существует также соответствующий PEP, который заменяет упомянутые в других ответах: PEP 420.