Python 包構建教程

setuptools 和 setup.py

Setuptoolsdistutils 都是用於編譯、分發和安裝 python 包的一個工具,特別是在包依賴問題場景下很是有用,它是一個強大的包管理工具。Setuptools 是 distutils 的增強版。編譯、分發和安裝 python 包的一個關鍵的事就是編寫 setup 腳本。setup 腳本的主要做用在於向包管理工具 Setuptoolsdistutils 說明你的模塊分發細節,因此 Setuptools 支持大量的命令以操做你的包。setup 腳本主要調用一個 setup() 方法,許多提供給 Setuptools 的信息都以 keyword arguments 的參數形式提供給 setup() 方法。html

你所須要作的事 & 一些概念

對於包開發者和使用者,所須要作的事:python

  • 編寫 setup.py 腳本,用於處理你的包
  • (可選)編寫 setup 配置文件
  • 建立源碼分發文件,python setup.py sdist
  • (可選)建立二進制分發文件,python setup.py bdist

對於包使用者,只須要 python setup.py install,即可以成功安裝 python 包。linux

基礎概念

  • module 模塊:module 是 python 中代碼重用的基本單元,一個 module 能夠經過 import 語句導入到另外一個 module;module 分爲:pure python module(純 python 模塊)、extension module(擴展模塊)和 package(包)
  • pure python module:純 python 模塊是用純 python 語言編寫的模塊,單一的 .py 文件做爲一個模塊使用,也就是一個 .py 能夠稱爲模塊了
  • extension module:擴展模塊是用底層的 C/C++、Objective-C或 Java 編寫的模塊,一般包含了一個動態連接庫,好比 so、dll 或 Java,目前 distutils 只支持 C/C++ 和 Objective-C,不支持 Java 編寫擴展模塊;可是 python 提供了一個 JCC 這樣一個用於生成訪問 Java 類接口的 C++ 代碼的膠水模塊,應該也是可使用 Java 編寫模塊的。
  • package:包是一個帶有 __init__.py 文件的文件夾,用於包含其餘模塊
  • root package:root package 是包的最頂層,它不是實質性的包,由於它不包含 __init__.py 文件。大量的標準庫位於 root package,由於它們不屬於一個任何更大的模塊集合了。實際上,每個 sys.path 列舉出來的文件夾都是 root package,你能夠在這些文件夾中找到大量的模塊。
  • distribution:模塊分發,一個歸檔在一塊兒的 python 模塊集合,它做爲一個可下載安裝的資源,方便用戶使用,做爲開發者便須要努力建立一個易於使用的 distribution
  • distribution root:源代碼樹的最頂層,也就是 setup.py 所在的位置。

關於源碼分發文件和二進制分發文件

源碼分發文件是將包分享給其餘人更爲推薦的一種形式,由於源碼分發文件比二進制分發文件更適合跨平臺,這樣使用者能夠在本身的機器上經過編譯獲得本身的機器相關的包代碼而且進行安裝。ios

示例和分發選擇

  • 若是你只是發佈幾個腳本文件而已,特別是它們邏輯上不屬於同一個包,你可使用 py_modules 選項一個一個地指定;
  • 若是你須要發佈的模塊文件太多,使用 py_modules 一個一個指定比較麻煩,特別是模塊位於多個包中,那麼你可使用 packages 指定整個包,另外只須要另外指定 package_dir,位於 distribution root 下的模塊文件也能夠被處理;
  • setuptools 幫助文檔聲明,package_dirpy_modules 也能夠支持分發任何沒有包含 __init__.py 文件夾下的模塊,經測試安裝過程沒有報錯,可是沒有包含 __init__.py 的文件夾下的模塊是沒有被正確安裝的!所以,若是 python 模塊分佈在不一樣的文件夾,最好是在該文件夾下建立一個 __init__.py 文件,以表示它是一個包。

pure python module

舉個簡單的例子,你須要發佈兩個模塊 foo 以及 bar.bar,以供別人使用(import foo 和 import bar.bar)。
其目錄樹以下:git

pure_module
├── bar
│   └── bar.py
│   └── __init__.py
├── foo.py
└── setup.py

如上圖所示,pure_module 目錄下包含了一個 foo 模塊以及一個 bar 包,同時在 bar 包下還包含了一個 bar 模塊。算法

一個僅使用 py_modules 的 setup 腳本能夠這樣寫:windows

from setuptools import setup

