[英]pip install -e, Python path and namespace packages
我有這樣的包布局
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 .
從頂層myproject
文件夾中。 (這是在 conda 環境中),然后路徑/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
並因此出現在 Python 路徑上的myproject
的特定克隆的此文件夾的副本?
添加
在這種情況下,命名空間包會發生什么也讓我感到困惑。 例如,如果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
總是憑借pip install -e
在 Python 路徑中?
然后最后一個問題是,這會對意外跨越兩個目錄的“巨型” deploy
命名空間包內的絕對與相對導入和導入優先級產生什么影響。
對sys.path
的值有影響的是site-packages
目錄中的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.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 的路徑中:
$ 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']
通過在解釋器的site-packages
目錄中的.pth
中寫入其位置,可以將目錄添加到 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
但是由於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
),那么這是導入的初始化包中的版本。
筆記
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.