
作者:HelloGitHub-Prodesire
HelloGitHub 的《講解開源項目》系列,項目地址:https://github.com/HelloGitHub-Team/Article
一、前言
在前面三篇介紹 fire 的文章中,我們全面了解了 fire 強大而不失簡潔的能力。按照慣例,我們要像使用 argparse、docopt 和 click 一樣使用 fire 來實現 git 命令。
本文的關注點並不在 git 的各種命令是如何實現的,而是怎么使用 fire 去打造一個實用命令行程序,代碼結構是怎樣的。因此,和 git 相關的操作,將會使用 gitpython 庫來簡單實現。
為了讓沒讀過 使用 xxx 實現 git 命令(xxx 指 argparse、docopt 和 click) 的小伙伴也能讀明白本文,我們仍會對 git 常用命令和 gitpython 做一個簡單介紹。
本系列文章默認使用 Python 3 作為解釋器進行講解。
若你仍在使用 Python 2,請注意兩者之間語法和庫的使用差異哦~
二、git 常用命令
當你寫好一段代碼或增刪一些文件后,會用如下命令查看文件狀態:
git status
確認文件狀態后,會用如下命令將的一個或多個文件(夾)添加到暫存區:
git add [pathspec [pathspec ...]]
然后使用如下命令提交信息:
git commit -m "your commit message"
最后使用如下命令將提交推送到遠程倉庫:
git push
我們將使用 fire 和 gitpython 庫來實現這 4 個子命令。
三、關於 gitpython
gitpython 是一個和 git 倉庫交互的 Python 第三方庫。
我們將借用它的能力來實現真正的 git 邏輯。
安裝:
pip install gitpython
四、思考
在實現前,我們不妨先思考下會用到 fire 的哪些功能?整個程序的結構是怎樣的?
fire
git 的 4 個子命令的實現其實對應於四個函數,我們可以都放到一個類中,實現四個實例方法。
而對於 git add 命令,需要接受任意個參數,在實例方法中用 *pathspecs 參數來表達。
對於 git commit 命令,需要接受 -m 選項,在實例方法中用 m 參數來表達。
程序結構
程序結構上:
- 實例化
Git對象,供全局使用 - 在
GitCli類中定義四個命令對應的實例方法status、add、commit、push
則基本結構如下:
import os
import fire
from git.cmd import Git
git = Git(os.getcwd())
class GitCli:
def status(self):
"""
處理 status 命令
"""
pass
def add(self, *pathspecs):
"""
處理 add 命令
"""
pass
def commit(self, m):
"""
處理 -m <msg> 命令
"""
pass
def push(self):
"""
處理 push 命令
"""
pass
if __name__ == '__main__':
fire.Fire(GitCli())
下面我們將一步步地實現我們的 git 程序。
五、實現
假定我們在 fire-git.py 文件中實現我們的 git 程序。
5.1 status 子命令
status 子命令不接受任何參數和選項,因此 status 方法無需任何入參。
class GitCli:
def status(self):
"""
處理 status 命令
"""
cmd = ['git', 'status']
output = git.execute(cmd)
return output
不難看出,我們最后調用了真正的 git status 來實現,並打印了輸出。
5.2 add 子命令
add 子命令相對於 status 子命令,需要接受任意個 pathspec 參數,因此 add 方法需要增加 *pathspecs 入參。
fire 最終傳入的是一個元組,我們需要將其轉換成 list 以便后續處理。
class GitCli:
def add(self, *pathspecs):
"""
處理 add 命令
"""
cmd = ['git', 'add'] + list(pathspecs)
output = git.execute(cmd)
return output
當我們執行 python3 fire-git.py add --help 時,結果如下:
INFO: Showing help with the command 'fire-git.py add -- --help'.
NAME
fire-git.py add - 處理 add 命令
SYNOPSIS
fire-git.py add [PATHSPECS]...
DESCRIPTION
處理 add 命令
POSITIONAL ARGUMENTS
PATHSPECS
5.3 commit 子命令
commit 子命令相對於 status 子命令,需要接受 -m 選項,因此 commit 方法需要增加 m 入參。
class GitCli:
def commit(self, m):
"""
處理 -m <msg> 命令
"""
cmd = ['git', 'commit', '-m', m]
output = git.execute(cmd)
return output
5.4 push 子命令
push 子命令同 status 子命令一樣,不接受任何參數和選項,因此 push 方法無需任何入參。
class GitCli:
def push(self):
"""
處理 push 命令
"""
cmd = ['git', 'push']
output = git.execute(cmd)
return output
至此,我們就實現了一個簡單的 git 命令行,使用 python fire-git.py status 便可查詢項目狀態。
非常方便的是,每個命令函數的 docstring 都將作為這個命令的幫助信息,因此,當我們執行 python3 fire-git.py --help 會自動生成如下幫助內容:
INFO: Showing help with the command 'fire-git.py -- --help'.
NAME
fire-git.py
SYNOPSIS
fire-git.py COMMAND
COMMANDS
COMMAND is one of the following:
add
處理 add 命令
commit
處理 -m <msg> 命令
push
處理 push 命令
status
處理 status 命令
想看整個源碼,請戳 fire-git.py 。
六、小結
本文簡單介紹了日常工作中常用的 git 命令,然后提出實現它的思路,最終一步步地使用 fire 和 gitpython 實現了 git 程序。
對比 argparse、docopt 和 click 的實現版本,你會發現使用 fire 來實現是最簡單的:
- 相較於
argparse,子解析器、參數類型什么的統統不需要關心 - 相較於
docopt,參數解析和命令調用處理也不需要關心 - 相較於
click,裝飾器所定義的命令行參數信息也必須要關心
無疑,fire 把能簡化的都簡化了,簡直就是懶人福音。
關於 fire 的講解將告一段落,回顧下 fire 的至簡之道,你會深愛上它。這也體現出了 Python 之美。
現在,你已學會了四個特點各異的主流命令行解析庫的使用了,再也不需要為命令行程序的實現而煩惱了。
什么,你為要使用哪一個庫而發愁?在下一篇也是最后一篇文章中,我們將對這些庫做一個橫向對比,以對什么場景下使用什么樣的命令行庫了然於胸~
『講解開源項目系列』——讓對開源項目感興趣的人不再畏懼、讓開源項目的發起者不再孤單。跟着我們的文章,你會發現編程的樂趣、使用和發現參與開源項目如此簡單。歡迎留言聯系我們、加入我們,讓更多人愛上開源、貢獻開源~