NAME = 'foo'
VERSION = '1.0'
PY_MODULES = ['foo', 'bar.bar']

setup(name = NAME
        , version = VERSION
        , py_modules = PY_MODULES)

py_modules 指定了 foo 模塊以及 bar.bar 模塊。app

經過 python setup.py install --user --prefix= 進行安裝後,即可以直接經過 import foo 和 import bar.bar 直接使用了。curl

  • 注意:經測試,若是 .py 文件位於其餘文件夾,該文件夾須要建立一個容許爲空的 __init__.py 文件,表示爲一個 package,不然安裝後不能正常使用其餘文件夾的模塊。

package

上一節的例子中,bar.bar 屬於 bar 包,foo 位於 distribution root,安裝後屬於 root package,在 Setuptools 中 "" 能夠用於表示 root package。因此下面展現兩種 setup 腳本的寫法:

pure_module
├── bar
│   └── bar.py
│   └── __init__.py
├── foo.py
└── setup.py

僅使用 packages 的 setup.py 文件以下:

from setuptools import setup

NAME = 'foo'
VERSION = '1.0'
PACKAGES = ['', 'bar']

setup(name = NAME
        , version = VERSION
        , packages = PACKAGES
        )

如上,packages 包含了 package 的列表 root package 以及 bar,這樣便能輕鬆覆蓋到 distribution root 下的 foo.py 和 bar 文件夾下的 bar.py 了,在存在大量模塊的狀況下,省去像 py_modules 同樣窮舉模塊的麻煩。在 python 中,默認的狀況下,包的名字和目錄的名字是一致的,好比 bar 包對應了 bar 目錄,且包的路徑表示是相對於 distribution root 的(也就是 setup.py 所在目錄)。好比 packages = ['foo'] ,Setuptools 會在 setup.py 所在目錄下尋找 foo/__init__.py,並將 foo/ 下的全部模塊包含進去。

另一個關鍵字是 package_dir,它的做用是將 package 映射到其餘目錄,這樣的一個好處是方便將 package 移到其餘目錄而不用修改 packages 的參數值。舉個例子,假設咱們如今須要把 bar 移到 foobar 目錄下,按照原來的腳本,Setuptools 是沒法成功找到 bar 包的。

package_dir
├── foobar
│   ├── bar.py
│   └── __init__.py
├── foo.py
└── setup.py

經過 package_dir = = {'bar':'foobar'},將原來的 bar package 映射到 foobar 下。完整的腳本以下:

from setuptools import setup

NAME = 'foo'
VERSION = '1.0'
PACKAGE_DIR = {'bar':'foobar'}
PACKAGES = ['', 'bar']

setup(name = NAME
        , version = VERSION
        , package_dir = PACKAGE_DIR
        , packages = PACKAGES
        )

package_dir 是一個字典,它的 key 是 package 名("" 表示 root package),value 是相對於 distribution root 的目錄名。在上面的例子中, package_dir = = {'bar':'foobar'} 改變了 packages 中 package 對應的目錄位置,這樣當 Setuptools 在找 bar package 時,會在 foobar 目錄下找相應的 __init__.py 文件。

  • 注意,package_dir 會影響 packages 下列出的全部 package,好比,packages =['bar', 'bar.lib'],package_dir 不只會影響全部和 bar 有關的 package,bar.lib 也會相應被映射到 foorbar.lib

extension module

擴展模塊須要使用 ext_modules 參數。上面所說的 package_dirpackages 都是針對純 python 模塊,而 ext_modules 針對的是使用 C/C++ 底層語言所寫的模塊。下面舉個最簡單的例子,擴展模塊僅包含一個 foo.cpp 文件,其中定義了可供 python 調用的 myPrint 函數。

#include <iostream>
#include <string>

using namespace std;

void myPrint(string text)
{
    cout <<  text << endl;
}
.
├── setup.py
└── src
    ├── foo.cpp
    ├── foo.h
    └── PythonWrapAPI.cpp

如今,咱們想要發佈擴展模塊供別人使用咱們的 myPrint 方法。想要 python 中成功導入你的包,須要利用額外的代碼封裝將被調用的方法。這裏的 PythonWrapAPI.cpp 的做用就是使用 Python 提供的庫封裝你所寫的接口,它是處在 python 和 C++ 間的膠水庫,當 python 調用你的 C++ 方法時,因爲語言類型的差異,須要作轉換。

