Python 命令列之旅:使用 docopt 實現 git 命令
作者:HelloGitHub-Prodesire
HelloGitHub 的《講解開源專案》系列,專案地址:https://github.com/HelloGitHub-Team/Article
一、前言
在前面兩篇介紹 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
。
『講解開源專案系列』——讓對開源專案感興趣的人不再畏懼、讓開源專案的發起者不再孤單。跟著我們的文章,你會發現程式設計的樂趣、使用和發現參與開源專案如此簡單。歡迎留言聯絡我們、加入我們,讓更多人愛上開源、貢獻開源