Как импортировать пакеты пространства имен, для которых необходимо настроить sys.path? - PullRequest
3 голосов
/ 18 марта 2012

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

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

Пример:

# lib1 contains plugins/__init__.py and plugins/foo/__init__.py
# lib2 contains plugins/__init__.py and plugins/bar/__init__.py
# plugins is a namespace package

import sys

sys.path.append ('lib1')
import plugins.foo

sys.path.append ('lib2')
import plugins.bar

Приведенный выше код завершается с ошибкой ImportError, предположительно потому, что строка «import plugins.foo» инициализировала пространство имен / пакет плагинов и больше не предпринимается попытка поиска sys.path для других пакетов в пространстве имен.

Если я изменю код на этот:

sys.path.append ('lib1')
sys.path.append ('lib2')
import plugins.foo
import plugins.bar

Оба импорта работают, но я хотел бы добавить в sys.path после импорта plugins.foo.

Итак, мои вопросы:

  1. Верно ли мое предположение, что второй импорт завершается неудачно из-за пакета пространства имен?
  2. Есть ли обходной путь?

Ответы [ 2 ]

2 голосов
/ 18 марта 2012

Я думаю, что ваш диагноз правильный. Когда вы импортируете plugins.foo, python также загружает plugins и кэширует его в sys.modules. Я не копировал вашу настройку, но я попробую del sys.modules['plugins'], прежде чем импортировать bar. Если это не сработает, попробуйте перезагрузить plugins:

sys.path.append ('lib2')
import plugins
reload(plugins)
import plugins.bar

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

1 голос
/ 19 марта 2018

Это не ответ, а расширенный комментарий.

Ситуация импорта, представленная в исходном вопросе выше, возникает не только при попытке импортировать два разных плагина.В нашем случае мы пытаемся обеспечить локальный запасной вариант для случая, когда требуемый пакет Python не установлен в текущей системе.

Общая структура проекта для MVE может выглядеть следующим образом:

.
|-- src
|   `-- balla
|       |-- __init__.py
|       `-- hurga
|           `-- __init__.py
|-- src2
|   `-- balla
|       |-- __init__.py
|       `-- hurga2
|           `-- __init__.py
`-- test.py

, где src/balla/__init__.py и src2/balla/__init__.py имеют одинаковый исходный код:

#!/usr/bin/env python
try:
    import pkg_resources
    pkg_resources.declare_namespace(__name__)
except ImportError:
    from pkgutil import extend_path
    __path__ = extend_path(__path__, __name__)

.Два src/balla/hurga/__init__.py и src2/balla/hurga2/__init__.py пока пусты.В нашем основном скрипте test.py:

import sys, os

sys.path.append(os.path.join(os.path.dirname(sys.argv[0]), 'src'))

try:
    import balla.hurga2
except:
    sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), 'src2'))
    import balla.hurga2

мы сначала пытаемся импортировать пакет "balla.hurga2" напрямую из системы, надеясь, что кто-то установил его правильно (например, через pip).Первый sys.path.append в связи с дополнительной папкой src, попробуйте смоделировать тот факт, что установлены другие пакеты с общим пространством имен "balla" (например, balla.extended).Но balla.hurga2 нет, поэтому объявление пространства имен "balla" прошло успешно, а фактический импорт - нет.В пути except мы сейчас пытаемся исправить эту ситуацию, вставив локальный путь в наш резервный пакет "src2".На новом import balla.hurga2 старая запись "balla" в диктовке sys.modules кэшируется и остается неизменной.Это приводит к классическому сообщению об ошибке:

Traceback (most recent call last):
  File "test.py", line 9, in <module>
    import balla.hurga2
ImportError: No module named hurga2

Удалив кэшированную запись для пакета "balla" из sys.modules, проблема может быть решена:

import sys, os

sys.path.append(os.path.join(os.path.dirname(sys.argv[0]), 'src'))

try:
    import balla.hurga2
except:
    if 'balla' in sys.modules:
        del sys.modules['balla']
    sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), 'src2'))
    import balla.hurga2

Дополнительная-clause требуется для систем, в которых пакет balla отсутствует вообще.

...