PythonWrapAPI.cpp 以下:

#include "foo.h"
#include <string>
#include <python2.6/Python.h>

using namespace std;

/*
Notice:Python Interface Wrap
*/

static PyObject *_myPrint(PyObject *self, PyObject *args)
{
    char *text;

    // 解析 Python 傳過來的參數
    if (!PyArg_ParseTuple(args, "s", &text))
        return NULL;

    myPrint(text);
    return Py_None;
}

static PyMethodDef ExtestMethods[] =
{
    { "myPrint", _myPrint, METH_VARARGS },
    { NULL, NULL },
};

PyMODINIT_FUNC initmyprint(void) {
    (void)Py_InitModule("myprint", ExtestMethods);
}
  • wrapper 函數 _myPrint。它負責將 Python 的參數轉化爲C/C++的參數(PyArg_ParseTuple),而後調用實際的 myPrint,並處理 myPrint 的返回值,最終返回給Python環境。須要注意的是,C/C++ 中無返回值時,不能直接返回 NULL,而是須要返回 Py_None。
  • 導出表 ExtestMethods。它負責告訴Python這個模塊裏有哪些函數能夠被 Python 調用。導出表的名字能夠隨便起,每一項有4個參數:第一個參數是提供給 Python 環境的函數名稱,第二個參數是_myPrint,即 wrapper 函數。第三個參數的含義是參數變長,第四個參數是一個說明性的字符串。導出表老是以{NULL, NULL, 0, NULL}結束。說明,第 3 和 4 個參數能夠省略。
  • 導出函數 initmyprint。這個的名字不是任取的,是你的 module 名稱添加前綴init。導出函數中將模塊名稱與導出表進行鏈接。

擴展模塊和純 python 模塊有點不太同樣,咱們導入的 package 名字與 setup() 的 packages 或者 package_dir 參數是一致的,可是擴展模塊的名字是由 Extension 實例的 name 參數決定的,且須要和導出函數對應的initxxx 名字以及 Py_InitModule 方法對應的第一個參數相同。

最後,咱們須要編寫 setup 腳本編譯咱們的 cpp 文件爲 so 動態連接庫,並進行相應的封裝。運行 python setup.py install,setuptools 會幫咱們自動編譯。

from setuptools import setup, Extension, find_packages

# package name, import NAME
NAME = "foo"
VERSION = '1.0.0'
# an Extension instance list
EXT_MODULES = [ 
                Extension(
                    name = 'myprint' 
                    , sources=['src/foo.cpp','src/PythonWrapAPI.cpp']
                    , include_dirs = ['src']
                    )   
                ]   
setup(name = NAME
    , version = VERSION
    , ext_modules = EXT_MODULES
    , )

ext_modules 是一個 Extension 實例列表,Extension 的參數 sources 用於指定全部源文件位置,include_dirs 指定頭文件位置,同時還可使用 library_dirs 和 libraries 指定外部連接庫,以及 extra_compile_args 指定額外的編譯參數。

package 元信息參數

在編寫一個 package 的時候,儘可能提供更多的元信息,這樣使用者更加可以瞭解到 package 的相關信息,而且有些信息會被 PyPi 使用。

元信息 類型 描述 說明
name short string package 名字, 這裏用於 pypi 的顯示, 而不是用於 import, import 的包名與 packages 和 package_dir 參數一致 1
version short string package 的發佈版本, 建議:major.minor[.patch[.sub]] 1
author short string package 做者名字 3
author_email short string package 做者郵箱 3
maintainer short string package 維護者名字 3
maintainer_email short string package 維護者郵箱 3
url short string package 項目地址 1
description short string 簡介
long_description long string 顯示於 pypi 的介紹
download_url short string package 下載地址 2
classifiers strings list 分類符, 這樣便於 pypi 索引, 由 pypi 固定提供, https://pypi.python.org/pypi?%3Aaction=list_classifiers 2
platforms strings list 支持的平臺列表
license short string 受權協議
  1. 必填
  2. 若是爲了兼容 2.2.3 或 2.3 版本,不建議使用此字段
  3. 若是提供了 maintainer,那麼 distutils 會將其加入 PKG-INFO

package 內容參數

py_modules 列舉每一個模塊

py_modules 是一個字符串列表,用於指定全部的模塊,即 py 文件模塊。 若是你只是發佈幾個腳本文件而已,特別是它們邏輯上不屬於同一個 package。

