pip install -e,Python 路径和命名空间包



我有一个这样的包布局

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/ 
...

如果我发出pip install -e .(这是在 conda 环境中(,则从顶级myproject文件夹内部,则路径/path/to/myproject最终始终是sys.path的一部分。

例如,如果我制作myproject存储库的新克隆并将其存储在新文件夹中,例如myproject2,然后在该文件夹中的交互式解释器中做一些工作,我发现sys.path在启动时仍然会自动/path/to/myproject

我的预感是,这以某种方式发生,因为pip install -e会将文件从/path/to/myproject符号链接到site-packages或其他合适的位置,并且 Python 中的模块系统初始化必须执行一些特殊处理,它遵循符号链接并自动将源目录添加到路径。

我遇到的问题是deploy/中的(非包(脚本使用绝对导入来引用同一文件夹中的其他脚本(因为它们旨在从myproject的顶层执行(,我如何防止 Python 只找到这个文件夹的副本,该副本存在于使用pip install -e的特定克隆myproject,因此出现在 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

那么,如果我在处理deploy/脚本/path/to/myproject2结束,它们最终是否会被视为一个命名空间包,其中还包括/path/to/myproject/deploy/,因为/path/to/myproject总是在 Pythonpip install -e路径中?

然后最后一个问题是,这会对意外跨越两个目录的"巨型"deploy命名空间包中的绝对导入和相对导入和导入优先级产生什么影响。

它是site-packages目录中对sys.path的值有影响的easy-install.pth文件。此效果不依赖于当前工作目录。我仍然不完全确定*.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.onefoo.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.threefoo.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 的路径上:

$ 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 的路径中,方法是将其位置写入解释器site-packages目录中的.pth

$ 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.threefoo.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

但由于从src目录中foo的包具有初始值设定项,因此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的版本,则这是导入的初始化包中的版本。

笔记

  • 蟒蛇 3.6.9

最新更新