我有这样的包裹布局
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
(位于conda环境中),则从顶层pip install -e .
文件夹内部,然后路径/path/to/myproject
最终成为sys.path
全部的一部分时间。
例如,如果我复制了myproject
仓库的新副本并将其存储在myproject2
等新文件夹中,然后在该文件夹中的交互式解释器中进行了一些工作,发现sys.path
在启动时仍会自动拥有/path/to/myproject
。
我的直觉是某种原因,因为pip install -e
会将文件从/path/to/myproject
符号链接到site-packages
或其他合适的位置,并且Python中的模块系统初始化必须做一些特殊的处理,它遵循符号链接,并自动将源目录添加到路径。
我遇到的问题是deploy/
中的(非打包)脚本的问题,这些脚本使用绝对导入来引用同一文件夹中的其他脚本(因为它们打算从{{1 }}),如何防止Python仅查找该文件夹的副本,该文件夹对于使用myproject
并因此出现在Python路径上的myproject
的特定克隆而存在?
已添加
这也让我感到困惑,在这种情况下,名称空间包会发生什么。例如,如果pip install -e
文件夹中没有deploy/
文件,但是您正在执行__init__.py
工作目录中的内容(或者在Python路径中包含该内容),则在Python中3.3+,它将被视为可以跨越目录的名称空间包。
那如果我两个都拥有怎么办
/path/to/myproject
然后,如果我在/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/
在Python路径中?
然后,最后一个问题将是这对绝对跨越相对目录和相对优先级以及意外跨越两个目录的“巨型” /path/to/myproject
名称空间包内部的导入优先级有什么影响。
答案 0 :(得分:1)
我认为是site-packages
目录中的*.pth
文件(很可能是easy-install.pth
文件)会影响sys.path
的值。此效果不依赖于当前工作目录。我仍然不完全确定site-packages
目录中的*.egg-link
文件到底起什么作用(尽管它的确至少部分是作为平台独立的符号链接的替代手段)。
关于名称空间包...让我们考虑以下目录树:
.
├── 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的 path 上:
$ 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']
可以通过在解释器的.pth
目录中的site-packages
中写入目录的位置来将目录添加到Python的路径中:
$ 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
)的版本,则这是从导入的初始化包中得到的版本。
注释