好比,py_modules = ['mod1', 'pkg.mod2']。這指定了兩個模塊,一個位於 root package,而另外一個位於 pkg package。若是沒有使用 package_dir 從新映射 package 和目錄的關係的話,那麼這兩個模塊分別對應了 mod1.py 以及 pkg/mod2.py 文件,而且在 pkg 文件夾下還存在 __init__.py 文件。

package 列舉每一個包

若是你須要發佈的模塊文件太多,使用 py_modules 一個一個指定比較麻煩,特別是模塊位於多個包中,那麼你可使用 packages 指定整個包。

packages 是一個包名列表,packages 參數告訴 Setuptools 處理列舉出的 package 下全部純 python 模塊。在文件系統中,默認地,package 的名字與目錄是一一對應的,也就是說,packages = ['foo'],Setuptools 會去查找 foo/__init__.py 文件。

package_dir 從新映射 package 和目錄的關係

當你想要重命名你的 package 所在的文件夾,或者想要移動整個 package 到其餘目錄下,通常狀況下,一旦你的源代碼佈局改變,你須要從新修改 packages。可是 package_dir 能夠從新映射 package 和目錄的關係。好比你將 root package 下的模塊和 package 移到 lib 目錄下,那麼你只須要在 package_dir 中將 root package 映射到 lib 下。好比 package_dir = {'': 'lib'}

再舉個例子,好比一下目錄結果,當我使用 package_dir= {'bar':'foobar'} 和 packages= ['bar']時,Setuptools 根據 packages 參數查找 bar package 時,會在 foobar 文件夾下找相應的 __init__.py 文件。

package_dir
├── foobar
│   ├── bar.py
│   └── __init__.py
└── setup.py
  • 注意,package_dir 會影響 packages 下列出的全部 package,好比,packages =['bar', 'bar.lib'],package_dir 不只會影響全部和 bar 有關的 package,bar.lib 也會相應被映射到 foorbar.lib

install_requires 能夠聲明 package 安裝時所需的依賴模塊及其版本。安裝 package 時,Setuptools 便可以從 PyPi 上自動下載其所依賴的模塊,而且將依賴信息包含進 Python Eggs 中。

好比咱們在本身的 package 中用到了一個 python 非標準庫 pycurl 和 xmltodict,當咱們的 package 在別的機器上使用時便會報錯。爲了解決這個問題,咱們可使用 install_requires = ['pycurl', 'xmltodict'] 將 pycurl 和 xmltodict 加入 package 依賴。

install_requires 能夠是 string 或 string list,指明所須要依賴的模塊。當 install_requires 爲 string 類型,而且依賴多於 1 個時,每一個依賴的聲明都要另起一行。

最新版本的 Setuptools 的install_requires 有另外兩個做用:

  1. 在運行時,任何腳本都會檢查其依賴模塊的正確性,而且確保正確的依賴版本都加入到 sys.path 中(假若有多個版本的話)。
  2. Python Egg distributions 將會包含依賴相關的元信息。

前面說到,Setuptools 便可以從 PyPi 上自動下載其所依賴的模塊,可是在某些環境下沒法正常訪問 Pypi 下,咱們也能夠經過 dependency_links 參數 指定到本身的 python 源,這樣即可以解決下載問題。

好比,dependency_links = ['http://xxx/xmltodict', 'http://xxx/pycurl']

dependency_links 是一個字符串列表,包含了依賴的下載 URL。

Setuptools 對連接的支持比較強大!

下載的資源能夠知足如下條件:

  • 經過 python setup.py sdist 進行分發的壓縮文件,默認狀況下在 linux 爲 .tar.gz,在 windows 爲 zip
  • 單一的 py 文件
  • VCS 倉庫(Subversion, Mercurial, Git)

URL 連接能夠是:

  • 能夠直接下載的 URL
  • 包含資源下載連接的網頁 URL
  • 倉庫 URL

當包含資源下載連接的網頁 URL 中存在多個版本時,Setuptools 會根據版本要求下載合適的版本。

通常,比較好的方式是網頁 URL 方式。咱們也可使用 SourceForge 的 showfiles.php 連接來下載咱們所依賴的模塊。

若是依賴的模塊是一個 py 文件時,你必須在 URL 添加 "#egg=project-version" 後綴,以指出模塊的名字和版本,另外須要確保將模塊名和版本中出現的 - 替換爲 _。EasyInstall 將會識別這個後綴而且自動建立一個 setup.py 腳本,將咱們的 py 文件包裝爲 egg 文件。若是爲 VCS,將會 checkout 對應的版本,建立一個臨時文件夾並執行 setup.py bdist_egg,安裝所需的依賴。

