一文读懂:从 Python 打包到 CLI 工具
最近項目組在寫項目的 CLI 工具,已經接近尾聲,想做成 pip 的安裝包,所以才有了這篇文章。
1,文章介紹了如何生成 Python Egg ,上傳 PyPI 及其 pip 的安裝測試
2,在后面的進階部分會介紹簡單的生成cli工具的方法
話說既然研究了如何做包,不想浪費這次機會,干脆水一個博客吧
整理項目
先創建一個項目的文件夾
$ mkdir eds # eds 是我項目的名稱,你隨意修改成自己的即可 $ cd eds在里面在創建一個 edssdk 的文件夾,這個文件夾的名稱我故意創建的和上層目錄不一樣,以免誤會,這個文件夾其實就是包名稱了
$ mkdir edssdk # 這個文件夾就是包名稱 $ cd edssdk這個時候就是寫代碼的時候了,如果項目邏輯簡單,你可以選擇在文件夾里面只創建一個 init.py 文件,將所有的函數寫到此文件里
當然如果項目復雜,你可以多創建幾個文件,這里我繁中取簡,只創建一個其他文件。
$ touch __init__.py # 這個文件作用就是給這個文件夾打成包 $ touch help.py # 這里是你的邏輯代碼了,我就簡單寫了我寫了兩個函數在 help.py 中
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:857662006 尋找有志同道合的小伙伴,互幫互助, 群里還有不錯的視頻學習教程和PDF電子書! ''' $ cat help.py #!/usr/bin/env python #-*- coding:utf-8 -*-def sum(*values):s = 0for v in values:i = int(v)s = s + iprint s def output():print 'http://xiaoh.me'制作PyPI包
現在項目邏輯已經完成,那么開始做 PyPI 的包了。
在 eds 文件夾中,創建 Egg 的配置文件 setup.py,并填寫配置
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:857662006 尋找有志同道合的小伙伴,互幫互助, 群里還有不錯的視頻學習教程和PDF電子書! ''' $ cat setup.py #!/usr/bin/env python #-*- coding:utf-8 -*-from setuptools import setup, find_packages setup(name = "edssdk",version = "0.0.1",keywords = ("pip", "datacanvas", "eds", "xiaoh"),description = "eds sdk",long_description = "eds sdk for python",license = "MIT Licence",url = "http://xiaoh.me",author = "xiaoh",author_email = "huoxingming@gmail.com",packages = find_packages(),include_package_data = True,platforms = "any",install_requires = [] )當你的包很復雜的時候,勢必會引用其他的包,你需要將你所有引用的包名稱寫到 install_requires 這個里面:
install_requires = ["requests"]當然 setup 還有很多可以選擇的項可以填,你可以 python setup.py –help 查看,也可以去看 文檔
OK,看一下現在目錄的結構:
$ tree $ eds $ ├── edssdk $ │ ├── help.py $ │ └── __init__.py $ └── setup.py這里面還需要說一下,setup 文件支持用配置文件來編寫里面的參數
$ cat setup.cfg [metadata] name = edssdk version = 0.1.1 zip_safe = False description = eds sdk author = xiaoh author_email = huoxingming@gmail.com license = MIT Licence platforms = any [files] packages = find_packages()打包
打包這一步我認為比較簡單,目前比較流行的2中打包的方式:
$ python setup.py bdist_egg # 生成類似 edssdk-0.0.1-py2.7.egg,支持 easy_install $ python setup.py sdist # 生成類似 edssdk-0.0.1.tar.gz,支持 pip上面兩條命令都會將文件生成到 dist 目錄中
當然還有其他非主流格式或者其他選項,可以通過下面這個命令查看:
$ python setup.py --help-commands注冊PyPI包
我是直接在 SSH 下面進行操作的,你也可以通過 網頁 來做,SSH 步驟:
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:857662006 尋找有志同道合的小伙伴,互幫互助, 群里還有不錯的視頻學習教程和PDF電子書! ''' $ python setup.py register running register running egg_info writing dependency_links to eds_sdk.egg-info/dependency_links.txt writing eds_sdk.egg-info/PKG-INFO writing top-level names to eds_sdk.egg-info/top_level.txt reading manifest file 'eds_sdk.egg-info/SOURCES.txt' writing manifest file 'eds_sdk.egg-info/SOURCES.txt' running check We need to know who you are, so please choose either:1. use your existing login,2. register as a new user,3. have the server generate a new password for you (and email it to you), or4. quit Your selection [default 1]: Username: xingming Password: Registering edssdk to https://pypi.python.org/pypi Server response (200): OK I can store your PyPI login so future submissions will be faster. (the login will be stored in /home/xingming/.pypirc) Save your login (y/N)?y關于 register 更詳細的內容可以看 PackageIndex
上傳到PyPI
上傳文件也是有 SSH 和 網頁兩種方法。
網頁: 在瀏覽器登陸到 PyPI 點擊 Your packages 中 Egg 的項目,然后選擇某個版本的 files 即可看到上傳界面。
下面這句話還是比較常用的,因為你的包以后更新的話,就不用再去注冊了,直接使用下面的命令生成包并上傳即可。
SSH:
這個又是一堆的輸出信息,我就不羅列了。
安裝測試
用 pip 安裝:
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:857662006 尋找有志同道合的小伙伴,互幫互助, 群里還有不錯的視頻學習教程和PDF電子書! ''' $ pip install edssdk Downloading/unpacking edssdk Downloading edssdk-0.0.1.tar.gz Running setup.py (path:/home/huoxm/pyvirt/build/edssdk/setup.py) egg_info for package edssdk Installing collected packages: edssdk Running setup.py install for edssdk測試:
$ python -c "from edssdk import help; help.sum(3,5);help.output();" 8 http://xiaoh.meOK, 現在這個 sdk 大家都可以用到了~~~~~
關于 upload 更詳細的內容可以看 Uploading
setup.py 中調用當前目錄的文件一定要加 MANIFEST.in 并將調用文件 include 進來
使用 python setup.py sdist 打包時,如果 setup.py 調用了當前目錄中的文件(如README.rst):
$ long_description = open('README.rst').read()一定要在增加 MANIFEST.in 文件并將調用文件 include 進來,否則將導致用戶在 pip install 時報文件找不到的錯誤,示例:
$ cat MANIFEST.in include README.rst項目邏輯內容如果直接在 ‘init.py’ 中完成的話,那么引用就可以更簡便了。
CLI命令行工具介紹
CLI(command-line interface,命令行界面)是指可在用戶提示符下鍵入可執行指令的界面,它通常不支持鼠標,用戶通過鍵盤輸入指令,計算機接收到指令后,予以執行。
就我直觀的理解就是,這就是給程序員使用的,可以在終端即使裝XX的美妙工具。
CLI制作
由于是 CLI 的命令行工具,所以程序一定要有一個入口的位置,所以我在 help.py 里面添加了 main 函數:
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:857662006 尋找有志同道合的小伙伴,互幫互助, 群里還有不錯的視頻學習教程和PDF電子書! ''' def main():print 'this is main()'print sys.argv[1:] if __name__ == "__main__":main()這個 main 下面會在 setup.py 中用到,下面說一下 setup.py 的相關修改。
看英文文檔真的是頭大,所以后來干脆到 github 上面去找 CLI 的源碼來看。發現每個 CLI 工具的 setup.py 中都會有 entry_points 這個節點。
entry_points = {'console_scripts': ['edssdk = edssdk.help:main'] }完整的 setup.py 為:
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:857662006 尋找有志同道合的小伙伴,互幫互助, 群里還有不錯的視頻學習教程和PDF電子書! ''' from setuptools import setup, find_packages setup(name = "edssdk",version = "0.1.2",keywords = ("pip", "datacanvas", "eds", "xiaoh"),description = "eds sdk",long_description = "eds sdk for python",license = "MIT Licence",url = "http://xiaoh.me",author = "xiaoh",author_email = "huoxingming@gmail.com",packages = find_packages(),include_package_data = True,platforms = "any",install_requires = [],scripts = [],entry_points = {'console_scripts': ['edssdk = edssdk.help:main']} )這個里面多了一個 entrypoints 節點,里面的 consolescripts 指明了命令行工具的名稱:
edssdk = edssdk.help:main等號前面指明了工具包的名稱,等號后面的內容指明了程序的入口地址,當然,這個可以有多條記錄,這樣一個項目就可以制作多個命令行工具了
制作PyPI
制作包的過程和上面的是一樣的
$ python setup.py sdist $ python setup.py register $ python setup.py sdist upload安裝測試
通過 pip 更新一下包:
測試工具包:
$ edssdk 2345 this is main() ['2345']OK,方法已經找到了,CLI 工具也就好弄了。
這里講解一下 Setup.py 中的一些參數
- packages 告訴Distutils需要處理那些包(包含 init.py的文件夾)
- package_dir 告訴Distutils哪些目錄下的文件被映射到哪個源碼包,感覺好像是一個相對路徑的定義。一個例子: package_dir = {’’: ‘lib’},表示以lib為主目錄。
- ext_modules 是一個包含Extension實例的列表,Extension的定義也有一些參數。
- ext_package 定義extension的相對路徑
- requires 定義依賴哪些模塊
- provides 定義可以為哪些模塊提供依賴
- scripts 指定python源碼文件,可以從命令行執行。在安裝時指定–install-script
- package_data 通常包含與包實現相關的一些數據文件或類似于readme的文件。
- packagedata = {’’: [’.txt’], ‘mypkg’: [‘data/_.dat’],}
表示包含所有目錄下的txt文件和mypkg/data目錄下的所有dat文件。
data_files 指定其他的一些文件(如配置文件) setup(…, data_files=[('bitmaps', ['bm/b1.gif', 'bm/b2.gif']), ('config', ['cfg/data.cfg']), ('/etc/init.d', ['init-script'])] )規定了哪些文件被安裝到哪些目錄中。如果目錄名是相對路徑,則是相對于 sys.prefix 或 sys.exec_prefix 的路徑。如果沒有提供模板,會被添加到MANIFEST文件中。
執行sdist命令時,默認會打包哪些東西呢?
- 所有由 py_modules或 packages指定的源碼文件
- 所有由 ext_modules或 libraries指定的C源碼文件
- 由scripts指定的腳本文件
- 類似于 test/test*.py的文件
- README.txt或 README, setup.py, setup.cfg
- 所有 package_data或 data_files指定的文件
還有一種方式是寫一個manifest template,名為MANIFEST.in,定義如何生成MANIFEST文件,內容就是需要包含在分發包中的文件。一個MANIFEST.in文件如下:
include *.txt recursive-include examples *.txt *.py prune examples/sample?/buildfcon 是剛剛完成的一個python模塊,他可以查找指定目錄下的符合一定規則文件名的內容中包含一定規則的行,并打印出來。說起來比較拗口,就是三個參數,文件目錄,文件正則,字符正則,三個一綜合就是輸出結果了。
這個工具主要幫我解決查找部分內容的功能,寫的博客里面有好多用的是默認的背景圖,有時間的時候我就會換一下,這時候我就需要查出來那些用到了默認的。
你可以通過 pip install fcon 來安裝使用。
這次打包過程還挺煩的,一直有問題,是因為我用到了 Click模塊 之后就發現,我不用指定內置的運行函數(main)了,而且,我希望可以輸出version,并且這個version在setup.py里面也可以使用,這就高出了好多問題。還好剛剛都解決了。
首先說,看一下我所有文件的結構:
$ tree . ├── bin │ └── fcon ├── fcon │ └── __init__.py ├── README.md └── setup.py 2 directories, 4 files可以發現,我的 fcon 腳本放到了 bin 目錄下,而 fcon 文件夾里面只有一個 init.py 文件,這是為了包含的時候不沖突,比如我在 fcon 腳本里面用到了這句話:
try:import fcon except:sys.path.append(os.path.join(os.path.dirname(__file__), "../"))import fcon def output_version(ctx, param, value):if not value or ctx.resilient_parsing:returnclick.echo("Version: %s" % fcon.__version__)ctx.exit()這個就是通過引用來設置 version 的部分,這樣就實現了,version一處改,大家起開懷的要求了。 init.py 如下:
對的,就這么一句話。
再看我的 setup.py 關鍵就是這個文件了:
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:857662006 尋找有志同道合的小伙伴,互幫互助, 群里還有不錯的視頻學習教程和PDF電子書! ''' $ cat setup.py #!/usr/bin/env python #-*- coding:utf-8 -*-from setuptools import setup, find_packages import fcon setup(name = "fcon",version = fcon.__version__,keywords = ("find", "fcon", "xiaoh"),description = "find content",long_description = "print files which contain the content you want to search.",license = "MIT Licence",url = "http://xiaoh.me",author = "xiaoh",author_email = "xiaoh@about.me",packages = ['fcon'],package_data = {},include_package_data = True,platforms = "any",install_requires = ["click"],scripts = ['bin/fcon'] # entry_points = { # 'console_scripts': [ # 'fcon = bin/fcon' # ] # } )注釋的地方是我測試的過程。。。
就說這么多,關鍵還得是看腳本,看寫法。
總結
以上是生活随笔為你收集整理的一文读懂:从 Python 打包到 CLI 工具的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: GitHub标星2.6万!Python算
- 下一篇: 如何使用 Python 进行时间序列预测