Python 命令行之旅:使用 docopt 實現 git 命令
原文發表於 Prodesire 博客。
一、前言
在前面兩篇介紹 docopt
的文章中,我們全面瞭解了 docopt
的能力。按照慣例,我們要像使用 argparse
一樣使用 docopt
來實現 git 命令。
爲了讓沒讀過 使用 argparse 實現 git 命令
的小夥伴也能讀明白本文,我們仍會對 git 常用命令和 gitpython 做一個簡單介紹。
本系列文章默認使用 Python 3 作爲解釋器進行講解。
若你仍在使用 Python 2,請注意兩者之間語法和庫的使用差異哦~
二、git 常用命令
當你寫好一段代碼或增刪一些文件後,會用如下命令查看文件狀態:
git status
確認文件狀態後,會用如下命令將的一個或多個文件(夾)添加到暫存區:
git add [pathspec [pathspec ...]]
然後使用如下命令提交信息:
git commit -m "your commit message"
最後使用如下命令將提交推送到遠程倉庫:
git push
我們將使用 docopt
和 gitpython
庫來實現這 4 個子命令。
三、關於 gitpython
gitpython 是一個和 git
倉庫交互的 Python 第三方庫。
我們將借用它的能力來實現真正的 git
邏輯。
安裝:
pip install gitpython
四、思考
在實現前,我們不妨先思考下會用到 docopt
的哪些功能?整個程序的結構是怎樣的?
docopt
不同於使用 argparse
時需要考慮嵌套解析器、各類參數等問題,在使用 docopt
只需將我們要實現的 git 命令用接口描述先定義清楚即可。
程序結構
程序結構上,除了開頭處定義接口描述外,其餘和使用 argparse
實現 git 命令的結構是一樣的:
- 命令行程序需要一個
cli
函數來作爲統一的入口,它負責構建解析器,並解析命令行參數 - 我們還需要四個
handle_xxx
函數響應對應的子命令
則基本結構如下:
import os
import docopt
from git.cmd import Git
def cli():
"""
git 命名程序入口
"""
pass
def handle_status(git):
"""
處理 status 命令
"""
pass
def handle_add(git, pathspec):
"""
處理 add 命令
"""
pass
def handle_commit(git, msg):
"""
處理 -m <msg> 命令
"""
pass
def handle_push(git):
"""
處理 push 命令
"""
pass
if __name__ == '__main__':
cli()
下面我們將一步步地實現我們的 git
程序。
五、實現
假定我們在 docopt-git.py 文件中實現我們的 git
程序。
5.1 定義接口描述
根據我們的要求,可以很容易的定義出接口描述:
Usage:
git status
git add [<pathspec>...]
git commit -m msg
git push
Options:
-h --help Show help.
-m --message msg Commit with message.
進而就可以在 cli()
中解析命令行:
def cli():
"""
git 命名程序入口
"""
args = docopt(__doc__)
git = Git(os.getcwd())
5.2 status 子命令
如果 args['status']
爲 True
,說明輸入了 status 子命令,那麼就調用 handle_status
函數進行處理。
def cli():
...
if args['status']:
handle_status(git)
def handle_status(git):
"""
處理 status 命令
"""
cmd = ['git', 'status']
output = git.execute(cmd)
print(output)
不難看出,我們最後調用了真正的 git status
來實現,並打印了輸出。
5.3 add 子命令
如果 args['add']
爲 True
,說明輸入了 add 子命令,那麼就調用 handle_add
函數進行處理,需要傳入 args['<pathspec>']
表示添加的路徑。
def cli():
...
elif args['add']:
handle_add(git, args['<pathspec>'])
def handle_add(git, pathspec):
"""
處理 add 命令
"""
cmd = ['git', 'add'] + pathspec
output = git.execute(cmd)
print(output)
5.4 commit 子命令
如果 args['commit']
爲 True
,說明輸入了 commit 子命令,那麼就調用 handle_commit
函數進行處理,需要傳入 args['--message']
表示提交的信息。
def cli():
...
elif args['commit']:
handle_commit(git, args['--message'])
def handle_commit(git, msg):
"""
處理 -m <msg> 命令
"""
cmd = ['git', 'commit', '-m', msg]
output = git.execute(cmd)
print(output)
5.5 push 子命令
如果 args['push']
爲 True
,說明輸入了 commit 子命令,那麼就調用 handle_push
函數進行處理。
def cli():
...
elif args['push']:
handle_push(git)
def handle_push(git):
"""
處理 push 命令
"""
cmd = ['git', 'push']
output = git.execute(cmd)
print(output)
至此,我們就實現了一個簡單的 git
命令行,使用 python docopt-git.py status
便可查詢項目狀態。
想看整個源碼,請戳 docopt-git.py 。
六、小結
本文簡單介紹了日常工作中常用的 git
命令,然後提出實現它的思路,最終一步步地使用 docopt
和 gitpython
實現了 git
程序。
對比 argparse
的實現版本,你會發現使用 docopt
來實現變得非常簡單,子解析器、參數類型什麼的統統不需要關心!這可以說是 docopt
最大的優勢了。
關於 docopt
的講解將告一段落,回顧下 docopt
的三步曲,加上今天的內容,感覺它的使用方式還是比 argparse
簡單不少的。
現在,你已學會了兩個命令行解析庫的使用了。但你以爲這就夠了嗎?
但人類的智慧是多麼璀璨呀,有些人並不喜歡這兩個庫的使用方式,於是他們有開闢了一個全新的思路。
在下篇文章中,將爲大家介紹一個在 Python 界十分流行的命令行庫 —— click
。