在使用 VCS 的狀況下,你也可使用 #egg=project-version 指定要使用的版本。你能夠經過在 #egg=project-version 前加入 @REV 來指定要 checkout 的版本。另外你也能夠經過在 URL 前加上如下標識顯式聲明 URL 使用 的是 VCS:

  • Subversion:svn+URL
  • Git:git+URL
  • Mercurial:hg+URL

所以使用 VCS 更復雜的一個示例爲: vcs+proto://host/path@revision#egg=project-version

ext_module Python 調用 C/C++

Python 的可擴展性特別強,不只支持 python 語言的擴展模塊,並且支持其餘語言的擴展。

Python 調用 C++ 的詳細文檔能夠查看 https://docs.python.org/2/extending/building.html

這裏假設已經懂得怎麼調用 C++ 方法了,接下來只須要使用 ext_module 參數,使 Setuptools 可以編譯和安裝擴展模塊了。

ext_module 參數是一個 Extension 實例列表,Extension 相似於 gcc/g++ 的所需參數,包含了指定源文件、頭文件、依賴的靜態庫或動態庫、額外的編譯參數、宏定義等功能。

name 擴展模塊名字

name 是一個字符串,用於指定擴展模塊的名字。

packagespackage_dir 用於支持 python 語言編寫模塊,其 import 語句使用的包名與 packagespackage_dir 中所指定的名字是一致的。可是擴展模塊的名字是由 Extension 實例的 name 參數決定的,且須要和導出函數對應的initxxx 名字以及 Py_InitModule 方法對應的第一個參數相同。定義好模塊的名字 xxx 後,咱們即可以使用 import xxx 使用咱們本身的模塊了。

sources 和 include_dirs

sources 爲用於指定要編譯源文件的字符串列表,好比,sources=['foo/foo.cpp', 'bar/bar.cpp'],Setuptools 支持 C/C++ 以及 Objective-C。
include_dirs 爲用於指定編譯須要的頭文件目錄的字符串列表,好比,include_dirs=['foo/include', 'bar/include']。若是頭文件位於 distribution root 目錄,須要使用 '.' 表示頭文件位於當前目錄,不能爲 '',不然將找不到頭文件。

另外還支持 extra_objects 向連接程序傳遞 object 文件,好比 .o 文件。

define_macros 和 undef_macros

gcc 支持在編譯的時候定義新的宏變量和取消某個宏變量的定義,具體的選項 [-Dmacro[=defn]...] [-Umacro]。Extension 也支持這樣的選項。

你可使用 define_macrosundef_macros 定義新的宏變量和取消某個宏變量的定義。

define_macros 是一個 (name, value) 元組列表,其中的 name 爲宏變量名字符串,value 爲對應的值,能夠爲字符串、數字或爲 None 類型(說明:官方文檔沒有聲明 value 能夠爲數字,可是通過測試,只要是 python 支持的數字類型均可以用於 value,可是最好仍是使用字符串的形式,這樣腳本的兼容性會更好).

好比,define_macros=[('DEBUG', None), ('FOO', '1'), ('BAR', 2), ('FOOBAR', '"abc"')],gcc 對應的編譯選項結果爲 -DDEBUG -DFOO=1 -DBAR=2 -DFOOBAR="abc"

undef_macrosdefine_macros 簡單得多,它就是一個宏變量字符串列表,舉個例子,咱們想要取消以上定義的宏變量,對應的 undef_macros 值爲 undef_macros=['DEBUG', 'FOO', 'BAR', 'FOOBAR']

libraries 和 library_dirs

Setuptools 對 C/C++ 庫的引用方法和 gcc 同樣,具體的規則能夠參考 gcc。

libraries 爲要添加的庫的名字字符串列表,而 library_dirs 爲要添加的庫所在的目錄,舉個例子:

.
├── setup.py
└── curl
    ├── include
        ├── curl.h
        ├── test.h
    ├── lib
        ├── libcurl.a
        ├── libtest.a

其對應的參數爲 libraries=['curl', 'test']library_dirs=['curl/lib']include_dirs=[‘curl/include’]

注意:在實際的使用過程當中碰到過一個連接錯誤的坑,Setuptools 在編譯的時候報錯:

