日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > python >内容正文

python

Python模块开发【Distutils】

發(fā)布時(shí)間:2025/3/17 python 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python模块开发【Distutils】 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Distutils可以用來(lái)在Python環(huán)境中構(gòu)建和安裝額外的模塊。新的模塊可以是純Python的,也可以是用C/C++寫(xiě)的擴(kuò)展模塊,或者可以是Python包,包中包含了由C和Python編寫(xiě)的模塊。

?

一:Distutils簡(jiǎn)介

1.1概念和術(shù)語(yǔ)

???????? 對(duì)于模塊開(kāi)發(fā)者以及需要安裝模塊的使用者來(lái)說(shuō),Distutils的使用都很簡(jiǎn)單,作為一個(gè)開(kāi)發(fā)者,除了編寫(xiě)源碼之外,還需要:

編寫(xiě)setup腳本(一般是setup.py);

編寫(xiě)一個(gè)setup配置文件(可選);

創(chuàng)建一個(gè)源碼發(fā)布;

創(chuàng)建一個(gè)或多個(gè)構(gòu)建(二進(jìn)制)發(fā)布(可選);

?

???????? 有些模塊開(kāi)發(fā)者在開(kāi)發(fā)時(shí)不會(huì)考慮多個(gè)平臺(tái)發(fā)布,所以就有了packagers的角色,它們從模塊開(kāi)發(fā)者那取得源碼發(fā)布,然后在多個(gè)平臺(tái)上面進(jìn)行構(gòu)建,并發(fā)布多個(gè)平臺(tái)的構(gòu)建版本。

?

1.2簡(jiǎn)單例子

???????? 由python編寫(xiě)的setup腳本一般都非常簡(jiǎn)單。作為autoconf類(lèi)型的配置腳本,setup腳本可以在構(gòu)建和安裝模塊發(fā)布時(shí)運(yùn)行多次。

? ? ? ? ?比如,如果需要發(fā)布一個(gè)叫做foo的模塊,它包含在一個(gè)文件foo.py,那setup腳本可以這樣寫(xiě):

