1. 程式人生 > >Python 命令列之旅:使用 click 實現 git 命令

Python 命令列之旅:使用 click 實現 git 命令



作者:HelloGitHub-Prodesire

HelloGitHub 的《講解開源專案》系列,專案地址:https://github.com/HelloGitHub-Team/Article

一、前言

在前面五篇介紹 click 的文章中,我們全面瞭解了 click 的強大能力。按照慣例,我們要像使用 argparsedocopt 一樣使用 click 來實現 git 命令。

本文的關注點並不在 git 的各種命令是如何實現的,而是怎麼使用 click 去打造一個實用命令列程式,程式碼結構是怎樣的。因此,和 git 相關的操作,將會使用 gitpython 庫來簡單實現。

為了讓沒讀過 使用 xxx 實現 git 命令

xxxargparsedocopt) 的小夥伴也能讀明白本文,我們仍會對 git 常用命令和 gitpython 做一個簡單介紹。

本系列文章預設使用 Python 3 作為直譯器進行講解。
若你仍在使用 Python 2,請注意兩者之間語法和庫的使用差異哦~

二、git 常用命令

當你寫好一段程式碼或增刪一些檔案後,會用如下命令檢視檔案狀態:

git status

確認檔案狀態後,會用如下命令將的一個或多個檔案(夾)新增到暫存區:

git add [pathspec [pathspec ...]]

然後使用如下命令提交資訊:

git commit -m "your commit message"

最後使用如下命令將提交推送到遠端倉庫:

git push

我們將使用 clickgitpython 庫來實現這 4 個子命令。

三、關於 gitpython

gitpython 是一個和 git 倉庫互動的 Python 第三方庫。
我們將借用它的能力來實現真正的 git 邏輯。

安裝:

pip install gitpython

四、思考

在實現前,我們不妨先思考下會用到 click 的哪些功能?整個程式的結構是怎樣的?

click

git 的 4 個子命令的實現其實對應於四個函式,每個函式使用 clickcommand 來裝飾。
而對於 git addgit commit

,則分別需要表示引數的 click.argument 和表示選項的 click.option 來裝飾。

程式結構

程式結構上:

  • 例項化 Git 物件,供全域性使用
  • 定義 cli 函式作為命令組,也就是整個命令程式的入口
  • 定義四個命令對應的實現函式 statusaddcommitpush

則基本結構如下:

import os
import click
from git.cmd import Git

git = Git(os.getcwd())


@click.group()
def cli():
    """
    git 命令列
    """
    pass


@cli.command()
def status():
    """
    處理 status 命令
    """
    pass


@cli.command()
@click.argument('pathspec', nargs=-1)
def add(pathspec):
    """
    處理 add 命令
    """
    pass


@cli.command()
@click.option('-m', 'msg')
def commit(msg):
    """
    處理 -m <msg> 命令
    """
    pass


@cli.command()
def push():
    """
    處理 push 命令
    """
    pass


if __name__ == '__main__':
    cli()

下面我們將一步步地實現我們的 git 程式。

五、實現

假定我們在 click-git.py 檔案中實現我們的 git 程式。

5.1 status 子命令

status 子命令不接受任何引數和選項,因此其實現函式只需 cli.command() 裝飾。

@cli.command()
def status():
    """
    處理 status 命令
    """
    cmd = ['git', 'status']
    output = git.execute(cmd)
    click.echo(output)

不難看出,我們最後呼叫了真正的 git status 來實現,並列印了輸出。

5.2 add 子命令

add 子命令相對於 status 子命令,需要接受任意個 pathspec 引數,因此增加一個 click.argument 裝飾器,並且在 add 函式中需要增加同名的 pathspec 入參。
click 處理後的 pathspec 其實是個元組,和列表相加前,需要先轉換為列表。

@cli.command()
@click.argument('pathspec', nargs=-1)
def add(pathspec):
    """
    處理 add 命令
    """
    cmd = ['git', 'add'] + list(pathspec)
    output = git.execute(cmd)
    click.echo(output)

當我們執行 python3 click-git.py add --help 時,結果如下:

Usage: click-git.py add [OPTIONS] [PATHSPEC]...

  處理 add 命令

Options:
  --help  Show this message and exit.

既然 git add 能接受任意多個 pathspec,那麼 add(pathspec) 的引數其實改為複數形式更為合適,但我們又希望幫助資訊中是單數形式,這就需要額外指定 metavar,則有:

@cli.command()
@click.argument('pathspecs', nargs=-1, metavar='[PATHSPEC]...')
def add(pathspecs):
    """
    處理 add 命令
    """
    cmd = ['git', 'add'] + list(pathspecs)
    output = git.execute(cmd)
    click.echo(output)

5.3 commit 子命令

add 子命令相對於 status 子命令,需要接受 -m 選項,因此增加一個 click.option 裝飾器,指定選項名稱 msg,並且在 commit 函式中增加同名入參。

@cli.command()
@click.option('-m', 'msg')
def commit(msg):
    """
    處理 -m <msg> 命令
    """
    cmd = ['git', 'commit', '-m', msg]
    output = git.execute(cmd)
    click.echo(output)

5.4 push 子命令

push 子命令同 status 子命令一樣,不接受任何引數和選項,因此其實現函式只需 cli.command() 裝飾。

@cli.command()
def push():
    """
    處理 push 命令
    """
    cmd = ['git', 'push']
    output = git.execute(cmd)
    click.echo(output)

至此,我們就實現了一個簡單的 git 命令列,使用 python click-git.py status 便可查詢專案狀態。

非常方便的是,每個命令函式的 docstring 都將作為這個命令的幫助資訊,因此,當我們執行 python3 click-git.py --help 會自動生成如下幫助內容:

Usage: click-git.py [OPTIONS] COMMAND [ARGS]...

  git 命令列

Options:
  --help  Show this message and exit.

Commands:
  add     處理 add 命令
  commit  處理 -m <msg> 命令
  push    處理 push 命令
  status  處理 status 命令

想看整個原始碼,請戳 click-git.py 。

六、小結

本文簡單介紹了日常工作中常用的 git 命令,然後提出實現它的思路,最終一步步地使用 clickgitpython 實現了 git 程式。

對比 argparseclick 的實現版本,你會發現使用 click 來實現變得特定簡單:

  • 相較於 argparse,子解析器、引數型別什麼的統統不需要關心
  • 相較於 docopt,引數解析和命令呼叫處理也不需要關心

這無疑是 click 最大的優勢了。

關於 click 的講解將告一段落,回顧下 click 的至簡之道,你會愛上它。

現在,你已學會了三個命令列解析庫的使用了。但你以為這就夠了嗎?click 已經夠簡單了吧,夠直接了吧?但它仍然不是最簡單的。

在下篇文章中,將為大家介紹一個由谷歌出品的在 Python 界很火的命令列庫 —— fire


『講解開源專案系列』——讓對開源專案感興趣的人不再畏懼、讓開源專案的發起者不再孤單。跟著我們的文章,你會發現程式設計的樂趣、使用和發現參與開源專案如此簡單。歡迎留言聯絡我們、加入我們,讓更多人愛上開源、貢獻開源