程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Python >> Python包管理工具setuptools詳解

Python包管理工具setuptools詳解

編輯:Python

0.什麼是setuptools

setuptools是Python distutils增強版的集合,它可以幫助我們更簡單的創建和分發Python包,尤其是擁有依賴關系的。用戶在使用setuptools創建的包時,並不需要已安裝setuptools,只要一個啟動模塊即可。

功能亮點:

  • 利用EasyInstall自動查找、下載、安裝、升級依賴包
  • 創建Python Eggs
  • 包含包目錄內的數據文件
  • 自動包含包目錄內的所有的包,而不用在setup.py中列舉
  • 自動包含包內和發布有關的所有相關文件,而不用創建一個MANIFEST.in文件
  • 自動生成經過包裝的腳本或Windows執行文件
  • 支持Pyrex,即在可以setup.py中列出.pyx文件,而最終用戶無須安裝Pyrex
  • 支持上傳到PyPI
  • 可以部署開發模式,使項目在sys.path中
  • 用新命令或setup()參數擴展distutils,為多個項目發布/重用擴展
  • 在項目setup()中簡單聲明entry points,創建可以自動發現擴展的應用和框架

總之,setuptools就是比distutils好用的多,基本滿足大型項目的安裝和發布

1.安裝setuptools

1) 最簡單安裝,假定在ubuntu下

sudo apt-get install python-setuptools

2) 啟動腳本安裝

wget http://peak.telecommunity.com/dist/ez_setup.py
sudo python ez_setup.py

2.創建一個簡單的包

有了setuptools後,創建一個包基本上是無腦操作

cd /tmp 
mkdir demo
cd demo

在demo中創建一個setup.py文件,寫入

from setuptools import setup, find_packages
setup(
    name = "demo",
    version = "0.1",
    packages = find_packages(),
)

執行python setup.py bdist_egg即可打包一個test的包了。

demo
|-- build
|   `-- bdist.linux-x86_64
|-- demo.egg-info
|   |-- dependency_links.txt
|   |-- PKG-INFO
|   |-- SOURCES.txt
|   `-- top_level.txt
|-- dist
|   `-- demo-0.1-py2.7.egg
`-- setup.py

在dist中生成的是egg包

file dist/demo-0.1-py2.7.egg
dist/demo-0.1-py2.7.egg: Zip archive data, at least v2.0 to extract

看一下生成的.egg文件,是個zip包,解開看看先

upzip -l dist/demo-0.1-py2.7.egg

Archive:  dist/demo-0.1-py2.7.egg
  Length      Date    Time    Name
---------  ---------- -----   ----
        1  2013-06-07 22:03   EGG-INFO/dependency_links.txt
        1  2013-06-07 22:03   EGG-INFO/zip-safe
      120  2013-06-07 22:03   EGG-INFO/SOURCES.txt
        1  2013-06-07 22:03   EGG-INFO/top_level.txt
      176  2013-06-07 22:03   EGG-INFO/PKG-INFO
---------                     -------
      299                     5 files

我們可以看到,裡面是一系列自動生成的文件。現在可以介紹一下剛剛setup()中的參數了

  • name 包名
  • version 版本號
  • packages 所包含的其他包

要想發布到PyPI中,需要增加別的參數,這個可以參考官方文檔中的例子了。

3.給包增加內容

上面生成的egg中沒有實質的內容,顯然誰也用不了,現在我們稍微調色一下,增加一點內容。

在demo中執行mkdir demo,再創建一個目錄,在這個demo目錄中創建一個__init__.py的文件,表示這個目錄是一個包,然後寫入:

#!/usr/bin/env python
#-*- coding:utf-8 -*-

def test():
    print "hello world!"  

if __name__ == '__main__':
    test()

現在的主目錄結構為下:

demo
|-- demo
|   `-- __init__.py
`-- setup.py

再次執行python setup.py bdist_egg後,再看egg包

Archive:  dist/demo-0.1-py2.7.egg
  Length      Date    Time    Name
---------  ---------- -----   ----
        1  2013-06-07 22:23   EGG-INFO/dependency_links.txt
        1  2013-06-07 22:23   EGG-INFO/zip-safe
      137  2013-06-07 22:23   EGG-INFO/SOURCES.txt
        5  2013-06-07 22:23   EGG-INFO/top_level.txt
      176  2013-06-07 22:23   EGG-INFO/PKG-INFO
       95  2013-06-07 22:21   demo/__init__.py
      338  2013-06-07 22:23   demo/__init__.pyc
---------                     -------
      753                     7 files

這回包內多了demo目錄,顯然已經有了我們自己的東西了,安裝體驗一下。

python setup.py install

這個命令會講我們創建的egg安裝到python的dist-packages目錄下,我這裡的位置在

tree /usr/local/lib/python2.7/dist-packages/demo-0.1-py2.7.egg

查看一下它的結構:

/usr/local/lib/python2.7/dist-packages/demo-0.1-py2.7.egg
|-- demo
|   |-- __init__.py
|   `-- __init__.pyc
`-- EGG-INFO
    |-- dependency_links.txt
    |-- PKG-INFO
    |-- SOURCES.txt
    |-- top_level.txt
    `-- zip-safe

打開python終端或者ipython都行,直接導入我們的包

>>> import demo
>>> demo.test()
hello world!
>>>

好了,執行成功!

4.setuptools進階

在上例中,在前兩例中,我們基本都使用setup()的默認參數,這只能寫一些簡單的egg。一旦我們的project逐漸變大以後,維護起來就有點復雜了,下面是setup()的其他參數,我們可以學習一下

使用find_packages()

對於簡單工程來說,手動增加packages參數很容易,剛剛我們用到了這個函數,它默認在和setup.py同一目錄下搜索各個含有__init__.py的包。其實我們可以將包統一放在一個src目錄中,另外,這個包內可能還有aaa.txt文件和data數據文件夾。

demo
├── setup.py
└── src
    └── demo
        ├── __init__.py
        ├── aaa.txt
        └── data
            ├── abc.dat
            └── abcd.dat

如果不加控制,則setuptools只會將__init__.py加入到egg中,想要將這些文件都添加,需要修改setup.py

from setuptools import setup, find_packages
setup(
    packages = find_packages('src'),  # 包含所有src中的包
    package_dir = {'':'src'},   # 告訴distutils包都在src下

    package_data = {
        # 任何包中含有.txt文件,都包含它
        '': ['*.txt'],
        # 包含demo包data文件夾中的 *.dat文件
        'demo': ['data/*.dat'],
    }
)

這樣,在生成的egg中就包含了所需文件了。看看:

Archive:  dist/demo-0.0.1-py2.7.egg
  Length     Date   Time    Name
 --------    ----   ----    ----
       88  06-07-13 23:40   demo/__init__.py
      347  06-07-13 23:52   demo/__init__.pyc
        0  06-07-13 23:45   demo/aaa.txt
        0  06-07-13 23:46   demo/data/abc.dat
        0  06-07-13 23:46   demo/data/abcd.dat
        1  06-07-13 23:52   EGG-INFO/dependency_links.txt
      178  06-07-13 23:52   EGG-INFO/PKG-INFO
      157  06-07-13 23:52   EGG-INFO/SOURCES.txt
        5  06-07-13 23:52   EGG-INFO/top_level.txt
        1  06-07-13 23:52   EGG-INFO/zip-safe
 --------                   -------
      777                   10 files

另外,也可以排除一些特定的包,如果在src中再增加一個tests包,可以通過exclude來排除它,

find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"])

使用entry_points

一個字典,從entry point組名映射道一個表示entry point的字符串或字符串列表。Entry points是用來支持動態發現服務和插件的,也用來支持自動生成腳本。這個還是看例子比較好理解:

setup(
    entry_points = {
        'console_scripts': [
            'foo = demo:test',
            'bar = demo:test',
        ],
        'gui_scripts': [
            'baz = demo:test',
        ]
    }
)

修改setup.py增加以上內容以後,再次安裝這個egg,可以發現在安裝信息裡頭多了兩行代碼(Linux下):

Installing foo script to /usr/local/bin
Installing bar script to /usr/local/bin

查看/usr/local/bin/foo內容

#!/usr/bin/python
# EASY-INSTALL-ENTRY-SCRIPT: 'demo==0.1','console_scripts','foo'
__requires__ = 'demo==0.1'
import sys
from pkg_resources import load_entry_point

if __name__ == '__main__':
    sys.exit(
        load_entry_point('demo==0.1', 'console_scripts', 'foo')()
    )

這個內容其實顯示的意思是,foo將執行console_scripts中定義的foo所代表的函數。執行foo,發現打出了hello world!,和預期結果一樣。

使用Eggsecutable Scripts

從字面上來理解這個詞,Eggsecutable是Eggs和executable合成詞,翻譯過來就是另eggs可執行。也就是說定義好一個參數以後,可以另你生成的.egg文件可以被直接執行,貌似Java的.jar也有這機制?不很清楚,下面是使用方法:

setup(
    # other arguments here...
    entry_points = {
        'setuptools.installation': [
            'eggsecutable = demo:test',
        ]
    }
)

這麼寫意味著在執行python *.egg時,會執行我的test()函數,在文檔中說需要將.egg放到PATH路徑中。

包含數據文件

在3中我們已經列舉了如何包含數據文件,其實setuptools提供的不只這麼一種方法,下面是另外兩種

1)包含所有包內文件

這種方法中包內所有文件指的是受版本控制(CVS/SVN/GIT等)的文件,或者通過MANIFEST.in聲明的

from setuptools import setup, find_packages
setup(
    ...
    include_package_data = True
)

2)包含一部分,排除一部分

from setuptools import setup, find_packages
setup(
    ...
    packages = find_packages('src'),  
    package_dir = {'':'src'},   

    include_package_data = True,    

    # 排除所有 README.txt
    exclude_package_data = { '': ['README.txt'] },
)

如果沒有使用版本控制的話,可以還是使用3中提到的包含方法

可擴展的框架和應用

setuptools可以幫助你將應用變成插件模式,供別的應用使用。官網舉例是一個幫助博客更改輸出類型的插件,一個博客可能想要輸出不同類型的文章,但是總自己寫輸出格式化代碼太繁瑣,可以借助一個已經寫好的應用,在編寫博客程序的時候動態調用其中的代碼。

通過entry_points可以定義一系列接口,供別的應用或者自己調用,例如:

setup(
    entry_points = {'blogtool.parsers': '.rst = some_module:SomeClass'}
)

setup(
    entry_points = {'blogtool.parsers': ['.rst = some_module:a_func']}
)

setup(
    entry_points = """
        [blogtool.parsers]
        .rst = some.nested.module:SomeClass.some_classmethod [reST]
    """,
    extras_require = dict(reST = "Docutils>=0.3.5")
)

上面列舉了三中定義方式,即我們將我們some_module中的函數,以名字為blogtool.parsers的借口共享給別的應用。

別的應用使用的方法是通過pkg_resources.require()來導入這些模塊。

另外,一個名叫stevedore的庫將這個方式做了封裝,更加方便進行應用的擴展。

5. 以後增加

以上內容大部分來自於官方文檔,需要額外學習的以後再增加

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