程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Python的Distutils模塊

編輯:Python

參考   Python的Distutils模塊 - 雲+社區 - 騰訊雲

目錄

一、Distutils簡介

1.1、概念和術語

1.2、簡單例子

1.3、基本術語

1.4、Distutils術語

二、編寫setup腳本

2.1、列出整個包

2.2、列出單獨的模塊

2.3、擴展模塊

2.3.1、擴展名和包

2.3.2、擴展的源碼文件

2.3.3、其他選項

2.4、發布和包的關系

2.5、安裝腳本

2.6、安裝package data

2.7、安裝其他文件

2.8、元數據

2.9、調試setup腳本

三、配置文件

四、源碼發布

4.1、指定發布的文件

4.2、Manifest相關選項

4.3、MANIFEST.in模板

五、構建發布(Built Distributions)

六、Distutils與PYPI

七、簡單示例

7.1、純Python發布(包)

7.3、單獨的擴展模塊

八、其他


Distutils可以用來在Python環境中構建和安裝額外的模塊。新的模塊可以是純Python的,也可以是用C/C++寫的擴展模塊,或者可以是Python包,包中包含了由C和Python編寫的模塊。

一、Distutils簡介

1.1、概念和術語

對於模塊開發者以及需要安裝模塊的使用者來說,Distutils的使用都很簡單,作為一個開發者,除了編寫源碼之外,還需要:

  • 編寫setup腳本(一般是setup.py);
  • 編寫一個setup配置文件(可選);
  • 創建一個源碼發布;
  • 創建一個或多個構建(二進制)發布(可選);

有些模塊開發者在開發時不會考慮多個平台發布,所以就有了packagers的角色,它們從模塊開發者那取得源碼發布,然後在多個平台上面進行構建,並發布多個平台的構建版本。

1.2、簡單例子

由python編寫的setup腳本一般都非常簡單。作為autoconf類型的配置腳本,setup腳本可以在構建和安裝模塊發布時運行多次。比如,如果需要發布一個叫做foo的模塊,它包含在一個文件foo.py,那setup腳本可以這樣寫:

from distutils.core import setup
setup(name='foo',
version='1.0',
py_modules=['foo'],
) 

setup函數的參數表示提供給Distutils的信息,這些參數分為兩類:包的元數據(包名、版本號)以及包的信息(本例中是一個Python模塊的列表);模塊由模塊名表示,而不是文件名(對於包和擴展而言也是這樣);建議可以提供更多的元數據,比如你的名字,email地址和項目的URL地址。編寫好setup.py之後,就可以創建該模塊的源碼發布了:

python setup.py sdist 

對於Windows而言,命令是:

setup.py sdist

sdist命令會創建一個archive 文件(比如Unix上的tar文件,Windows上的zip文件),它包含setup.py, foo.py。該archive文件命名為foo-1.0.tar.gz(zip),解壓之後的目錄名是foo-1.0。如果一個用戶希望安裝foo模塊,他只需要下載foo-1.0.tar.gz,解壓,進入foo-1.0目錄,然後運行:

python setup.py install 

該命令最終會將foo.py復制到Python環境存放第三方模塊的目錄中。在linux環境下,運行該命令的輸出是:

# python setup.py install
running install
running build
running build_py
creating build
creating build/lib
copying foo.py -> build/lib
running install_lib
copying build/lib/foo.py -> /usr/lib/python2.7/site-packages
byte-compiling /usr/lib/python2.7/site-packages/foo.py to foo.pyc
running install_egg_info
Writing /usr/lib/python2.7/site-packages/foo-1.0-py2.7.egg-info 

該命令生成的文件是:

/usr/lib/python2.7/site-packages/foo-1.0-py2.7.egg-info

/usr/lib/python2.7/site-packages/foo.py

/usr/lib/python2.7/site-packages/foo.pyc

這個簡單的例子展示了Distutils的基本概念。第一,開發者和安裝者有同樣的用戶接口,也就是setup腳本,但他們使用的Distutils命令不同,sdist命令幾乎只有開發者使用,而install對於安裝者更常用。如果希望使用者的使用盡可能的簡單,則可以創建多個構建發布。比如,如果在Windows中,可以使用bdist_wininst命令創建一個exe安裝文件,下面的命令會在當前目錄中創建foo-1.0.win32.exe文件:

