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

时间:2020-01-10 13:04:21

标签: python pip python-import pythonpath

我有这样的包裹布局

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名称空间包内部的导入优先级有什么影响。

1 个答案:

答案 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.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的 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.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

但是由于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