путь импорта 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
by /path/to/projects/cust1/proj1
(например, путем установки PYTHONPATH
или добавлять до 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
by /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 с тем же именем, но внутренние детали все еще неясны для меня.
в любом случае, очевидное решение-добавить еще один уровень квалификации пространства имен, превратив каталоги проектов в супер-пакеты: Add __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
поэтому я могу импортировать из всех проектов последовательным образом, если есть проекты и пакеты с одинаковыми именами ?
3 ответов
этой это решение моей проблемы, хотя сначала это может быть не очевидно.
в моих проектах я теперь ввел соглашение об одном пространстве имен для каждого клиента. В каждой папке клиента (cust1
, cust2
, etc.), есть __init__.py
файл с таким кодом:
import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)
все остальные __init__.py
файлы в моих пакетах пусты (в основном потому, что у меня еще не было времени, чтобы узнать, что еще с ними делать).
As объяснил здесь, 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
на a с другой стороны, мне пришлось создать еще более глубокую структуру папок проекта:
/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
выполняется, независимо от того, из какого проекта я что-то импортирую.
вы должны использовать отличные virtualenv и virtualenvwrapper инструменты.
Что произойдет, если вы случайно импортировать код из одного клиента/проекта в другой и не заметит? Когда вы доставите его, он почти наверняка потерпит неудачу. Я бы принял соглашение о создании PYTHONPATH для одного проекта за раз, а не пытаться сделать все, что вы когда-либо писали, импортируемым сразу.
вы можете использовать сценарий-оболочку для каждого проекта для установки PYTHONPATH и запуска python или использовать сценарии для переключения сред при переключении проектов.
конечно некоторые проекты хорошо зависят от других проектов (тех библиотек, которые вы упомянули), но если вы хотите, чтобы клиент мог импортировать сразу несколько проектов, вам нужно организовать, чтобы имена не конфликтовали. Вы можете иметь эту проблему только тогда, когда у вас есть несколько проектов на PYTHONPATH, что не предполагается использовать вместе.