1. 程式人生 > >Python深入:Distutils釋出Python模組

Python深入:Distutils釋出Python模組

         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基本術語:

        模組(module):       Python中可複用的基本程式碼單元,可由其他程式碼import的一塊程式碼,這裡我們只關注三種類型的模組:純python模組,擴充套件模組和包。

純python模組(pure Python module):      由python編寫的模組,包含在單獨的py檔案中(或者是pyc/pyo檔案)。

擴充套件模組(extension module):由實現Python的底層語言編寫的模組(C/C++ for Python, Java for Jython)。通常包含在單獨的動態載入檔案中,比如Unix中的so檔案,windows中的DLL檔案,或者是Jython擴充套件的java類檔案。(注意,目前為止Distutils只能處理Python的C/C++擴充套件)

包(package):包是含其他模組的模組,經常由包含__init__.py檔案的目錄釋出。

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命令預設在原始碼釋出中包含下列檔案:

由py_modules和packages選項指定的所有python原始碼檔案;

由ext_modules或libraries選項指定的所有C原始碼檔案;

由scripts指定的指令碼;

測試指令碼:test/test*.py;

README.txt (或者README), setup.py 和setup.cfg;

package_data指定的所有檔案;

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

         具體的子命令資訊,可以參閱https://docs.python.org/2/distutils/builtdist.html

六:Distutils與PYPI

         PYPI,也就是Python Package Index,它是Python第三方模組的集中營,Python開發者可以向PYPI上傳自己的Python模組。PYPI中存放了釋出檔案以及釋出的元資料。

         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。

         Distutils可以進行擴充套件,比如增加新的命令、修改現有的命令。可參閱https://docs.python.org/2/distutils/extending.html

         Distutils的API參閱https://docs.python.org/2/distutils/apiref.html