依賴安裝:setup.py和requirements.txt的對比

發表於2016-09-23

Contents

  • setup.py vs requirements.txt
    • Python庫
    • Python應用
    • 那麼,抽象和具體又有什麼關係呢?
    • Setuptools的dependency_links
    • 開發可複用的包與不重複自己

對於 setup.pyrequirements.txt 的角色有很多誤解,很多人認為它們是兩個重複的事情,甚至創造了 工具 來處理 這種“重複”。

12.1. Python庫

這裡所說的Python庫是指那些被開發並且為了其他人來使用而釋出的東西,你可以在 PyPI 找到很多Python庫。為了更好的推廣和傳播 自己,Python庫會包含很多的資訊,比如它的名字,版本號,依賴等等。而 setup.py 就是用來提供這些資訊的:

這樣很簡單地宣告瞭這個Python庫的一些資訊。但是,你並沒有看到你可以從哪裡獲取這些依賴庫。 這裡並沒有提供一個url或者一個檔案系統來獲取這些依賴,而只是告訴我們依賴是 requestsbcrypt ,這很重要,我們可以稱這種宣告方式為“抽象的依賴”,它們只是以名字(或者加上了一個具體的版本號) 的方式來出現的。這就像我們說的“鴨子型別”,你不在乎它是什麼樣子的只要它看起來是requests

12.2. Python應用

這裡所講的Python應用是指你所要部署的一些東西,這是區別於我們之前所講的Python庫的。Python應用或許可以在 PyPI上找到,但是不像Python庫,它們並不是一種可以被開發者使用多次的工具性的東西。PyPI上的Python應用經常會 在這個應用的旁邊放置一個檔案用來宣告該應用部署的依賴。

一個應用經常會有很多依賴,或許會很複雜。這些依賴裡很多沒有一個名字,或者沒有我們說所的那些資訊。這便反映了 pip 的requirements檔案所做的事情了,一個典型的requirements檔案看起來是這樣的:

這裡每個依賴都標明瞭準確的版本號,一般一個Python庫對依賴的版本比較寬鬆,而一個應用則會依賴比較具體的版本號。雖然也許跑其他 版本的 requests 並不會出錯,但是我們在本地測試順利後,我們就會希望線上上也跑相同的版本。

檔案的頭部有一個 --index https://pypi.python.org/simple/ ,一般如果你不用宣告這項,除非你使用的不是PyPI。然而它卻是 requirements.txt 的一個重要部分, 這一行把一個抽象的依賴宣告 requests==1.2.0 轉變為一個具體的依賴宣告requests 1.2.0 from pypi.python.org/simple/ ,這不像“鴨子型別”,倒像一次isinstance 檢查。

12.3. 那麼,抽象和具體又有什麼關係呢?

讀到這裡你或許會說,OK, 我已經知道 setup.py 是為可發行的Python庫那些包準備的,而 requirements.txt 是為那些不被經常作為工具利用的Python應用準備的,但是我已經把我的 requirements.txt 讀進來填充了我的 install_requires=[...]啊(譯者注: 比如你在 setup.py 中把 requirements.txt 檔案讀取進來並切割成行列表,賦值給關鍵字 install_requires),那我為何要在乎這個區別呢?

對於抽象依賴和具體依賴的區分是非常重要的,這點使我們的PyPI映象源正常工作,這點允許我們可以在公司裡搭建我們 私有的包索引服務,甚至這點允許了你去fork一個包並改造它。因為一個抽象的依賴只是一個名字和一個可選的版本號,你可以從 PyPi來安裝它,或者從你自己的檔案系統,你可以fork它並改造它,只要你指明瞭正確的名字和版本號你就可以一直這麼使用下去。

一個極端點的情況是,你在該使用抽象依賴的地方使用了具體的依賴,這在Go語言中可以看到

這裡我們指明瞭一個具體的url。現在如果我以這種指明url的方式使用了這個庫,而且現在我想要改造這個庫(比如它缺失了我想要的某個功能, 或者有一個討厭的bug)。我可能不僅僅需要fork bar 這個庫,還需要fork依賴這個庫的其他庫。(譯者注:也就是說,想要替換一個底層依賴的話,需要改動依賴這個庫的其他依賴對該庫的依賴宣告。)

12.5. 開發可複用的包與不重複自己

那麼我們寫依賴宣告的時候需要在 setup.py 中寫好抽象依賴,在requirements.txt 中寫好具體的依賴,但是我們並不想維護兩份依賴檔案,這樣會讓我們很難 做好同步。 requirements.txt 可以更好地處理這種情況,我們可以在有 setup.py 的目錄裡寫下一個這樣的 requirements.txt

這樣 pip install -r requirements.txt 可以照常工作,它會先安裝該檔案路徑下的包,然後繼續開始解析抽象依賴,結合 --index 選項後轉換為具體依賴然後再安裝她們。

這個辦法可以讓我們解決一種類似這樣的情形:比如你有兩個或兩個以上的包在一起開發但是是分開發行的,或者說你有一個尚未釋出的包並把它分成了幾個部分。如果你的頂層的包 依然僅僅按照“名字”來依賴的話,我們依然可以使用requirements.txt 來安裝開發版本的依賴包:

這會首先從 https://github.com/foo/bar.git 來安裝包 bar , 然後進行到第二行 -e . ,開始安裝 setup 中的抽象依賴,但是包 bar 已經安裝過了, 所以 pip 會跳過安裝,而是仍然使用github.com上安裝了的開發版本的包 bar

相關文章