python setup.py bdist_wininst 

其他的構建發布有RPM(由bdist_rpm命令實現),Solaris pkgtool(bdist_pkgtool),以及HP-UX swinstall (bdist_sdux)。比如,下面的命令將會創建RPM文件foo-1.0.noarch.rpm(bdist_rpm命令必須運行於基於RPM的系統,比如Red Hat Linux, SuSE Linux, Mandrake Linux):

python setup.py bdist_rpm

可以通過下面的命令得到當前支持的發布格式:

python setup.py bdist --help-formats

1.3、基本術語

  1. 模塊(module):Python中可復用的基本代碼單元,可由其他代碼import的一塊代碼,這裡我們只關注三種類型的模塊:純python模塊,擴展模塊和包。
  2. 純python模塊(pure Python module):由python編寫的模塊,包含在單獨的py文件中(或者是pyc/pyo文件)。
  3. 擴展模塊(extension module):由實現Python的底層語言編寫的模塊(C/C++ for Python, Java for Jython)。通常包含在單獨的動態加載文件中,比如Unix中的so文件,windows中的DLL文件,或者是Jython擴展的java類文件。(注意,目前為止Distutils只能處理Python的C/C++擴展)
  4. 包(package):包是含其他模塊的模塊,經常由包含__init__.py文件的目錄發布。
  5. Root包(root package):包層次關系中的根(它不是真正的包,因為它不包含__init__.py文件)。

1.4、Distutils術語

  • 模塊發布(module distribution):一些Python模塊的集合,它們將會被一起安裝。一些常見的模塊發布有Numeric Python,PyXML,PIL,mxBase。
  • 純模塊發布:一個只包含純python模塊和包的模塊發布。
  • 非純模塊發布:至少包含一個擴展模塊的模塊發布。
  • 發布根:源碼樹的根目錄;setup.py所在的目錄。

二、編寫setup腳本

setup腳本是使用Distutils構建、發布和安裝模塊的核心。setup腳本的作用是向Distutils描述發布模塊的信息。從上面那個簡單的例子中可知,setup腳本主要是調用setup函數,而且模塊開發者向Distutils提供的模塊信息多數是由setup函數的關鍵字參數提供的。下面是一個更高級一些的例子:Distutils模塊本身的setup腳本:

setup(name='Distutils',
version='1.0',
description='Python Distribution Utilities',
author='Greg Ward',
author_email='[email protected]',
url='https://www.python.org/sigs/distutils-sig/',
packages=['distutils', 'distutils.command'],
) 

上面這個腳本有更多的元數據,列出的是兩個包(packages),而不是列出每個模塊。因為Distutils包含多個模塊,這些模塊分成了兩個包;如果列出所有模塊的話則是冗長且難以維護的。注意,在setup腳本中的路徑必須以Unix形式來書寫,也就是由”/”分割的。Distutils會在使用這些路徑之前,將這種表示方法轉換為適合當前平台的格式。

2.1、列出整個包

Setup函數的packages參數是一個列表,其中包含了Distutils需要處理(構建、發布、安裝等)的所有包。要實現此目的,那麼包名和目錄名必須能夠相互對應,比如包名是distutils,則意味著在發布的根目錄(setup腳本所在目錄)下存在distutils子目錄;再比如在setup腳本中packages = ['foo'],意味著要在setup腳本所在目錄下存在相應的foo目錄和foo/__init__.py文件。比如如果setup腳本內容如下:

setup(name='foo',
version='1.0',
packages = ['foo']
) 

而setup腳本所在目錄並沒有foo目錄(只有一個setup.py腳本),則在執行python setup.py bdist命令時,會打印如下錯誤:

error: package directory 'foo' does not exist 

如果創建了foo目錄,但是沒有foo/__init__.py文件,則Distutils會產生下面的警告,但是仍會處理該包:

package init file 'foo/__init__.py' not found (or not a regular file) 

