путь импорта python: пакеты с одинаковыми именами в разных папках - PullRequest
20 голосов
/ 20 января 2012

Я разрабатываю несколько проектов Python для нескольких клиентов одновременно.Упрощенная версия структуры папок моего проекта выглядит примерно так:

/path/
  to/
    projects/
      cust1/
        proj1/
          pack1/
            __init__.py
            mod1.py
        proj2/
          pack2/
            __init__.py
            mod2.py
      cust2/
        proj3/
          pack3/
            __init__.py
            mod3.py

Когда я, например, хочу использовать функциональность с proj1, я расширяю sys.path на /path/to/projects/cust1/proj1 (например, установив PYTHONPATH или добавив файл .pth в папку site_packages или даже изменив sys.path напрямую), а затем импортируйте модуль следующим образом:

>>> from pack1.mod1 import something

Когда я работаю над большим количеством проектов, бывает так, что разныепроекты имеют идентичные имена пакетов:

/path/
  to/
    projects/
      cust3/
        proj4/
          pack1/    <-- same package name as in cust1/proj1 above
            __init__.py
            mod4.py

Если я теперь просто увеличу sys.path на /path/to/projects/cust3/proj4, я все еще могу импортировать из proj1, но не из proj4:

>>> from pack1.mod1 import something
>>> from pack1.mod4 import something_else
ImportError: No module named mod4

Я думаю, что причина, по которой второй импорт завершается неудачно, заключается в том, что Python ищет только первую папку в sys.path, где он находит пакет pack1 и сдается, если он не находит там модуль mod4.Я спрашивал об этом в предыдущем вопросе, см. импорт модулей Python с тем же именем , но внутренние детали мне все еще неясны.

В любом случае, очевидное решение состоит в том, чтобы добавитьеще один уровень квалификации пространства имен, превратив каталоги проектов в суперпакеты: добавьте __init__.py файлы в каждую папку proj* и удалите эти папки из строк, на которые расширен sys.path, например,

$ export PYTHONPATH=/path/to/projects/cust1:/path/to/projects/cust3
$ touch /path/to/projects/cust1/proj1/__init__.py
$ touch /path/to/projects/cust3/proj4/__init__.py
$ python
>>> from proj1.pack1.mod1 import something
>>> from proj4.pack1.mod4 import something_else

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

/path/
  to/
    projects/
      cust3/
        proj1/    <-- same project name as for cust1 above
          __init__.py
          pack4/
            __init__.py
            mod4.py

Попытка импорта из mod4 больше не работает по той же причине, что и раньше:

>>> from proj1.pack4.mod4 import yet_something_else
ImportError: No module named pack4.mod4

Следуя тому же подходу, который решал эту проблему ранее, я бы добавил еще один слой пакета / пространства имен и превратил бы папки клиента в супер-супер-пакеты.

Однако это противоречит другим требованиям, предъявляемым к моему проекту.структура папок, например

  • структура разработки / выпуска для поддержки нескольких строк кода
  • другие виды исходного кода, такие как, например, JavaScript, SQL и т. Д.
  • другие файлы, помимо исходных файлов, например, документы или данные.

Менее упрощенный, более реальный-world описание некоторых папок проекта выглядит следующим образом:

/path/
  to/
    projects/
      cust1/
        proj1/
          Development/
            code/
              javascript/
                ...
              python/
                pack1/
                  __init__.py
                  mod1.py
            doc/
              ...
          Release/
            ...
        proj2/
          Development/
            code/
              python/
                pack2/
                  __init__.py
                  mod2.py

Я не понимаю, как я могу удовлетворить требования, которые интерпретатор Python предъявляет к структуре папок и к тем, которые у меня есть одновременно.Возможно, я мог бы создать дополнительную структуру папок с некоторыми символическими ссылками и использовать ее в sys.path, но, глядя на усилия, которые я уже делаю, у меня возникает ощущение, что во всем моем подходе что-то в корне не так.Что касается sidenote, мне также трудно поверить, что python действительно ограничивает меня в выборе имен папок с исходным кодом, как это происходит в случае, изображенном.

Как я могу настроить свой проектпапки и sys.path, чтобы я мог согласованно импортировать из всех проектов, если есть проекты и пакеты с одинаковыми именами?

Ответы [ 3 ]

16 голосов
/ 25 января 2012

Это является решением моей проблемы, хотя поначалу это может и не быть очевидным.

В моих проектах я теперь ввел соглашение об одном пространстве имен для каждого клиента.В каждой папке клиента (cust1, cust2 и т. Д.) Есть файл __init__.py с таким кодом:

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

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

Как объяснено здесь , extend_path удостоверяется, что Python знает, что существует более одногосубпакет в пакете, физически расположенный в другом месте, и, насколько я понимаю, интерпретатор не прекращает поиск после того, как ему не удается найти модуль по первому пути пакета, с которым он сталкивается в sys.path, но ищет все пути в __path__.

Теперь я могу получить доступ ко всему коду согласованным образом, перекрещивая между всеми проектами, например,

from cust1.proj1.pack1.mod1 import something
from cust3.proj4.pack1.mod4 import something_else
from cust3.proj1.pack4.mod4 import yet_something_else

С другой стороны, мне пришлось создать еще более глубокую структуру папок проекта:

/path/
  to/
    projects/
      cust1/
        proj1/
          Development/
            code/
              python/
                cust1/
                  __init__.py   <--- contains code as described above
                  proj1/
                    __init__.py <--- empty
                    pack1/
                    __init__.py <--- empty
                    mod1.py

но это кажется мне очень приемлемым, особенно учитывая, как мало мне нужно приложить усилий для поддержания этого соглашения.sys.path расширен для /path/to/projects/cust1/proj1/Development/code/python для этого проекта.

В sidenote, я заметил, что из всех файлов __init__.py для одного и того же клиента, тот в пути, который появляется первым в sys.pathвыполняется независимо от того, из какого проекта я импортирую что-либо.

0 голосов
/ 25 января 2012

Что произойдет, если вы случайно импортируете код из одного клиента / проекта в другого и не заметите?Когда вы доставите это почти наверняка потерпит неудачу.Я бы принял соглашение о настройке PYTHONPATH для одного проекта за раз, и не пытался бы сделать так, чтобы все, что вы когда-либо писали, импортировалось за один раз.

Вы можете использовать скрипт-обертку для каждого проекта, чтобы установитьPYTHONPATH и запуск python или использование сценариев для переключения сред при переключении проектов.

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

0 голосов
/ 20 января 2012

Вы должны использовать превосходные инструменты virtualenv и virtualenvwrapper .

...