libcurl.a : relocation against .rodata can not be used when making a shared object:recompile with -fPIC
libcurl.a : could not read symbols:Bad value

前面提到,python 在建立擴展模塊時會將源文件編譯爲動態連接庫,動態連接庫在加載的時候,內存位置是不固定的,因此咱們連接的外部庫代碼也須要所有使用相對地址,這樣代碼即可以加載到內存的任意位置。由於有的庫沒有使用 -fPIC 選項進行編譯,致使庫最終在連接到 so 文件時報錯。

解決方案是使用 -fPIC 從新編譯 libcurl.a 庫。

extra_compile_args

在編譯擴展模塊時,Setuptools 會自動指定編譯參數,好比下面一個模塊的編譯:

gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -fPIC -DDEBUG -DFOO=1 -DBAR=2 -DFOOBAR="abc" -Isrc -I/usr/include/python2.6 -c src/foo.cpp -o build/temp.linux-x86_64-2.6/src/foo.o
gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -fPIC -DDEBUG -DFOO=1 -DBAR=2 -DFOOBAR="abc" -Isrc -I/usr/include/python2.6 -c src/PythonWrapAPI.cpp -o build/temp.linux-x86_64-2.6/src/PythonWrapAPI.o
g++ -pthread -shared build/temp.linux-x86_64-2.6/src/foo.o build/temp.linux-x86_64-2.6/src/PythonWrapAPI.o -L/usr/lib64 -lpython2.6 -o build/lib.linux-x86_64-2.6/myprint.so

這麼多的編譯參數絕大部分是 Setuptools 自動指定的,可是若是咱們還想要在每一個文件的編譯再加上額外的編譯選項,可使用 extra_compile_argsextra_link_args,其中 extra_link_args 選項用於連接。

extra_compile_args 是一個編譯選項字符串列表,每一個編譯選項都要單獨做爲一個字符串,不能並在一塊兒,不然會報錯。

建立源碼分發

建議使用源碼分發的形式發佈你的包,而不是二進制發佈形式,這樣包將更方便跨平臺。

sdist 命令

建立源碼分發的命令爲:python setup.py sdist,命令執行後會建立 dist 目錄,收集一些必要的文件以及 setup 腳本,生成一個壓縮文件,用戶安裝時,只須要解壓,而後執行 python setup.py install 命令,將進行編譯和安裝,將相應的文件存放到 python 第三方庫目錄下。

sdist 比較經常使用的一個選項是 --format,選擇壓縮的格式。好比,使用 zip 進行壓縮,python setup.py sdist --format=zip

格式 後綴
zip .zip
gztar .tar.gz
bztar .tar.bz2
ztar .tar.Z
tar .tar

說明:python setup.py sdist --format=zip, tar,Setuptools 會分別使用 zip 和 tar 進行壓縮,將同時產生兩個壓縮文件。

setuptools 和 distutils 對於文件查找的算法是同樣的:

  • 全部在 py_modulespackages 指定的對應模塊文件
  • 全部在 ext_moduleslibraries 選項指定的源文件和庫
  • scripts 選項指定的腳本文件
  • 全部相似測試腳本的文件,好比:test/test*.py (低版本的包管理工具可能不支持)
  • README.txt(或 README),setup.py 以及 setup.cfg(README 文件目前沒法支持更多的後綴格式)
  • package_data 選項指定的文件
  • data_files 選項指定的文件

另外在使用過程當中,遇到 Setuptools 的一個巨坑,確實能夠包含文件,可是它並不總能包含文件,這是有前提的。

bdist是發佈二進制文件,sdist是發佈源文件。而在舊版本的 python 中(2.7 之前), package_data只有在使用 bdist 時候纔有用,也就是若是使用 sdist,是沒法正確包含文件的。而在新版本中,會自動把package_data 裏面的內容添加到 MANIFEST 文件中。

MANIFEST.in 模版文件

當咱們使用 sdist 進行分發包時,若是須要包含額外的文件,可使用 MANIFEST.in 文件,在該文件中列舉出須要包含的文件。當咱們執行 sdist 時,將會對 MANIFEST.in 文件進行檢查,讀取解釋並生成 MANIFEST 文件,該文件列舉了全部須要包含進包的文件。位於 distribution root 下的 MANIFEST.in 文件每行對應一條包含一系列文件的命令。

相關文章
相關標籤/搜索