一文读懂:从 Python 打包到 CLI 工具
最近項(xiàng)目組在寫項(xiàng)目的 CLI 工具,已經(jīng)接近尾聲,想做成 pip 的安裝包,所以才有了這篇文章。
1,文章介紹了如何生成 Python Egg ,上傳 PyPI 及其 pip 的安裝測(cè)試
2,在后面的進(jìn)階部分會(huì)介紹簡(jiǎn)單的生成cli工具的方法
話說(shuō)既然研究了如何做包,不想浪費(fèi)這次機(jī)會(huì),干脆水一個(gè)博客吧
整理項(xiàng)目
先創(chuàng)建一個(gè)項(xiàng)目的文件夾
$ mkdir eds # eds 是我項(xiàng)目的名稱,你隨意修改成自己的即可 $ cd eds在里面在創(chuàng)建一個(gè) edssdk 的文件夾,這個(gè)文件夾的名稱我故意創(chuàng)建的和上層目錄不一樣,以免誤會(huì),這個(gè)文件夾其實(shí)就是包名稱了
$ mkdir edssdk # 這個(gè)文件夾就是包名稱 $ cd edssdk這個(gè)時(shí)候就是寫代碼的時(shí)候了,如果項(xiàng)目邏輯簡(jiǎn)單,你可以選擇在文件夾里面只創(chuàng)建一個(gè) init.py 文件,將所有的函數(shù)寫到此文件里
當(dāng)然如果項(xiàng)目復(fù)雜,你可以多創(chuàng)建幾個(gè)文件,這里我繁中取簡(jiǎn),只創(chuàng)建一個(gè)其他文件。
$ touch __init__.py # 這個(gè)文件作用就是給這個(gè)文件夾打成包 $ touch help.py # 這里是你的邏輯代碼了,我就簡(jiǎn)單寫了我寫了兩個(gè)函數(shù)在 help.py 中
''' 遇到問(wèn)題沒(méi)人解答?小編創(chuàng)建了一個(gè)Python學(xué)習(xí)交流QQ群:857662006 尋找有志同道合的小伙伴,互幫互助, 群里還有不錯(cuò)的視頻學(xué)習(xí)教程和PDF電子書(shū)! ''' $ 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包
現(xiàn)在項(xiàng)目邏輯已經(jīng)完成,那么開(kāi)始做 PyPI 的包了。
在 eds 文件夾中,創(chuàng)建 Egg 的配置文件 setup.py,并填寫配置
''' 遇到問(wèn)題沒(méi)人解答?小編創(chuàng)建了一個(gè)Python學(xué)習(xí)交流QQ群:857662006 尋找有志同道合的小伙伴,互幫互助, 群里還有不錯(cuò)的視頻學(xué)習(xí)教程和PDF電子書(shū)! ''' $ 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 = [] )當(dāng)你的包很復(fù)雜的時(shí)候,勢(shì)必會(huì)引用其他的包,你需要將你所有引用的包名稱寫到 install_requires 這個(gè)里面:
install_requires = ["requests"]當(dāng)然 setup 還有很多可以選擇的項(xiàng)可以填,你可以 python setup.py –help 查看,也可以去看 文檔
OK,看一下現(xiàn)在目錄的結(jié)構(gòu):
$ tree $ eds $ ├── edssdk $ │ ├── help.py $ │ └── __init__.py $ └── setup.py這里面還需要說(shuō)一下,setup 文件支持用配置文件來(lái)編寫里面的參數(shù)
$ 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()打包
打包這一步我認(rèn)為比較簡(jiǎn)單,目前比較流行的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上面兩條命令都會(huì)將文件生成到 dist 目錄中
當(dāng)然還有其他非主流格式或者其他選項(xiàng),可以通過(guò)下面這個(gè)命令查看:
$ python setup.py --help-commands注冊(cè)PyPI包
我是直接在 SSH 下面進(jìn)行操作的,你也可以通過(guò) 網(wǎng)頁(yè) 來(lái)做,SSH 步驟:
''' 遇到問(wèn)題沒(méi)人解答?小編創(chuàng)建了一個(gè)Python學(xué)習(xí)交流QQ群:857662006 尋找有志同道合的小伙伴,互幫互助, 群里還有不錯(cuò)的視頻學(xué)習(xí)教程和PDF電子書(shū)! ''' $ 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關(guān)于 register 更詳細(xì)的內(nèi)容可以看 PackageIndex
上傳到PyPI
上傳文件也是有 SSH 和 網(wǎng)頁(yè)兩種方法。
網(wǎng)頁(yè): 在瀏覽器登陸到 PyPI 點(diǎn)擊 Your packages 中 Egg 的項(xiàng)目,然后選擇某個(gè)版本的 files 即可看到上傳界面。
下面這句話還是比較常用的,因?yàn)槟愕陌院蟾碌脑?#xff0c;就不用再去注冊(cè)了,直接使用下面的命令生成包并上傳即可。
SSH:
這個(gè)又是一堆的輸出信息,我就不羅列了。
安裝測(cè)試
用 pip 安裝:
''' 遇到問(wèn)題沒(méi)人解答?小編創(chuàng)建了一個(gè)Python學(xué)習(xí)交流QQ群:857662006 尋找有志同道合的小伙伴,互幫互助, 群里還有不錯(cuò)的視頻學(xué)習(xí)教程和PDF電子書(shū)! ''' $ 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測(cè)試:
$ python -c "from edssdk import help; help.sum(3,5);help.output();" 8 http://xiaoh.meOK, 現(xiàn)在這個(gè) sdk 大家都可以用到了~~~~~
關(guān)于 upload 更詳細(xì)的內(nèi)容可以看 Uploading
setup.py 中調(diào)用當(dāng)前目錄的文件一定要加 MANIFEST.in 并將調(diào)用文件 include 進(jìn)來(lái)
使用 python setup.py sdist 打包時(shí),如果 setup.py 調(diào)用了當(dāng)前目錄中的文件(如README.rst):
$ long_description = open('README.rst').read()一定要在增加 MANIFEST.in 文件并將調(diào)用文件 include 進(jìn)來(lái),否則將導(dǎo)致用戶在 pip install 時(shí)報(bào)文件找不到的錯(cuò)誤,示例:
$ cat MANIFEST.in include README.rst項(xiàng)目邏輯內(nèi)容如果直接在 ‘init.py’ 中完成的話,那么引用就可以更簡(jiǎn)便了。
CLI命令行工具介紹
CLI(command-line interface,命令行界面)是指可在用戶提示符下鍵入可執(zhí)行指令的界面,它通常不支持鼠標(biāo),用戶通過(guò)鍵盤輸入指令,計(jì)算機(jī)接收到指令后,予以執(zhí)行。
就我直觀的理解就是,這就是給程序員使用的,可以在終端即使裝XX的美妙工具。
CLI制作
由于是 CLI 的命令行工具,所以程序一定要有一個(gè)入口的位置,所以我在 help.py 里面添加了 main 函數(shù):
''' 遇到問(wèn)題沒(méi)人解答?小編創(chuàng)建了一個(gè)Python學(xué)習(xí)交流QQ群:857662006 尋找有志同道合的小伙伴,互幫互助, 群里還有不錯(cuò)的視頻學(xué)習(xí)教程和PDF電子書(shū)! ''' def main():print 'this is main()'print sys.argv[1:] if __name__ == "__main__":main()這個(gè) main 下面會(huì)在 setup.py 中用到,下面說(shuō)一下 setup.py 的相關(guān)修改。
看英文文檔真的是頭大,所以后來(lái)干脆到 github 上面去找 CLI 的源碼來(lái)看。發(fā)現(xiàn)每個(gè) CLI 工具的 setup.py 中都會(huì)有 entry_points 這個(gè)節(jié)點(diǎn)。
entry_points = {'console_scripts': ['edssdk = edssdk.help:main'] }完整的 setup.py 為:
''' 遇到問(wèn)題沒(méi)人解答?小編創(chuàng)建了一個(gè)Python學(xué)習(xí)交流QQ群:857662006 尋找有志同道合的小伙伴,互幫互助, 群里還有不錯(cuò)的視頻學(xué)習(xí)教程和PDF電子書(shū)! ''' 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']} )這個(gè)里面多了一個(gè) entrypoints 節(jié)點(diǎn),里面的 consolescripts 指明了命令行工具的名稱:
edssdk = edssdk.help:main等號(hào)前面指明了工具包的名稱,等號(hào)后面的內(nèi)容指明了程序的入口地址,當(dāng)然,這個(gè)可以有多條記錄,這樣一個(gè)項(xiàng)目就可以制作多個(gè)命令行工具了
制作PyPI
制作包的過(guò)程和上面的是一樣的
$ python setup.py sdist $ python setup.py register $ python setup.py sdist upload安裝測(cè)試
通過(guò) pip 更新一下包:
測(cè)試工具包:
$ edssdk 2345 this is main() ['2345']OK,方法已經(jīng)找到了,CLI 工具也就好弄了。
這里講解一下 Setup.py 中的一些參數(shù)
- packages 告訴Distutils需要處理那些包(包含 init.py的文件夾)
- package_dir 告訴Distutils哪些目錄下的文件被映射到哪個(gè)源碼包,感覺(jué)好像是一個(gè)相對(duì)路徑的定義。一個(gè)例子: package_dir = {’’: ‘lib’},表示以lib為主目錄。
- ext_modules 是一個(gè)包含Extension實(shí)例的列表,Extension的定義也有一些參數(shù)。
- ext_package 定義extension的相對(duì)路徑
- requires 定義依賴哪些模塊
- provides 定義可以為哪些模塊提供依賴
- scripts 指定python源碼文件,可以從命令行執(zhí)行。在安裝時(shí)指定–install-script
- package_data 通常包含與包實(shí)現(xiàn)相關(guān)的一些數(shù)據(jù)文件或類似于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'])] )規(guī)定了哪些文件被安裝到哪些目錄中。如果目錄名是相對(duì)路徑,則是相對(duì)于 sys.prefix 或 sys.exec_prefix 的路徑。如果沒(méi)有提供模板,會(huì)被添加到MANIFEST文件中。
執(zhí)行sdist命令時(shí),默認(rèn)會(huì)打包哪些東西呢?
- 所有由 py_modules或 packages指定的源碼文件
- 所有由 ext_modules或 libraries指定的C源碼文件
- 由scripts指定的腳本文件
- 類似于 test/test*.py的文件
- README.txt或 README, setup.py, setup.cfg
- 所有 package_data或 data_files指定的文件
還有一種方式是寫一個(gè)manifest template,名為MANIFEST.in,定義如何生成MANIFEST文件,內(nèi)容就是需要包含在分發(fā)包中的文件。一個(gè)MANIFEST.in文件如下:
include *.txt recursive-include examples *.txt *.py prune examples/sample?/buildfcon 是剛剛完成的一個(gè)python模塊,他可以查找指定目錄下的符合一定規(guī)則文件名的內(nèi)容中包含一定規(guī)則的行,并打印出來(lái)。說(shuō)起來(lái)比較拗口,就是三個(gè)參數(shù),文件目錄,文件正則,字符正則,三個(gè)一綜合就是輸出結(jié)果了。
這個(gè)工具主要幫我解決查找部分內(nèi)容的功能,寫的博客里面有好多用的是默認(rèn)的背景圖,有時(shí)間的時(shí)候我就會(huì)換一下,這時(shí)候我就需要查出來(lái)那些用到了默認(rèn)的。
你可以通過(guò) pip install fcon 來(lái)安裝使用。
這次打包過(guò)程還挺煩的,一直有問(wèn)題,是因?yàn)槲矣玫搅?Click模塊 之后就發(fā)現(xiàn),我不用指定內(nèi)置的運(yùn)行函數(shù)(main)了,而且,我希望可以輸出version,并且這個(gè)version在setup.py里面也可以使用,這就高出了好多問(wèn)題。還好剛剛都解決了。
首先說(shuō),看一下我所有文件的結(jié)構(gòu):
$ tree . ├── bin │ └── fcon ├── fcon │ └── __init__.py ├── README.md └── setup.py 2 directories, 4 files可以發(fā)現(xiàn),我的 fcon 腳本放到了 bin 目錄下,而 fcon 文件夾里面只有一個(gè) init.py 文件,這是為了包含的時(shí)候不沖突,比如我在 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()這個(gè)就是通過(guò)引用來(lái)設(shè)置 version 的部分,這樣就實(shí)現(xiàn)了,version一處改,大家起開(kāi)懷的要求了。 init.py 如下:
對(duì)的,就這么一句話。
再看我的 setup.py 關(guān)鍵就是這個(gè)文件了:
''' 遇到問(wèn)題沒(méi)人解答?小編創(chuàng)建了一個(gè)Python學(xué)習(xí)交流QQ群:857662006 尋找有志同道合的小伙伴,互幫互助, 群里還有不錯(cuò)的視頻學(xué)習(xí)教程和PDF電子書(shū)! ''' $ 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' # ] # } )注釋的地方是我測(cè)試的過(guò)程。。。
就說(shuō)這么多,關(guān)鍵還得是看腳本,看寫法。
總結(jié)
以上是生活随笔為你收集整理的一文读懂:从 Python 打包到 CLI 工具的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: GitHub标星2.6万!Python算
- 下一篇: 如何使用 Python 进行时间序列预测