Поместить отдельные пакеты Python в одно и то же пространство имен? - PullRequest
14 голосов
/ 18 января 2009

Я разрабатываю каркас Python, в котором «аддоны» написаны в виде отдельных пакетов. I.e.:

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

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

Ответы [ 5 ]

6 голосов
/ 12 ноября 2009

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

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

или в setuptools:

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

4 голосов
/ 18 января 2009

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

Возможно. Модуль 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 (). Естественно, вы также можете определить протоколы для аддонов, чтобы взаимодействовать с базовым поведением фреймворка и друг с другом. Вы также можете делать такие вещи, как внутренние классы, которые можно переопределить подклассом фреймворка, чтобы настраивать его без необходимости исправления классов, которые могут повлиять на другие приложения, если вам нужен такой уровень сложности.

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

4 голосов
/ 19 января 2009

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

1 голос
/ 24 октября 2017

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

0 голосов
/ 18 января 2009

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

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

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

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

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

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

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