關於python中的setup.py

發表於2015-09-08

前言

其實對於setup.py和setup.cfg的關注是從OpenStack的原始碼包中開始的,OpenStack每個元件的釋出時都是一個tar.gz包,同樣,我們直接從github上clone程式碼後也會發現兩個檔案的存在。當閱讀Nova或Ceilometer(其他元件可能也會涉及)的程式碼時,發現setup.cfg中內容對於程式碼的理解有很大的影響。那麼,到底setup.py和setup.cfg是幹什麼的?

setup.py

我們從例子開始。假設你要分發一個叫foo的模組,檔名foo.py,那麼setup.py內容如下:

然後,執行python setup.py sdist為模組建立一個原始碼包

在當前目錄下,會建立dist目錄,裡面有個檔名為foo-1.0.tar.gz,這個就是可以分發的包(如果使用命令python setup.py bdist_egg,那麼會在dist目錄中生成foo-1.0-py2.7.egg包,setup.py中第一句引入需要改為from setuptools import setup)。使用者拿到這個包後,解壓,到foo-1.0目錄下執行:python setup.py install,那麼,foo.py就會被拷貝到python類路徑下,可以被匯入使用(如果安裝是egg檔案,會把egg檔案拷貝到dist-packages目錄下)。

對於Windows,可以執行python setup.py bdist_wininst生成一個exe檔案;若要生成RPM包,執行python setup.py bdist_rpm,但系統必須有rpm命令的支援。可以執行下面的命令檢視所有格式的支援:

setup函式還有一些引數:

1、packages
告訴Distutils需要處理那些包(包含__init__.py的資料夾)
2、package_dir
告訴Distutils哪些目錄下的檔案被對映到哪個原始碼包,感覺好像是一個相對路徑的定義。一個例子:package_dir = {'': 'lib'},表示以lib為主目錄。
3、ext_modules
是一個包含Extension例項的列表,Extension的定義也有一些引數。
4、ext_package
定義extension的相對路徑
5、requires
定義依賴哪些模組
6、provides
定義可以為哪些模組提供依賴
7、scripts
指定python原始碼檔案,可以從命令列執行。在安裝時指定--install-script
8、package_data
通常包含與包實現相關的一些資料檔案或類似於readme的檔案。

表示包含所有目錄下的txt檔案和mypkg/data目錄下的所有dat檔案。

9、data_files
指定其他的一些檔案(如配置檔案)

 

規定了哪些檔案被安裝到哪些目錄中。如果目錄名是相對路徑,則是相對於sys.prefixsys.exec_prefix的路徑。如果沒有提供模板,會被新增到MANIFEST檔案中。

執行sdist命令時,預設會打包哪些東西呢?

  • 所有由py_modulespackages指定的原始碼檔案
  • 所有由ext_moduleslibraries指定的C原始碼檔案
  • scripts指定的指令碼檔案
  • 類似於test/test*.py的檔案
  • README.txt或README,setup.py,setup.cfg
  • 所有package_datadata_files指定的檔案

還有一種方式是寫一個manifest template,名為MANIFEST.in,定義如何生成MANIFEST檔案,內容就是需要包含在分發包中的檔案。一個MANIFEST.in檔案如下:

 setup.cfg

setup.cfg提供一種方式,可以讓包的開發者提供命令的預設選項,同時為使用者提供修改的機會。對setup.cfg的解析,是在setup.py之後,在命令列執行前。

setup.cfg檔案的形式類似於

其中,command是Distutils的命令引數,option是引數選項,可以通過python setup.py --help build_ext方式獲取。

需要注意的是,比如一個選項是–foo-bar,在setup.cfg中必須改成foo_bar的格式

符合Distutils2的setup.cfg有些不同。包含一些sections:
1、global
定義Distutils2的全域性選項,可能包含commands,compilers,setup_hook(定義指令碼,在setup.cfg被讀取後執行,可以修改setup.cfg的配置,pbr就用到了這個)
2、metadata
3、files

  • packages_root:根目錄
  • packages
  • modules
  • scripts
  • extra_files

Setuptools

上面的setup.py和setup.cfg都是遵循python標準庫中的Distutils,而setuptools工具針對Python官方的distutils做了很多針對性的功能增強,比如依賴檢查,動態擴充套件等。很多高階功能我就不詳述了,自己也沒有用過,等用的時候再作補充。詳情可參見這裡

一個典型的遵循setuptools的指令碼:

