pip install -e, Python path и пакеты пространства имен - PullRequest
1 голос
/ 10 января 2020

У меня есть макет пакета, подобный этому

myproject/
    setup.py   # contains package info for "myproject" package
    myproject/ # contains various Python source files
    deploy/    # contains non-package Python scripts for deployment tools
    tests/ 
    ...

Внутри папки myproject верхнего уровня, если я выдаю pip install -e . (это в среде conda), тогда путь /path/to/myproject заканчивается быть частью sys.path все время.

Например, если я сделаю новый клон репо myproject и сохраню его в новой папке, например, myproject2, скажем, затем сделаю несколько работая в интерактивном переводчике в этой папке, я обнаружил, что sys.path по-прежнему будет автоматически иметь /path/to/myproject при запуске.

Я догадываюсь, что это происходит как-то, потому что pip install -e будет ссылаться на файлы из /path/to/myproject в site-packages или другое подходящее место, и инициализация системы модулей в Python должна выполнить некоторую специальную обработку, когда она следует по символической ссылке и автоматически добавляет исходный каталог в путь.

Проблема, с которой я сталкиваюсь, заключается в для (не пакетных) сценариев в deploy/, которые используют абсолютный импорт для ссылки на другие сценарии в той же папке (поскольку они предназначены для выполнения с верхнего уровня myproject), как n Я запрещаю Python вместо того, чтобы искать только копию этой папки, которая существует для конкретного клона myproject, который использовал pip install -e и, таким образом, появляется на пути Python?

Добавлено

Меня также сбивает с толку, что может произойти с пакетами пространства имен в этом случае. Например, если в папке deploy/ нет файла __init__.py, но вы выполняете какие-либо действия из рабочего каталога /path/to/myproject (или у вас есть это в пути Python), то в Python 3.3+ он будет рассматриваться как пакет пространства имен, который может охватывать каталоги.

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

/path/to/myproject   # which has been installed with pip install -e
/path/to/myproject2  # not installed, extra clone of the same project

, тогда, если я нахожусь в /path/to/myproject2, имеющем дело с deploy/ сценарии, могут ли они в конечном итоге рассматриваться как пакет пространства имен, который также включает /path/to/myproject/deploy/, так как /path/to/myproject всегда находится в пути Python в силу pip install -e?

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

1 Ответ

1 голос
/ 13 января 2020

Я полагаю, что файл *.pth в каталоге site-packages (скорее всего, файл easy-install.pth) влияет на значение sys.path. Этот эффект не зависит от текущего рабочего каталога. Я до сих пор не совсем уверен, какую именно роль играет файл *.egg-link в каталоге site-packages (хотя он действительно подразумевается, по крайней мере частично, как независимая от платформы замена символических ссылок).

Что касается пакетов пространства имен ... Давайте рассмотрим следующее дерево каталогов:

.
├── alfa
│   ├── bravo
│   │   ├── one.py
│   │   └── zero.py
│   └── zero.py
├── foo
│   ├── bar
│   │   ├── two.py
│   │   └── zero.py
│   └── zero.py
└── src
    ├── alfa
    │   ├── bravo
    │   │   ├── three.py
    │   │   └── zero.py
    │   └── zero.py
    └── foo
        ├── bar
        │   ├── four.py
        │   └── zero.py
        ├── __init__.py
        └── zero.py

Все *.py файлы содержат следующее:

print(__name__, __file__)

Обратите внимание, как это существует только один инициализатор пакета src/foo/__init__.py.

Модули alfa.bravo.one и foo.bar.two импортируются, поскольку текущий каталог всегда находится в пути поиска интерпретатора Python (sys.path):

$ python3 -c 'import alfa.bravo.one'
alfa.bravo.one /home/sinoroc/workspace/so-59682278/alfa/bravo/one.py
$ python3 -c 'import foo.bar.two'
foo.bar.two /home/sinoroc/workspace/so-59682278/foo/bar/two.py

Но импорт alfa.bravo.three и foo.bar.four невозможен:

$ python3 -c 'import alfa.bravo.three'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ModuleNotFoundError: No module named 'alfa.bravo.three'
$ python3 -c 'import foo.bar.four'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ModuleNotFoundError: No module named 'foo.bar.four'

Это так, потому что они находятся в каталоге src, а этот каталог не в Python ' s путь :

$ python3 -c 'import sys; print(sys.path)'
['', '/usr/lib/python36.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload', '/home/sinoroc/workspace/so-59682278/.venv/lib/python3.6/site-packages']

Каталог можно добавить к пути Python, записав его местоположение в .pth в каталоге site-packages интерпретатора:

$ echo "${PWD}/src" > '.venv/lib/python3.6/site-packages/test.pth'
$ python3 -c 'import sys; print(sys.path)'
['', '/usr/lib/python36.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload', '/home/sinoroc/workspace/so-59682278/.venv/lib/python3.6/site-packages', '/home/sinoroc/workspace/so-59682278/src']

А теперь можно импортировать alfa.bravo.three и foo.bar.four:

$ python3 -c 'import alfa.bravo.three'
alfa.bravo.three /home/sinoroc/workspace/so-59682278/src/alfa/bravo/three.py
$ python3 -c 'import foo.bar.four'
foo /home/sinoroc/workspace/so-59682278/src/foo/__init__.py
foo.bar.four /home/sinoroc/workspace/so-59682278/src/foo/bar/four.py

Модуль alfa.bravo.one по-прежнему можно импортировать благодаря пакетам пространства имен:

$ python3 -c 'import alfa.bravo.one'
alfa.bravo.one /home/sinoroc/workspace/so-59682278/alfa/bravo/one.py

Но поскольку пакет foo из каталога src имеет инициализатор, foo не является пакетом пространства имен больше и модуль foo.bar.two не может быть импортирован:

$ python3 -c 'import foo.bar.two'
foo /home/sinoroc/workspace/so-59682278/src/foo/__init__.py
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ModuleNotFoundError: No module named 'foo.bar.two'

Теперь для модулей zero это немного удивительнее. В текущем каталоге и в каталоге src есть модули с точно таким же путем импорта:

$ python3 -c 'import alfa.zero'
alfa.zero /home/sinoroc/workspace/so-59682278/alfa/zero.py
$ python3 -c 'import alfa.bravo.zero'
alfa.bravo.zero /home/sinoroc/workspace/so-59682278/alfa/bravo/zero.py
$ python3 -c 'import foo.zero'
foo /home/sinoroc/workspace/so-59682278/src/foo/__init__.py
foo.zero /home/sinoroc/workspace/so-59682278/src/foo/zero.py
$ python3 -c 'import foo.bar.zero'
foo /home/sinoroc/workspace/so-59682278/src/foo/__init__.py
foo.bar.zero /home/sinoroc/workspace/so-59682278/src/foo/bar/zero.py

Если инициализированный пакет отсутствует (alfa), то версия в текущей рабочей каталог импортируется. Но если существует одна версия с инициализатором (foo), то это версия импортируемого инициализированного пакета.

Примечания

  • Python 3,6,9
...