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

Путь импорта python: пакеты с тем же именем в разных папках

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

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

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

4b9b3361

Ответ 1

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

В моих проектах я теперь представил соглашение об одном пространстве имен для каждого клиента. В каждой папке клиента (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.

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

Ответ 3

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

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

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