setuptools相對distutils,增強的關鍵字:
include_package_data:為True時自動新增受版本控制的資料檔案,可替代package_data,同時,exclude_package_data可以排除某些檔案。當你需要加入沒有被版本控制的檔案時,還是老老實實使用package_data吧。
install_requires:代替require函式。表示當前包的安裝依賴於哪些分發包,這些資訊會寫入egg的元資訊中,這些包在安裝時會自動(從PyPI)下載並安裝。如果包在PyPI中找不到,則會從dependency_links標識的URL中獲取。
extras_require:當前包的高階/額外特性需要依賴的分發包。
entry_points:這個很經典。見下面的講解。
setup_requires: 安裝指令碼執行時需要依賴的分發包,主要用於構建過程。注意,這裡列出的包不會自動安裝,如果需要,同時要在install_requires中指定。
dependency_links:URL地址。這些地址在安裝setup_requirestests_require指定的包時使用。會寫入egg的metadata資訊中。

如何讓一個egg可被執行?

如何定義一個可選特性?

表示如果系統安裝docutils了,那麼可提供reST特性。當然,docutils包不會自動安裝,只有第三方包依賴本包的reST特性時,才會下載安裝。

特性如何使用呢?需要與entry points結合使用,在上一個setup.py指令碼中增加:

表示如果要使用rst2pdf指令碼,就要安裝docutils。

或者被其他project依賴:install_requires = ["Project-A[PDF]"]

我想大家最熟悉的就是這個特性了吧。比如一個部落格系統想用不同的外掛支援不同的語言輸出格式,那麼就可以定義一個“entry point group”,不同的外掛就可以註冊“entry point”,外掛註冊的示例:

 管理依賴

我們寫依賴宣告的時候需要在 setup.py 中寫好抽象依賴(install_requires),在 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 會跳過安裝。

Differences between distribute, distutils, setuptools and distutils2

Distutils is the standard tool used for packaging. It works rather well for simple needs, but is limited and not trivial to extend.

Setuptools is a project born from the desire to fill missing distutils functionality and explore new directions. In some subcommunities, it’s a de facto standard. It uses monkey-patching and magic that is frowned upon by Python core developers.

Distribute is a fork of Setuptools that was started by developers feeling that its development pace was too slow and that it was not possible to evolve it. Its development was considerably slowed when distutils2 was started by the same group. 2013-August update: distribute is merged back into setuptools and discontinued.

Distutils2 is a new distutils library, started as a fork of the distutils codebase, with good ideas taken from setup tools (of which some were thoroughly discussed in PEPs), and a basic installer inspired by pip. The actual name you use to import Distutils2 is packaging in the Python 3.3+ standard library, or distutils2 in 2.4+ and 3.1–3.2. (A backport will be available soon.) Distutils2 did not make the Python 3.3 release, and it was put on hold.

PBR

pbr是setuptools的輔助工具,最初是為OpenStack開發(https://launchpad.net/pbr),基於d2to1

A library for managing setuptools packaging needs in a consistent manner.

pbr會讀取和過濾setup.cfg中的資料,然後將解析後的資料提供給setup.py作為引數。包含如下功能:
1、從git中獲取Version、AUTHORS and ChangeLog資訊
2、Sphinx Autodoc。pbr會掃描project,找到所有模組,生成stub files
3、Requirements。pbr會讀取requirements.txt,生成setup函式需要的install_requires/tests_require/dependency_links

這裡需要注意,在requirements.txt檔案的頭部可以使用:--index https://pypi.python.org/simple/,這一行把一個抽象的依賴宣告如 requests==1.2.0 轉變為一個具體的依賴宣告 requests 1.2.0 from pypi.python.org/simple/

4、long_description。從README.rst, README.txt or README file中生成long_description引數

使用pbr很簡單:

使用pbr時,setup.cfg中有一些配置。在[files]中,有三個key:
packages:指定需要包含的包,行為類似於setuptools.find_packages
namespace_packages:指定namespace packages
data_files: 指定目的目錄和原始檔路徑,一個示例:

 Babel

A collection of tools for internationalizing Python applications

Babel是 Python 的一個國際化工具包,提供了對distutils或setuptools的支援,包含一些命令。

1、compile_catalog
類似於msgfmt工具,takes a message catalog from a PO file and compiles it to a binary MO file.

2、extract_messages
類似於xgettext,it can extract localizable messages from a variety of difference source files, and generate a PO (portable object) template file from the collected messages.

 

3、update_catalog
類似於msgmerge,it updates an existing translations catalog based on a PO template file (POT).

setup.py和pip

表面上,python setup.py installpip install都是用來安裝python包的,實際上,pip提供了更多的特性,更易於使用。體現在以下幾個方面:

  • pip會自動下載依賴,而如果使用setup.py,則需要手動搜尋和下載;
  • pip會自動管理包的資訊,使解除安裝/更新更加方便和容易,使用pip uninstall即可。而使用setup.py,必須手動刪除,有時容易出錯。
  • pip提供了對virtualenv更好的整合。

結語

OK,講了這麼多瑣碎的東西,現在去看看Nova或Ceilometer的setup指令碼,是不是一下清晰了很多?!但說實話,setup.py的使用,我還不能講的特別清楚,需要在後續的實戰中學習。

 

相關文章