可以使用package_dir選項來改變這種默認的對應規則。package_dir是個字典,其中的key是要安裝的包名,如果為空,則表明是root package,value就是該包(key)對應的源碼樹的目錄。比如如果setup.py內容如下:

setup(name='foo',
version='1.0',
package_dir = {'':'lib'},
packages = ['foo']
) 

則必須在目錄中存在lib子目錄,lib/foo子目錄,以及文件lib/foo/__init__.py。所以源碼樹如下:

setup.py  

lib/  

    foo/  

        __init__.py  

        foo.py  

最後生成的文件是:

\usr\local\lib\python2.7\dist-packages\ foo-1.0.egg-info

\usr\local\lib\python2.7\dist-packages\foo\__init__.py

\usr\local\lib\python2.7\dist-packages\foo\__init__.pyc

\usr\local\lib\python2.7\dist-packages\foo\foo.py

\usr\local\lib\python2.7\dist-packages\foo\foo.pyc

另外一個例子,foo包對應lib目錄,所以,foo.bar包就對應著lib/bar子目錄。所以如果在setup.py中這麼寫:

package_dir = {'foo':'lib'},
packages = ['foo',’foo.bar’] 

則必須存在lib/__init__.py,  lib/bar/__init__.py文件。源碼樹如下:

setup.py  

lib/  

    __init__.py  

    foo.py  

    bar/  

        __init__.py  

        bar.py  

最後生成的文件是:

\usr\local\lib\python2.7\dist-packages\ foo-1.0.egg-info

\usr\local\lib\python2.7\dist-packages\foo\__init__.py

\usr\local\lib\python2.7\dist-packages\foo\__init__.pyc

\usr\local\lib\python2.7\dist-packages\foo\foo.py

\usr\local\lib\python2.7\dist-packages\foo\foo.pyc

\usr\local\lib\python2.7\dist-packages\foo\bar\__init__.py

\usr\local\lib\python2.7\dist-packages\foo\bar\__init__.pyc

\usr\local\lib\python2.7\dist-packages\foo\bar\bar.py

\usr\local\lib\python2.7\dist-packages\foo\bar\bar.pyc

2.2、列出單獨的模塊

如果發布中僅包含較少的模塊,你可能更喜歡列出所有模塊,而不是列出包,特別是在root package中存在單一模塊的情況(或者根本就沒有包)。可以使用py_modules參數,比如下面的例子:

setup(name='foo',
version='1.0',
py_modules = ['mod1', 'pkg.mod2']
) 

它描述了兩個模塊,一個在root package中,另一個在pkg包中。根據默認的包/目錄對應規則,這兩個模塊存在於文件mod1.py和pkg/mod2.py中,並且要存在pkg/__init__.py文件(不存在的話,會產生報警:package init file 'pkg/__init__.py' not found (or not a regular file))。當然,也可以使用package_dir選項改變這種對應關系。所以,源碼樹如下:

setup.py  

mod1.py  

pkg/  

    __init__.py  

    mod2.py 

最終生成的文件是:

\usr\local\lib\python2.7\dist-packages\foo-1.0.egg-info

\usr\local\lib\python2.7\dist-packages\mod1.py

\usr\local\lib\python2.7\dist-packages\mod1.pyc

\usr\local\lib\python2.7\dist-packages\pkg\__init__.py

\usr\local\lib\python2.7\dist-packages\pkg\__init__.pyc

\usr\local\lib\python2.7\dist-packages\pkg\mod2.py

\usr\local\lib\python2.7\dist-packages\pkg\mod2.pyc

2.3、擴展模塊

在Distutils中描述擴展模塊較描述純python模塊要復雜一些。對於純python模塊,僅需要列出模塊或包,然後Distutils就會去尋找合適的文件,這對於擴展模塊來說是不夠的,你還需要指定擴展名、源碼文件以及其他編譯/鏈接需要的參數(需要包含的目錄,需要連接的庫等等)。描述擴展模塊可以由setup函數的關鍵字參數ext_modules實現。ext_modules是Extension實例的列表,每一個Extension實例描述了一個獨立的擴展模塊。比如發布中包含一個獨立的擴展模塊稱為foo,由foo.c實現,且無需其他編譯鏈接指令,那麼下面的語句就可以描述該擴展模塊:

