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

StackOverflow https://stackoverflow.com/questions/454691

Вопрос

Я разрабатываю среду 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/ место установки, но тогда это приведет к тому, что разработка и развернутое пространство имен будут различаться.

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

Это было полезно?

Решение

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

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

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

class MyFramework(object):
    """A bare MyFramework, I only hold a person's 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().Естественно, вы также можете определить протоколы для дополнений, которые будут взаимодействовать с базовым поведением платформы и друг с другом.Вы также можете делать такие вещи, как внутренние классы, которые подкласс платформы может переопределить для настройки без необходимости вносить исправления в классы, которые могут повлиять на другие приложения, если вам нужен такой уровень сложности.

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

Другие советы

Смотрите пакеты пространства имен:

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

или в setuptools:

http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages

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

Звучит так, что то, что вам нужно, может быть выполнено довольно аккуратно с помощью ловушек импорта.

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top