[python]?view plaincopy
  • from?distutils.core?import?setup??
  • setup(name='foo',??
  • ???????version='1.0',??
  • ???????py_modules=['foo'],??
  • ??????)??
  • ???????? setup函數(shù)的參數(shù)表示提供給Distutils的信息,這些參數(shù)分為兩類(lèi):包的元數(shù)據(jù)(包名、版本號(hào))以及包的信息(本例中是一個(gè)Python模塊的列表);模塊由模塊名表示,而不是文件名(對(duì)于包和擴(kuò)展而言也是這樣);建議可以提供更多的元數(shù)據(jù),比如你的名字,email地址和項(xiàng)目的URL地址。

    ?

    ???????? 編寫(xiě)好setup.py之后,就可以創(chuàng)建該模塊的源碼發(fā)布了:

    [plain]?view plaincopy
  • python?setup.py?sdist??
  • ? ? ? ? 對(duì)于Windows而言,命令是:?

    [plain]?view plaincopy
  • setup.py?sdist??
  • ? ? ? ? sdist命令會(huì)創(chuàng)建一個(gè)archive?文件(比如Unix上的tar文件,Windows上的zip文件),它包含setup.py, foo.py。該archive文件命名為foo-1.0.tar.gz(zip),解壓之后的目錄名是foo-1.0。

    ?

    ???????? 如果一個(gè)用戶(hù)希望安裝foo模塊,他只需要下載foo-1.0.tar.gz,解壓,進(jìn)入foo-1.0目錄,然后運(yùn)行:

    [plain]?view plaincopy
  • python?setup.py?install??
  • ???????? 該命令最終會(huì)將foo.py復(fù)制到Python環(huán)境存放第三方模塊的目錄中。在linux環(huán)境下,運(yùn)行該命令的輸出是:

    [plain]?view plaincopy
  • #?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

    ?

    ???????? 這個(gè)簡(jiǎn)單的例子展示了Distutils的基本概念。第一,開(kāi)發(fā)者和安裝者有同樣的用戶(hù)接口,也就是setup腳本,但他們使用的Distutils命令不同,sdist命令幾乎只有開(kāi)發(fā)者使用,而install對(duì)于安裝者更常用。

    ?

    ???????? 如果希望使用者的使用盡可能的簡(jiǎn)單,則可以創(chuàng)建多個(gè)構(gòu)建發(fā)布。比如,如果在Windows中,可以使用bdist_wininst命令創(chuàng)建一個(gè)exe安裝文件,下面的命令會(huì)在當(dāng)前目錄中創(chuàng)建foo-1.0.win32.exe文件:

    [plain]?view plaincopy
  • python?setup.py?bdist_wininst??

  • ???????? 其他的構(gòu)建發(fā)布有RPM(由bdist_rpm命令實(shí)現(xiàn)),Solaris pkgtool(bdist_pkgtool),以及HP-UX?swinstall?(bdist_sdux)。

    ? ? ? ? ?比如,下面的命令將會(huì)創(chuàng)建RPM文件foo-1.0.noarch.rpm(bdist_rpm命令必須運(yùn)行于基于RPM的系統(tǒng),比如Red Hat Linux, SuSE Linux, Mandrake Linux):

    [plain]?view plaincopy
  • python?setup.py?bdist_rpm??

  • ?????? 可以通過(guò)下面的命令得到當(dāng)前支持的發(fā)布格式:

    [plain]?view plaincopy
  • python?setup.py?bdist?--help-formats??

  • 1.3基本術(shù)語(yǔ):

    ? ? ? ? 模塊(module):?????? Python中可復(fù)用的基本代碼單元,可由其他代碼import的一塊代碼,這里我們只關(guān)注三種類(lèi)型的模塊:純python模塊,擴(kuò)展模塊和包。

    ? ? ? ??純python模塊(pure Python module):????? 由python編寫(xiě)的模塊,包含在單獨(dú)的py文件中(或者是pyc/pyo文件)。

    ? ? ? ??擴(kuò)展模塊(extension module):由實(shí)現(xiàn)Python的底層語(yǔ)言編寫(xiě)的模塊(C/C++ for Python, Java for Jython)。通常包含在單獨(dú)的動(dòng)態(tài)加載文件中,比如Unix中的so文件,windows中的DLL文件,或者是Jython擴(kuò)展的java類(lèi)文件。(注意,目前為止Distutils只能處理Python的C/C++擴(kuò)展)

    ? ? ? ??包(package):包是含其他模塊的模塊,經(jīng)常由包含__init__.py文件的目錄發(fā)布。

    ? ? ? ??Root包(root package):?????? 包層次關(guān)系中的根(它不是真正的包,因?yàn)樗话琠_init__.py文件)。

    ?

    1.4 Distutils術(shù)語(yǔ)

    ? ? ? ??模塊發(fā)布(module distribution):一些Python模塊的集合,它們將會(huì)被一起安裝。一些常見(jiàn)的模塊發(fā)布有Numeric Python,PyXML,PIL,mxBase。

    ? ? ? ??純模塊發(fā)布:一個(gè)只包含純python模塊和包的模塊發(fā)布。

    ? ? ? ??非純模塊發(fā)布:至少包含一個(gè)擴(kuò)展模塊的模塊發(fā)布。

    ? ? ? ??發(fā)布根:源碼樹(shù)的根目錄;setup.py所在的目錄。

    ?

    二:編寫(xiě)setup腳本

    ?????? setup腳本是使用Distutils構(gòu)建、發(fā)布和安裝模塊的核心。setup腳本的作用是向Distutils描述發(fā)布模塊的信息。從上面那個(gè)簡(jiǎn)單的例子中可知,setup腳本主要是調(diào)用setup函數(shù),而且模塊開(kāi)發(fā)者向Distutils提供的模塊信息多數(shù)是由setup函數(shù)的關(guān)鍵字參數(shù)提供的。

    ?????? 下面是一個(gè)更高級(jí)一些的例子:Distutils模塊本身的setup腳本:

    [python]?view plaincopy
  • setup(name='Distutils',??
  • ??????version='1.0',??
  • ??????description='Python?Distribution?Utilities',??
  • ??????author='Greg?Ward',??
  • ??????author_email='gward@python.net',??
  • ??????url='https://www.python.org/sigs/distutils-sig/',??
  • ??????packages=['distutils',?'distutils.command'],??
  • ?????)??

  • ?????? 上面這個(gè)腳本有更多的元數(shù)據(jù),列出的是兩個(gè)包(packages),而不是列出每個(gè)模塊。因?yàn)镈istutils包含多個(gè)模塊,這些模塊分成了兩個(gè)包;如果列出所有模塊的話則是冗長(zhǎng)且難以維護(hù)的。

    ?????? 注意,在setup腳本中的路徑必須以Unix形式來(lái)書(shū)寫(xiě),也就是由”/”分割的。Distutils會(huì)在使用這些路徑之前,將這種表示方法轉(zhuǎn)換為適合當(dāng)前平臺(tái)的格式。

    ??????

    2.1列出整個(gè)包

    ???????? Setup函數(shù)的packages參數(shù)是一個(gè)列表,其中包含了Distutils需要處理(構(gòu)建、發(fā)布、安裝等)的所有包。要實(shí)現(xiàn)此目的,那么包名和目錄名必須能夠相互對(duì)應(yīng),比如包名是distutils,則意味著在發(fā)布的根目錄(setup腳本所在目錄)下存在distutils子目錄;再比如在setup腳本中packages?=?['foo'],意味著要在setup腳本所在目錄下存在相應(yīng)的foo目錄和foo/__init__.py文件。

    ?????? 比如如果setup腳本內(nèi)容如下:

    [python]?view plaincopy
  • setup(name='foo',??
  • ???????version='1.0',??
  • ???????packages?=?['foo']??
  • ?????)??
  • ?????? 而setup腳本所在目錄并沒(méi)有foo目錄(只有一個(gè)setup.py腳本),則在執(zhí)行python setup.py bdist命令時(shí),會(huì)打印如下錯(cuò)誤:

    [plain]?view plaincopy
  • error:?package?directory?'foo'?does?not?exist??
  • ? ? ? ? 如果創(chuàng)建了foo目錄,但是沒(méi)有foo/__init__.py文件,則Distutils會(huì)產(chǎn)生下面的警告,但是仍會(huì)處理該包:

    [plain]?view plaincopy
  • package?init?file?'foo/__init__.py'?not?found?(or?not?a?regular?file)??

  • ??????

    ? ? ? ? 可以使用package_dir選項(xiàng)來(lái)改變這種默認(rèn)的對(duì)應(yīng)規(guī)則。package_dir是個(gè)字典,其中的key是要安裝的包名,如果為空,則表明是root package,value就是該包(key)對(duì)應(yīng)的源碼樹(shù)的目錄。

    ? ? ? ? 比如如果setup.py內(nèi)容如下:

    [python]?view plaincopy
  • setup(name='foo',??
  • ??????version='1.0',??
  • ??????package_dir?=?{'':'lib'},??
  • ??????packages?=?['foo']??
  • ?????)??

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

    [plain]?view plaincopy
  • 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

    ?

    ?????? 另外一個(gè)例子,foo包對(duì)應(yīng)lib目錄,所以,foo.bar包就對(duì)應(yīng)著lib/bar子目錄。所以如果在setup.py中這么寫(xiě):

    [python]?view plaincopy
  • package_dir?=?{'foo':'lib'},??
  • packages?=?['foo',’foo.bar’]??

  • ?????? 則必須存在lib/__init__.py, ?lib/bar/__init__.py文件。源碼樹(shù)如下:

    [plain]?view plaincopy
  • 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列出單獨(dú)的模塊

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

    [python]?view plaincopy
  • setup(name='foo',??
  • ??????version='1.0',??
  • ??????py_modules?=?['mod1',?'pkg.mod2']??
  • ?????)??

  • ???????? 它描述了兩個(gè)模塊,一個(gè)在root package中,另一個(gè)在pkg包中。根據(jù)默認(rèn)的包/目錄對(duì)應(yīng)規(guī)則,這兩個(gè)模塊存在于文件mod1.py和pkg/mod2.py中,并且要存在pkg/__init__.py文件(不存在的話,會(huì)產(chǎn)生報(bào)警:package init file 'pkg/__init__.py' not found (or not a regular file))。當(dāng)然,也可以使用package_dir選項(xiàng)改變這種對(duì)應(yīng)關(guān)系。所以,源碼樹(shù)如下:

    [plain]?view plaincopy
  • 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擴(kuò)展模塊

    ???????? 在Distutils中描述擴(kuò)展模塊較描述純python模塊要復(fù)雜一些。對(duì)于純python模塊,僅需要列出模塊或包,然后Distutils就會(huì)去尋找合適的文件,這對(duì)于擴(kuò)展模塊來(lái)說(shuō)是不夠的,你還需要指定擴(kuò)展名、源碼文件以及其他編譯/鏈接需要的參數(shù)(需要包含的目錄,需要連接的庫(kù)等等)

    ???????? 描述擴(kuò)展模塊可以由setup函數(shù)的關(guān)鍵字參數(shù)ext_modules實(shí)現(xiàn)。ext_modules是Extension實(shí)例的列表,每一個(gè)Extension實(shí)例描述了一個(gè)獨(dú)立的擴(kuò)展模塊。比如發(fā)布中包含一個(gè)獨(dú)立的擴(kuò)展模塊稱(chēng)為foo,由foo.c實(shí)現(xiàn),且無(wú)需其他編譯鏈接指令,那么下面的語(yǔ)句就可以描述該擴(kuò)展模塊:

    [python]?view plaincopy
  • Extension('foo',?['foo.c'])??
  • ???????? Extension可以從distutils.core中隨setup一起引入。因此,對(duì)于僅包含一個(gè)擴(kuò)展模塊的發(fā)布來(lái)說(shuō),setup腳本如下:

    [python]?view plaincopy
  • from?distutils.core?import?setup,?Extension??
  • setup(name='foo',??
  • ??????version='1.0',??
  • ??????ext_modules=[Extension('foo',?['foo.c'])],??
  • ??????)??
  • ? ? ? ? 底層的擴(kuò)展構(gòu)建機(jī)制是由build_ext命令實(shí)現(xiàn)的。Extension類(lèi)在描述Python擴(kuò)展時(shí)具有很大的靈活性。

    ?

    2.3.1 擴(kuò)展名和包

    ???????? 通常,Extension類(lèi)的構(gòu)造函數(shù)的第一個(gè)參數(shù)都是擴(kuò)展的名字,比如下面的語(yǔ)句:

    [python]?view plaincopy
  • Extension('foo',?['src/foo1.c',?'src/foo2.c'])??
  • ???????? 如果執(zhí)行python ?setup.py?bdist,就會(huì)調(diào)用相應(yīng)的編譯器和連接器命令,最終根據(jù)生成foo.so文件,存放在發(fā)布包的根目錄中,最終生成的文件是:

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

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

    ?

    ? ? ? ? 又比如下面的語(yǔ)句:

    [python]?view plaincopy
  • Extension('pkg.foo',?['src/foo1.c',?'src/foo2.c'])??
  • ? ? ? ? 使用的源文件是一樣的,最終生成的結(jié)果文件也是一樣的foo.so,唯一的不同是最終的結(jié)果文件存放的目錄,是在發(fā)布包的根目錄下的pkg目錄下。因此最終生成的文件是:

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

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

    ?

    ? ? ? ? 如果一個(gè)包下有多個(gè)擴(kuò)展,而且要把這些擴(kuò)展都放在統(tǒng)一的目錄下,則可以使用ext_package關(guān)鍵字,比如下面的語(yǔ)句:

    [python]?view plaincopy
  • setup(...,??
  • ??????ext_package='pkg',??
  • ??????ext_modules=[Extension('foo',?['src/foo.c']),??
  • ???????????????????Extension('subpkg.bar',?['src/bar.c'])]??
  • ?????)??

  • ???????? 上面的描述將會(huì)編譯src/foo.c為pkg.foo,將src/bar.c編譯為pkg.subpkg.bar。因此源碼樹(shù)如下:

    [plain]?view plaincopy
  • 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 擴(kuò)展的源碼文件

    ???????? Extension構(gòu)建函數(shù)的第二個(gè)參數(shù)是源文件的列表。目前Distutils僅支持C、C++和Objective-C擴(kuò)展,所以這些源碼文件就是C、C++和Objective-C的源碼文件。(C++源碼文件的擴(kuò)展名可以是.cc和.cpp,Unix和Windows編譯器都支持)

    ???????? 不過(guò)還可以在列表中包含SWIG接口文件(.i文件),build_ext命令知道如何處理SWIG接口文件。盡管會(huì)發(fā)生報(bào)警,但是可以像下面這樣傳遞SWIG選項(xiàng):

    [python]?view plaincopy
  • setup(...,??
  • ??????ext_modules=[Extension('_foo',?['foo.i'],??
  • ?????????????????????????????swig_opts=['-modern',?'-I../include'])],??
  • ??????py_modules=['foo'],??
  • ?????)??
  • ???????? 或者是使用如下命令:

    [plain]?view plaincopy
  • >?python?setup.py?build_ext?--swig-opts="-modern?-I../include"??

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

    ????????

    2.3.3其他選項(xiàng)

    ???????? Extension還可以指定其他選項(xiàng),比如可以指定頭文件目錄,define或undefine宏、需要鏈接的庫(kù),鏈接時(shí)和運(yùn)行時(shí)搜索庫(kù)的路徑等等。具體可參閱:

    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發(fā)布和包的關(guān)系

    ???????? 發(fā)布和包有三種關(guān)系:它依賴(lài)其他包,它服務(wù)于其他包,它淘汰其他包。這些關(guān)系可以分別用setup函數(shù)的參數(shù)requires?,provides?和obsoletes?來(lái)指定,具體參閱:https://docs.python.org/2/distutils/setupscript.html#relationships-between-distributions-and-packages

    ?

    2.5安裝腳本

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

    ?

    2.6安裝package data

    ???????? 有時(shí)包中還需要安裝其他文件,這些文件與包的實(shí)現(xiàn)密切相關(guān),或者是包含文檔信息的文本文件等,這些文件就叫做package data。

    ???????? 使用setup函數(shù)中的package_data參數(shù)可以向packages中添加package data。該參數(shù)的值必須是個(gè)字典,字典的key就是package name,value是個(gè)list,其中包含了需要復(fù)制到package中的一系列路徑。這些路徑都是相對(duì)于包目錄而言的(比如package_dir),所以,這些文件必須存在于包的源碼目錄中。在安裝時(shí),也會(huì)創(chuàng)建相應(yīng)的目錄。

    ???????? 比如,如果包中有一個(gè)包含數(shù)據(jù)文件的子目錄,源碼樹(shù)如下:

    [plain]?view plaincopy
  • setup.py??
  • src/??
  • ????mypkg/??
  • ????????__init__.py??
  • ????????module.py??
  • ????????data/??
  • ????????????tables.dat??
  • ????????????spoons.dat??
  • ????????????forks.dat??
  • ???????? 相應(yīng)的setup函數(shù)可以這樣寫(xiě):

    [python]?view plaincopy
  • setup(...,??
  • ??????packages=['mypkg'],??
  • ??????package_dir={'mypkg':?'src/mypkg'},??
  • ??????package_data={'mypkg':?['data/*.dat']},??
  • ??????)??

  • 2.7安裝其他文件

    ???????? 可以通過(guò)data_files選項(xiàng)來(lái)安裝除了上面提到過(guò)的文件之外的其他文件,比如配置文件,數(shù)據(jù)文件等。data_files是個(gè)列表,列表中的元素是(directory,?files),比如:

    [python]?view plaincopy
  • setup(...,??
  • ??????data_files=[('bitmaps',?['bm/b1.gif',?'bm/b2.gif']),??
  • ??????????????????('config',?['cfg/data.cfg']),??
  • ??????????????????('/etc/init.d',?['init-script'])]??
  • ?????)??
  • ???????? (directory,?files)中,directory表示文件最終要被安裝到的地方,如果它是相對(duì)路徑的話,則是相對(duì)于installation prefix而言(對(duì)于純python包而言,就是sys.prefix;對(duì)于擴(kuò)展包,則是sys.exec_prefix)。files是要安裝的文件,其中的目錄信息(安裝前)是相對(duì)于setup.py所在目錄而言的,安裝時(shí),setup.py根據(jù)files的信息找到該文件,然后將其安裝到directory中。

    ??????

    2.8元數(shù)據(jù)

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

    ?

    2.9調(diào)試setup腳本

    ???????? 如果在運(yùn)行setup腳本是發(fā)生了錯(cuò)誤,則Distutils會(huì)打印出簡(jiǎn)單的錯(cuò)誤信息,對(duì)于開(kāi)發(fā)者而言這些錯(cuò)誤信息可能不足以找到錯(cuò)誤的原因。所以可以通過(guò)設(shè)置環(huán)境變量DISTUTILS_DEBUG,將其置為任意值(不能是空字符串),Distutils就會(huì)打印其執(zhí)行過(guò)程的詳細(xì)信息,并且在發(fā)生異常時(shí)打印全部的traceback,并且在像C編譯器這樣的外部程序發(fā)生錯(cuò)誤時(shí),打印整個(gè)命令行。

    ?

    三:配置文件

    ???????? 一般情況下,在構(gòu)建發(fā)布時(shí)無(wú)法將所有的選項(xiàng)都確定下來(lái),有些選項(xiàng)的值可能來(lái)自于用戶(hù),或者用戶(hù)的系統(tǒng)。這也就是配置文件setup.cfg存在的目的,用戶(hù)可以通過(guò)修改該配置文件進(jìn)行選項(xiàng)的配置。

    ???????? 在構(gòu)建時(shí),選項(xiàng)的處理順序是setup腳本、配置文件,命令行。所以,安裝者可以通過(guò)修改setup.cfg文件來(lái)覆蓋setup.py中的選項(xiàng);也可以通過(guò)運(yùn)行setup.py時(shí)的命令行選項(xiàng),來(lái)覆蓋setup.cfg。

    ?

    ???????? 配置文件的基本語(yǔ)法如下:

    [plain]?view plaincopy
  • [command]??
  • option=value??
  • ...??
  • ???????? command就是Distutils的命令(比如build_py,install等),option就是命令支持的選項(xiàng)。配置文件中的空行、注釋(以’#’開(kāi)頭,直到行尾)會(huì)被忽略。

    ?

    ???????? 可以通過(guò)--help選項(xiàng)得到某個(gè)命令支持的選項(xiàng),比如:

    [plain]?view plaincopy
  • >?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??
  • [...]??
  • ? ? ? ? 注意,命令行中的選項(xiàng)”--foo-bar”,在配置文件中要寫(xiě)成”foo_bar”。

    ? ? ? ??

    ? ? ? ? 比如,運(yùn)行以下命令:

    [plain]?view plaincopy
  • python?setup.py?build_ext?--inplace??
  • ? ? ? ? 如果不希望每次執(zhí)行命令時(shí)都輸入”--inplace”選項(xiàng),則可以在配置文件中寫(xiě)明:

    [plain]?view plaincopy
  • [build_ext]??
  • inplace=1??
  • ? ? ? ? 其他例子和注意事項(xiàng),可以參閱https://docs.python.org/2/distutils/configfile.html

    ?

    四:源碼發(fā)布

    ? ? ? ? 之前已經(jīng)提到過(guò),使用sdist命令可以創(chuàng)建包的源碼發(fā)布,該命令最終生成一個(gè)archive文件。Unix上默認(rèn)的文件格式是.tar.gz,在Windows上的是ZIP文件。可以使用”--formats”選項(xiàng)指定生成的格式,比如:python setup.py sdist --formats=gztar,zip,執(zhí)行該命令后,就會(huì)生成兩個(gè)文件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)

    ? ? ? ? 當(dāng)在Unix上使用tar格式時(shí)(gztar,bztar,ztar或tar),可以通過(guò)owner和group選項(xiàng)指定用戶(hù)和群組。比如:

    [plain]?view plaincopy
  • python?setup.py?sdist?--owner=root?--group=root??

  • 4.1指定發(fā)布的文件

    ???????? 如果沒(méi)有明確的列出需要發(fā)布的文件,則sdist命令默認(rèn)在源碼發(fā)布中包含下列文件:

    由py_modules和packages選項(xiàng)指定的所有python源碼文件;

    由ext_modules或libraries選項(xiàng)指定的所有C源碼文件;

    由scripts指定的腳本;

    測(cè)試腳本:test/test*.py;

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

    package_data指定的所有文件;

    data_files指定的所有文件。

    ?

    ? ? ? ? 如果還需要發(fā)布其他額外的文件,典型的做法是編寫(xiě)一個(gè)叫做MANIFEST.in的manifest模板。manifest模板包含如何創(chuàng)建MANIFEST文件的一系列指令,sdist命令會(huì)解析該模板,根據(jù)模板中的指令,以及找到的文件生成MANIFEST。

    ? ? ? ? 文件MANIFEST中明確的列出了包含在源碼發(fā)布中的所有文件。比如下面就是一個(gè)MANIFEST文件的內(nèi)容:

    [plain]?view plaincopy
  • #?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相關(guān)選項(xiàng)

    ? ? ? ? sdist命令的執(zhí)行步驟如下:

    ? ? ? ? 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).

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

    [plain]?view plaincopy
  • python?setup.py?sdist?--manifest-only??

  • 4.3 MANIFEST.in模板

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

    [plain]?view plaincopy
  • include?*.txt??
  • recursive-include?examples?*.txt?*.py??
  • prune?examples/sample?/build??
  • ???????? 很容易看出,上面的命令的意思是:包含所有的.txt文件;包含examples目錄下的所有.txt或者.py文件;排除所有匹配examples/sample?/build的目錄。所有這些過(guò)程,都是在標(biāo)準(zhǔn)規(guī)則執(zhí)行之后執(zhí)行的,所以可以在模板文件中排除標(biāo)準(zhǔn)集合中的文件。關(guān)于MANIFEST文件的其他內(nèi)容,參閱https://docs.python.org/2/distutils/sourcedist.html

    ?

    五:構(gòu)建發(fā)布(Built Distributions

    ???????? 所謂的構(gòu)建發(fā)布(built distribution),即是指二進(jìn)制包,或是指安裝文件。當(dāng)然它未必真的是二進(jìn)制,而有可能包含Python源碼和字節(jié)碼。

    ???????? 構(gòu)建發(fā)布是為了方便安裝者而創(chuàng)建的,比如對(duì)于基于RPM的Linux用戶(hù)來(lái)說(shuō),它可以是二進(jìn)制RPM包,而對(duì)于Windows用戶(hù)來(lái)說(shuō),它可以是一個(gè)可執(zhí)行的安裝文件等。

    ???????? 創(chuàng)建包的構(gòu)建發(fā)布,是前面介紹的packager的主要職責(zé)。它們拿到包的源碼發(fā)布之后,使用setup腳本以及bdist命令來(lái)生成構(gòu)建發(fā)布。比如,在包的源碼樹(shù)中運(yùn)行下面的命令:

    [plain]?view plaincopy
  • python?setup.py?bdist??
  • ? ? ? ? ?Distutils就會(huì)創(chuàng)建發(fā)布,執(zhí)行“偽”安裝(在build目錄中),并且創(chuàng)建當(dāng)前平臺(tái)下的默認(rèn)格式的構(gòu)建發(fā)布。構(gòu)建發(fā)布在Unix中的默認(rèn)格式是一個(gè)”dumb”的tar文件(之所以稱(chēng)之為”dumb”,是因?yàn)樵搕ar文件只有解壓到特定的目錄下才能工作),而在Windows上是一個(gè)簡(jiǎn)單可執(zhí)行安裝文件。

    ???????? 所以,在Unix上運(yùn)行上面的命令之后,就會(huì)在dist目錄中生成foo-1.0.linux-i686.tar.gz文件,在合適的位置解壓該文件,就安裝了foo模塊,等同于下載了該模塊的源碼發(fā)布之后運(yùn)行python setup.py install命令。所謂合適的位置,要么是文件系統(tǒng)的根目錄,要么是Python的prefix目錄,這取決于bdist_dump的命令選項(xiàng)。

    ???????? bdist命令有一個(gè)--formats選項(xiàng),類(lèi)似于sdist命令,該選項(xiàng)可用于指定生成的構(gòu)建發(fā)布的格式,比如命令:

    [plain]?view plaincopy
  • python?setup.py?bdist?--format=zip??

  • ???????? 在Unix上運(yùn)行該命令,就會(huì)創(chuàng)建foo-1.0.linux-i686.zip文件,在根目錄下解壓該文件就安裝了foo模塊。構(gòu)建發(fā)布支持的格式如下:

    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.

    ???????? 當(dāng)然,也可以不使用--formats選項(xiàng),而是用bdist的子命令,直接創(chuàng)建相應(yīng)的格式。比如使用bdist_dump命令可以生成所有的dumb archive格式(tar,ztar,gztar和zip),bdist_rpm會(huì)生成源碼和二進(jìn)制的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第三方模塊的集中營(yíng),Python開(kāi)發(fā)者可以向PYPI上傳自己的Python模塊。PYPI中存放了發(fā)布文件以及發(fā)布的元數(shù)據(jù)。

    ???????? Distutils提供了register和upload命令,來(lái)直接向PYPI推送元數(shù)據(jù)和發(fā)布文件,詳細(xì)內(nèi)容可以參閱https://docs.python.org/2/distutils/packageindex.html

    ?

    七:簡(jiǎn)單示例

    7.1純Python發(fā)布(模塊)

    ???????? 如果只是發(fā)布幾個(gè)模塊,這些模塊沒(méi)有放在包中,可是使用py_modules選項(xiàng)。比如源碼樹(shù)如下:

    [plain]?view plaincopy
  • setup.py??
  • foo.py??
  • bar.py??
  • ???????? setup腳本如下:

    [python]?view plaincopy
  • from?distutils.core?import?setup??
  • setup(name='foobar',??
  • ??????version='1.0',??
  • ??????py_modules=['foo',?'bar'],??
  • ??????)??
  • ???????? 安裝之后,會(huì)生成以下文件:

    \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發(fā)布(包)

    ???????? 如果有很多模塊需要發(fā)布,則可以將這些模塊放到統(tǒng)一的包中,然后在setup腳本中指明要發(fā)布的包,而不是列出所有的模塊。

    ???????? 即使模塊沒(méi)有放到包中,也可以通過(guò)向setup腳本聲明root包的方法來(lái)發(fā)布,與實(shí)際的包不同,根目錄下可以沒(méi)有__init__.py文件。比如上面的例子,源碼樹(shù)保持不變,setup腳本也可以這樣寫(xiě):

    [python]?view plaincopy
  • from?distutils.core?import?setup??
  • setup(name='foobar',??
  • ??????version='1.0',??
  • ??????packages=[''],??
  • ??????)??
  • ???????? 空字符串就意味著root包。安裝之后,生成的文件跟上面是一樣的。

    ?

    ???????? 如果將源文件放到發(fā)布根目錄下的子目錄中,比如源碼樹(shù):

    [plain]?view plaincopy
  • setup.py??
  • src/????????
  • ????????foo.py??
  • ????????bar.py??
  • ???????? 這種情況依然可以用聲明root包的方式來(lái)發(fā)布,只不過(guò)需要使用package_dir選項(xiàng)來(lái)指明包和目錄的關(guān)系:

    [python]?view plaincopy
  • from?distutils.core?import?setup??
  • setup(name='foobar',??
  • ??????version='1.0',??
  • ??????package_dir={'':?'src'},??
  • ??????packages=[''],??
  • ??????)??
  • ???????? 安裝之后生成的文件跟之前是一樣的。

    ?

    ???????? 更常見(jiàn)的做法是將多個(gè)模塊組織在同一個(gè)包中,比如在包foobar中包含foo和bar模塊,源碼樹(shù)如下:

    [plain]?view plaincopy
  • setup.py??
  • foobar/??
  • ????????__init__.py??
  • ????????foo.py??
  • ????????bar.py??
  • ???????? setup腳本如下:

    [python]?view plaincopy
  • from?distutils.core?import?setup??
  • setup(name='foobar',??
  • ??????version='1.0',??
  • ??????packages=['foobar'],??
  • ??????)??
  • ???????? 安裝之后,會(huì)生成以下文件:

    \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

    ?

    ???????? 如果不想以模塊所在的子目錄名來(lái)定義包名,則可以使用package_dir選項(xiàng),比如源碼樹(shù)如下:

    [plain]?view plaincopy
  • setup.py??
  • src/??
  • ????????__init__.py??
  • ????????foo.py??
  • ????????bar.py??
  • ???????? 則相應(yīng)的setup腳本如下:

    [python]?view plaincopy
  • from?distutils.core?import?setup??
  • setup(name='foobar',??
  • ??????version='1.0',??
  • ??????package_dir={'foobar':?'src'},??
  • ??????packages=['foobar'],??
  • ??????)??
  • ???????? 安裝之后生成的文件與上面的例子是一樣的。

    ?

    ???????? 或者,直接將所有模塊放到發(fā)布的根目錄下:

    [plain]?view plaincopy
  • setup.py??
  • __init__.py??
  • foo.py??
  • bar.py??
  • ???????? setup腳本如下:

    [python]?view plaincopy
  • from?distutils.core?import?setup??
  • setup(name='foobar',??
  • ??????version='1.0',??
  • ??????package_dir={'foobar':?''},??
  • ??????packages=['foobar'],??
  • ??????)??
  • ? ? ? ? 安裝之后生成的文件與上面的例子是一樣的。

    ?

    ???????? 如果涉及到子包的話,則必須在packages選項(xiàng)中明確的指出。不過(guò),package_dir中的值卻會(huì)自動(dòng)擴(kuò)展到其子目錄。比如源碼樹(shù)如下:

    [plain]?view plaincopy
  • setup.py??
  • src/??
  • ????????__init__.py??
  • ????????foo.py??
  • ????????bar.py??
  • ????????subfoo/??
  • ????????????????__init__.py??
  • ????????????????blah.py??
  • ???????? setup腳本如下:

    [python]?view plaincopy
  • 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單獨(dú)的擴(kuò)展模塊

    ???????? 擴(kuò)展模塊由選項(xiàng)ext_modules指定。package_dir選項(xiàng)對(duì)擴(kuò)展模塊的源碼文件沒(méi)有作用,它只影響純Python模塊。比如源碼樹(shù)如下:

    [plain]?view plaincopy
  • setup.py??
  • foo.c??
  • ???????? 如果setup腳本如下:

    [python]?view plaincopy
  • 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

    ?

    ???????? 如果源碼樹(shù)不變,setup腳本如下:

    [python]?view plaincopy
  • 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

    ?

    八:其他

    ???????? 運(yùn)行install命令,會(huì)首先運(yùn)行build命令,然后運(yùn)行子命令install_lib,install_data和install_scripts。

    ?

    ???????? Distutils可以進(jìn)行擴(kuò)展,比如增加新的命令、修改現(xiàn)有的命令。可參閱https://docs.python.org/2/distutils/extending.html

    ?

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

    總結(jié)

    以上是生活随笔為你收集整理的Python模块开发【Distutils】的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。