Extension('foo', ['foo.c']) 

Extension可以從distutils.core中隨setup一起引入。因此,對於僅包含一個擴展模塊的發布來說,setup腳本如下:

from distutils.core import setup, Extension
setup(name='foo',
version='1.0',
ext_modules=[Extension('foo', ['foo.c'])],
) 

 底層的擴展構建機制是由build_ext命令實現的。Extension類在描述Python擴展時具有很大的靈活性。

2.3.1、擴展名和包

通常,Extension類的構造函數的第一個參數都是擴展的名字,比如下面的語句:

Extension('foo', ['src/foo1.c', 'src/foo2.c']) 

如果執行python  setup.py bdist,就會調用相應的編譯器和連接器命令,最終根據生成foo.so文件,存放在發布包的根目錄中,最終生成的文件是:

\usr\local\lib\python2.7\dist-packages\foo.so
\usr\local\lib\python2.7\dist-packages\foo-1.0.egg-info

又比如下面的語句:

Extension('pkg.foo', ['src/foo1.c', 'src/foo2.c']) 

使用的源文件是一樣的,最終生成的結果文件也是一樣的foo.so,唯一的不同是最終的結果文件存放的目錄,是在發布包的根目錄下的pkg目錄下。因此最終生成的文件是:

\usr\local\lib\python2.7\dist-packages\pkg\foo.so

\usr\local\lib\python2.7\dist-packages\foo-1.0.egg-info

如果一個包下有多個擴展,而且要把這些擴展都放在統一的目錄下,則可以使用ext_package關鍵字,比如下面的語句:

setup(...,
ext_package='pkg',
ext_modules=[Extension('foo', ['src/foo.c']),
Extension('subpkg.bar', ['src/bar.c'])]
) 

上面的描述將會編譯src/foo.c為pkg.foo,將src/bar.c編譯為pkg.subpkg.bar。因此源碼樹如下:

setup.py  

src/  

    foo.c  

    bar.c  

最終生成的文件是:

\usr\local\lib\python2.7\dist-packages\foo-1.0.egg-info

\usr\local\lib\python2.7\dist-packages\pkg\foo.so

\usr\local\lib\python2.7\dist-packages\pkg\subpkg\bar.so

2.3.2、擴展的源碼文件

Extension構建函數的第二個參數是源文件的列表。目前Distutils僅支持C、C++和Objective-C擴展,所以這些源碼文件就是C、C++和Objective-C的源碼文件。(C++源碼文件的擴展名可以是.cc和.cpp,Unix和Windows編譯器都支持)。不過還可以在列表中包含SWIG接口文件(.i文件),build_ext命令知道如何處理SWIG接口文件。盡管會發生報警,但是可以像下面這樣傳遞SWIG選項:

setup(...,
ext_modules=[Extension('_foo', ['foo.i'],
swig_opts=['-modern', '-I../include'])],
py_modules=['foo'],
) 

或者是使用如下命令:

> python setup.py build_ext --swig-opts="-modern -I../include" 

在一些系統上,該列表中還可以包含能由編譯器處理的非源碼文件。當前只支持Windows message 文本文件(.mc)和Visual C++的資源定義文件(.rc)。它們將會編譯為二進制文件.res並且鏈接進可執行文件中。

2.3.3、其他選項

Extension還可以指定其他選項,比如可以指定頭文件目錄,define或undefine宏、需要鏈接的庫,鏈接時和運行時搜索庫的路徑等等。具體可參閱:

https://docs.python.org/2/distutils/setupscript.html#preprocessor-options

https://docs.python.org/2/distutils/setupscript.html#library-options

https://docs.python.org/2/distutils/setupscript.html#other-options

2.4、發布和包的關系

發布和包有三種關系:它依賴其他包,它服務於其他包,它淘汰其他包。這些關系可以分別用setup函數的參數requires ,provides 和obsoletes 來指定,具體參閱:https://docs.python.org/2/distutils/setupscript.html#relationships-between-distributions-and-packages。

2.5、安裝腳本

模塊通常不自己運行,而是由腳本引入。除了可以安裝模塊之外,還可以安裝能直接運行的腳本,具體參閱https://docs.python.org/2/distutils/setupscript.html#installing-scripts。

2.6、安裝package data

有時包中還需要安裝其他文件,這些文件與包的實現密切相關,或者是包含文檔信息的文本文件等,這些文件就叫做package data。使用setup函數中的package_data參數可以向packages中添加package data。該參數的值必須是個字典,字典的key就是package name,value是個list,其中包含了需要復制到package中的一系列路徑。這些路徑都是相對於包目錄而言的(比如package_dir),所以,這些文件必須存在於包的源碼目錄中。在安裝時,也會創建相應的目錄。比如,如果包中有一個包含數據文件的子目錄,源碼樹如下:

setup.py  

src/  

    mypkg/  

        __init__.py  

        module.py  

        data/  

            tables.dat  

            spoons.dat  

            forks.dat 

相應的setup函數可以這樣寫:

setup(...,  

      packages=['mypkg'],  

      package_dir={'mypkg': 'src/mypkg'},  

      package_data={'mypkg': ['data/*.dat']},  

      ) 

2.7、安裝其他文件

可以通過data_files選項來安裝除了上面提到過的文件之外的其他文件,比如配置文件,數據文件等。data_files是個列表,列表中的元素是(directory, files),比如:

setup(...,
data_files=[('bitmaps', ['bm/b1.gif', 'bm/b2.gif']),
('config', ['cfg/data.cfg']),
('/etc/init.d', ['init-script'])]
) 

(directory, files)中,directory表示文件最終要被安裝到的地方,如果它是相對路徑的話,則是相對於installation prefix而言(對於純python包而言,就是sys.prefix;對於擴展包,則是sys.exec_prefix)。files是要安裝的文件,其中的目錄信息(安裝前)是相對於setup.py所在目錄而言的,安裝時,setup.py根據files的信息找到該文件,然後將其安裝到directory中。

2.8、元數據

Setup腳本可以包含很多發布的元數據,比如名稱、版本、作者等信息,具體列表和注意信息,參閱https://docs.python.org/2/distutils/setupscript.html#additional-meta-data

2.9、調試setup腳本

如果在運行setup腳本是發生了錯誤,則Distutils會打印出簡單的錯誤信息,對於開發者而言這些錯誤信息可能不足以找到錯誤的原因。所以可以通過設置環境變量DISTUTILS_DEBUG,將其置為任意值(不能是空字符串),Distutils就會打印其執行過程的詳細信息,並且在發生異常時打印全部的traceback,並且在像C編譯器這樣的外部程序發生錯誤時,打印整個命令行。

三、配置文件

一般情況下,在構建發布時無法將所有的選項都確定下來,有些選項的值可能來自於用戶,或者用戶的系統。這也就是配置文件setup.cfg存在的目的,用戶可以通過修改該配置文件進行選項的配置。在構建時,選項的處理順序是setup腳本、配置文件,命令行。所以,安裝者可以通過修改setup.cfg文件來覆蓋setup.py中的選項;也可以通過運行setup.py時的命令行選項,來覆蓋setup.cfg。

配置文件的基本語法如下:

[command]
option=value
... 

command就是Distutils的命令(比如build_py,install等),option就是命令支持的選項。配置文件中的空行、注釋(以’#’開頭,直到行尾)會被忽略。可以通過--help選項得到某個命令支持的選項,比如:
 

> python setup.py --help build_ext
[...]
Options for 'build_ext' command:
--build-lib (-b) directory for compiled extension modules
--build-temp (-t) directory for temporary files (build by-products)
--inplace (-i) ignore build-lib and put compiled extensions into the
source directory alongside your pure Python modules
--include-dirs (-I) list of directories to search for header files
--define (-D) C preprocessor macros to define
--undef (-U) C preprocessor macros to undefine
--swig-opts list of SWIG command line options
[...]

注意,命令行中的選項”--foo-bar”,在配置文件中要寫成”foo_bar”。比如,運行以下命令:

python setup.py build_ext --inplace 

如果不希望每次執行命令時都輸入”--inplace”選項,則可以在配置文件中寫明:
 

[build_ext]
inplace=1 

其他例子和注意事項,可以參閱https://docs.python.org/2/distutils/configfile.html

四、源碼發布

之前已經提到過,使用sdist命令可以創建包的源碼發布,該命令最終生成一個archive文件。Unix上默認的文件格式是.tar.gz,在Windows上的是ZIP文件。可以使用”--formats”選項指定生成的格式,比如:python setup.py sdist --formats=gztar,zip,執行該命令後,就會生成兩個文件foo-1.0.tar.gz 和foo-1.0.zip。

支持的格式有:

Format

Description

zip

zip file (.zip)

gztar

gzip’ed tar file (.tar.gz)

bztar

bzip2’ed tar file (.tar.bz2)

ztar

compressed tar file (.tar.Z)

tar

tar file (.tar)

當在Unix上使用tar格式時(gztar,bztar,ztar或tar),可以通過owner和group選項指定用戶和群組。比如:

python setup.py sdist --owner=root --group=root 

4.1、指定發布的文件

如果沒有明確的列出需要發布的文件,則sdist命令默認在源碼發布中包含下列文件:

  1. 由py_modules和packages選項指定的所有python源碼文件;
  2. 由ext_modules或libraries選項指定的所有C源碼文件;
  3. 由scripts指定的腳本;
  4. 測試腳本:test/test*.py;
  5. README.txt (或者README), setup.py 和setup.cfg;
  6. package_data指定的所有文件;
  7. data_files指定的所有文件。

如果還需要發布其他額外的文件,典型的做法是編寫一個叫做MANIFEST.in的manifest模板。manifest模板包含如何創建MANIFEST文件的一系列指令,sdist命令會解析該模板,根據模板中的指令,以及找到的文件生成MANIFEST。文件MANIFEST中明確的列出了包含在源碼發布中的所有文件。比如下面就是一個MANIFEST文件的內容:

# file GENERATED by distutils, do NOT edit  

setup.py  

lib/__init__.py  

lib/foo.py  

lib/bar/__init__.py  

lib/bar/bar.py 

4.2、Manifest相關選項

sdist命令的執行步驟如下:

if the manifest file (MANIFEST by default) exists and the first line does not have a comment indicating it is generated from MANIFEST.in, then it is used as is, unaltered;

if the manifest file doesn’t exist or has been previously automatically generated, read MANIFEST.in and create the manifest;

if neither MANIFEST nor MANIFEST.in exist, create a manifest with just the default file set;

use the list of files now in MANIFEST (either just generated or read in) to create the source distribution archive(s).

如果僅僅需要(重新)創建MANIFEST文件,則可以使用如下命令:

python setup.py sdist --manifest-only 

4.3、MANIFEST.in模板

如果存在MANIFEST.in文件,則sdist命令就會根據該文件生成MANIFEST。在MANIFEST.in文件中,一行一個命令,每一個命令指定了源碼發布中需要包含或者需要排除的文件,比如下面的例子:

include *.txt
recursive-include examples *.txt *.py
prune examples/sample?/build 

很容易看出,上面的命令的意思是:包含所有的.txt文件;包含examples目錄下的所有.txt或者.py文件;排除所有匹配examples/sample?/build的目錄。所有這些過程,都是在標准規則執行之後執行的,所以可以在模板文件中排除標准集合中的文件。關於MANIFEST文件的其他內容,參閱https://docs.python.org/2/distutils/sourcedist.html

五、構建發布(Built Distributions)

所謂的構建發布(built distribution),即是指二進制包,或是指安裝文件。當然它未必真的是二進制,而有可能包含Python源碼和字節碼。構建發布是為了方便安裝者而創建的,比如對於基於RPM的Linux用戶來說,它可以是二進制RPM包,而對於Windows用戶來說,它可以是一個可執行的安裝文件等。創建包的構建發布,是前面介紹的packager的主要職責。它們拿到包的源碼發布之後,使用setup腳本以及bdist命令來生成構建發布。比如,在包的源碼樹中運行下面的命令:

python setup.py bdist 

Distutils就會創建發布,執行“偽”安裝(在build目錄中),並且創建當前平台下的默認格式的構建發布。構建發布在Unix中的默認格式是一個”dumb”的tar文件(之所以稱之為”dumb”,是因為該tar文件只有解壓到特定的目錄下才能工作),而在Windows上是一個簡單可執行安裝文件。所以,在Unix上運行上面的命令之後,就會在dist目錄中生成foo-1.0.linux-i686.tar.gz文件,在合適的位置解壓該文件,就安裝了foo模塊,等同於下載了該模塊的源碼發布之後運行python setup.py install命令。所謂合適的位置,要麼是文件系統的根目錄,要麼是Python的prefix目錄,這取決於bdist_dump的命令選項。bdist命令有一個--formats選項,類似於sdist命令,該選項可用於指定生成的構建發布的格式,比如命令:

python setup.py bdist --format=zip 

在Unix上運行該命令,就會創建foo-1.0.linux-i686.zip文件,在根目錄下解壓該文件就安裝了foo模塊。構建發布支持的格式如下:

gztar

gzipped tar file (.tar.gz)

ztar

compressed tar file (.tar.Z)

tar

tar file (.tar)

zip

zip file (.zip)

rpm

RPM

pkgtool

Solaris pkgtool

sdux

HP-UX swinstall

wininst

self-extracting ZIP file for Windows

msi

Microsoft Installer.

當然,也可以不使用--formats選項,而是用bdist的子命令,直接創建相應的格式。比如使用bdist_dump命令可以生成所有的dumb archive格式(tar,ztar,gztar和zip),bdist_rpm會生成源碼和二進制的RPM包,bdist的子命令如下表:

Command

Formats

bdist_dumb

tar, ztar, gztar, zip

bdist_rpm

rpm, srpm

bdist_wininst

wininst

bdist_msi

msi

具體的子命令信息,可以參閱5. Creating Built Distributions — Python 2.7.18 documentation

六、Distutils與PYPI

  1. PYPI,也就是Python Package Index,它是Python第三方模塊的集中營,Python開發者可以向PYPI上傳自己的Python模塊。PYPI中存放了發布文件以及發布的元數據。
  2. Distutils提供了register和upload命令,來直接向PYPI推送元數據和發布文件,詳細內容可以參閱https://docs.python.org/2/distutils/packageindex.html

七、簡單示例

7.1純Python發布(模塊)

如果只是發布幾個模塊,這些模塊沒有放在包中,可是使用py_modules選項。比如源碼樹如下:

setup.py  

foo.py  

bar.py  

setup腳本如下:

from distutils.core import setup
setup(name='foobar',
version='1.0',
py_modules=['foo', 'bar'],
) 

安裝之後,會生成以下文件:

\usr\lib\python2.7\site-packages\foobar-1.0-py2.7.egg-info

\usr\lib\python2.7\site-packages\ foo.py

\usr\lib\python2.7\site-packages\ foo.pyc

\usr\lib\python2.7\site-packages\ bar.py

\usr\lib\python2.7\site-packages\ bar.pyc

7.1、純Python發布(包)

如果有很多模塊需要發布,則可以將這些模塊放到統一的包中,然後在setup腳本中指明要發布的包,而不是列出所有的模塊。即使模塊沒有放到包中,也可以通過向setup腳本聲明root包的方法來發布,與實際的包不同,根目錄下可以沒有__init__.py文件。比如上面的例子,源碼樹保持不變,setup腳本也可以這樣寫:
 

from distutils.core import setup
setup(name='foobar',
version='1.0',
packages=[''],
) 

空字符串就意味著root包。安裝之後,生成的文件跟上面是一樣的。如果將源文件放到發布根目錄下的子目錄中,比如源碼樹:

setup.py  

src/        

        foo.py  

        bar.py 

這種情況依然可以用聲明root包的方式來發布,只不過需要使用package_dir選項來指明包和目錄的關系:
 

from distutils.core import setup
setup(name='foobar',
version='1.0',
package_dir={'': 'src'},
packages=[''],
) 

安裝之後生成的文件跟之前是一樣的。更常見的做法是將多個模塊組織在同一個包中,比如在包foobar中包含foo和bar模塊,源碼樹如下:

setup.py  

foobar/  

        __init__.py  

        foo.py  

        bar.py 

setup腳本如下:

from distutils.core import setup
setup(name='foobar',
version='1.0',
packages=['foobar'],
) 

安裝之後,會生成以下文件:

\usr\lib\python2.7\site-packages\foobar-1.0-py2.7.egg-info

\usr\lib\python2.7\site-packages\foobar\ __init__.py

\usr\lib\python2.7\site-packages\foobar\ __init__.pyc

\usr\lib\python2.7\site-packages\foobar\foo.py

\usr\lib\python2.7\site-packages\foobar\foo.pyc

\usr\lib\python2.7\site-packages\foobar\bar.py

\usr\lib\python2.7\site-packages\foobar\bar.pyc

如果不想以模塊所在的子目錄名來定義包名,則可以使用package_dir選項,比如源碼樹如下:

setup.py  

src/  

        __init__.py  

        foo.py  

        bar.py 

則相應的setup腳本如下:

from distutils.core import setup
setup(name='foobar',
version='1.0',
package_dir={'foobar': 'src'},
packages=['foobar'],
) 

安裝之後生成的文件與上面的例子是一樣的。或者,直接將所有模塊放到發布的根目錄下:

setup.py  

__init__.py  

foo.py  

bar.py 

setup腳本如下:

from distutils.core import setup
setup(name='foobar',
version='1.0',
package_dir={'foobar': ''},
packages=['foobar'],
) 

安裝之後生成的文件與上面的例子是一樣的。如果涉及到子包的話,則必須在packages選項中明確的指出。不過,package_dir中的值卻會自動擴展到其子目錄。比如源碼樹如下:

setup.py  

src/  

        __init__.py  

        foo.py  

        bar.py  

        subfoo/  

                __init__.py  

                blah.py 

setup腳本如下:

from distutils.core import setup
setup(name='foobar',
version='1.0',
package_dir = {'foobar':'src'},
packages=['foobar', 'foobar.subfoo'],
) 

 安裝之後,生成文件如下:

\usr\lib\python2.7\site-packages\foobar-1.0-py2.7.egg-info

\usr\lib\python2.7\site-packages\foobar\ __init__.py

\usr\lib\python2.7\site-packages\foobar\ __init__.pyc

\usr\lib\python2.7\site-packages\foobar\foo.py

\usr\lib\python2.7\site-packages\foobar\foo.pyc

\usr\lib\python2.7\site-packages\foobar\bar.py

\usr\lib\python2.7\site-packages\foobar\bar.pyc

\usr\lib\python2.7\site-packages\foobar\subfoo\__init__.py

\usr\lib\python2.7\site-packages\foobar\subfoo\__init__.pyc

\usr\lib\python2.7\site-packages\foobar\subfoo\blah.py

\usr\lib\python2.7\site-packages\foobar\subfoo\blah.pyc

7.3、單獨的擴展模塊

擴展模塊由選項ext_modules指定。package_dir選項對擴展模塊的源碼文件沒有作用,它只影響純Python模塊。比如源碼樹如下:

setup.py  

foo.c 

如果setup腳本如下:

from distutils.core import setup
from distutils.extension import Extension
setup(name='foobar',
version='1.0',
ext_modules=[Extension('foo', ['foo.c'])],
) 

則生成的文件是:

\usr\lib\python2.7\site-packages\foobar-1.0-py2.7.egg-info

\usr\lib\python2.7\site-packages\ foo.so

如果源碼樹不變,setup腳本如下:

from distutils.core import setup
from distutils.extension import Extension
setup(name='foobar',
version='1.0',
ext_modules=[Extension('foopkg.foo', ['foo.c'])],
) 

則生成的文件是:

\usr\lib\python2.7\site-packages\foobar-1.0-py2.7.egg-info

\usr\lib\python2.7\site-packages\foopkg\ foo.so

八、其他

運行install命令,會首先運行build命令,然後運行子命令install_lib,install_data和install_scripts。


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved