日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網(wǎng)站首頁 編程語言 正文

Python實現(xiàn)腳本轉(zhuǎn)換為命令行程序_python

作者:Mark?Meyer ? 更新時間: 2022-11-14 編程語言

在我的職業(yè)生涯中,我寫過、用過和看到過很多隨意的腳本。一些人需要半自動化完成任務(wù),于是它們誕生了。一段時間后,它們變得越來越大。它們在一生中可能轉(zhuǎn)手很多次。我常常希望這些腳本提供更多的?命令行工具式?的感覺。但是,從一次性腳本到合適的工具,真正提高質(zhì)量水平有多難呢?事實證明這在 Python 中并不難。

搭建骨架腳本

在本文中,我將從一小段 Python 代碼開始。我將把它應(yīng)用到????scaffold?????模塊中,并使用????click????庫擴展它以接受命令行參數(shù)。

#!/usr/bin/python

from glob import glob
from os.path import join, basename
from shutil import move
from datetime import datetime
from os import link, unlink


LATEST = 'latest.txt'
ARCHIVE = '/Users/mark/archive'
INCOMING = '/Users/mark/incoming'
TPATTERN = '%Y-%m-%d'


def transmogrify_filename(fname):
    bname = basename(fname)
    ts = datetime.now().strftime(TPATTERN)
    return '-'.join([ts, bname])

def set_current_latest(file):
    latest = join(ARCHIVE, LATEST)

    try:
        unlink(latest)
    except:
        pass
    link(file, latest)


def rotate_file(source):

    target = join(ARCHIVE, transmogrify_filename(source))
    move(source, target)
    set_current_latest(target)


def rotoscope():
    file_no = 0
    folder = join(INCOMING, '*.txt')
    print(f'Looking in {INCOMING}')

    for file in glob(folder):
        rotate_file(file)
        print(f'Rotated: {file}')
        file_no = file_no + 1
    print(f'Total files rotated: {file_no}')

if __name__ == '__main__':

    print('This is rotoscope 0.4.1. Bleep, bloop.')
    rotoscope()

本文所有沒有在這里插入顯示的代碼示例,你都可以在????https://codeberg.org/ofosos/rotoscope????中找到特定版本的代碼。該倉庫中的每個提交都描述了本文操作過程中一些有意義的步驟。

這個片段做了幾件事:

?INCOMING?
?ARCHIVE?
?ARCHIVE/latest.txt?

作為一個示例,它很簡單,但它會讓你理解這個過程。

使用 Pyscaffold 創(chuàng)建應(yīng)用程序

首先,你需要安裝????scaffold????、???click?????和????tox????????Python 庫???。

$ python3 -m pip install scaffold click tox

安裝????scaffold?????后,切換到示例的????rotoscope????項目所在的目錄,然后執(zhí)行以下命令:

$ putup rotoscope -p rotoscope \

    --force --no-skeleton -n rotoscope \
    -d 'Move some files around.' -l GLWT \
    -u http://codeberg.org/ofosos/rotoscope \
    --save-config --pre-commit --markdown

Pyscaffold 會重寫我的????README.md???,所以從 Git 恢復(fù)它:

$ git checkout README.md

Pyscaffold 在文檔中說明了如何設(shè)置一個完整的示例項目,我不會在這里介紹,你之后可以探索。除此之外,Pyscaffold 還可以在項目中為你提供持續(xù)集成(CI)模板:

  • 打包: 你的項目現(xiàn)在啟用了 PyPi,所以你可以將其上傳到一個倉庫并從那里安裝它。
  • 文檔: 你的項目現(xiàn)在有了一個完整的文檔文件夾層次結(jié)構(gòu),它基于 Sphinx,包括一個???readthedocs.org????構(gòu)建器。
  • 測試: 你的項目現(xiàn)在可以與 tox 一起使用,測試文件夾包含運行基于 pytest 的測試所需的所有樣板文件。
  • 依賴管理: 打包和測試基礎(chǔ)結(jié)構(gòu)都需要一種管理依賴關(guān)系的方法。???setup.cfg????文件解決了這個問題,它包含所有依賴項。
  • 預(yù)提交鉤子: 包括 Python 源代碼格式工具 black 和 Python 風格檢查器 flake8。

查看測試文件夾并在項目目錄中運行????tox????命令,它會立即輸出一個錯誤:打包基礎(chǔ)設(shè)施無法找到相關(guān)庫。

現(xiàn)在創(chuàng)建一個????Git?????標記(例如????v0.2????),此工具會將其識別為可安裝版本。在提交更改之前,瀏覽一下自動生成的????setup.cfg?????并根據(jù)需要編輯它。對于此示例,你可以修改????LICENSE????和項目描述,將這些更改添加到 Git 的暫存區(qū),我必須禁用預(yù)提交鉤子,然后提交它們。否則,我會遇到錯誤,因為 Python 風格檢查器 flake8 會抱怨糟糕的格式。

$ PRE_COMMIT_ALLOW_NO_CONFIG=1 git commit

如果這個腳本有一個入口點,用戶可以從命令行調(diào)用,那就更好了。現(xiàn)在,你只能通過找????.py?????文件并手動執(zhí)行它來運行。幸運的是,Python 的打包基礎(chǔ)設(shè)施有一個很好的“罐裝”方式,可以輕松地進行配置更改。將以下內(nèi)容添加到????setup.cfg?????的????options.entry_points????部分:

console_scripts =
    roto = rotoscope.rotoscope:rotoscope

這個更改會創(chuàng)建一個名為????roto?????的 shell 命令,你可以使用它來調(diào)用 rotoscope 腳本,使用????pip?????安裝 rotoscope 后,可以使用????roto????命令。

就是這樣,你可以從 Pyscaffold 免費獲得所有打包、測試和文檔設(shè)置。你還獲得了一個預(yù)提交鉤子來保證(大部分情況下)你按照設(shè)定規(guī)則提交。

CLI 工具化

現(xiàn)在,一些值會硬編碼到腳本中,它們作為命令????參數(shù)?????會更方便。例如,將????INCOMING????常量作為命令行參數(shù)會更好。

首先,導(dǎo)入????click?????庫,使用 Click 提供的命令裝飾器對????rotoscope()?????方法進行裝飾,并添加一個 Click 傳遞給????rotoscope????函數(shù)的參數(shù)。Click 提供了一組驗證器,因此要向參數(shù)添加一個路徑驗證器。Click 還方便地使用函數(shù)的內(nèi)嵌字符串作為命令行文檔的一部分。所以你最終會得到以下方法簽名:

@click.command()
@click.argument('incoming', type=click.Path(exists=True))
def rotoscope(incoming):

    """

    Rotoscope 0.4 - Bleep, blooop.

    Simple sample that move files.

    """

主函數(shù)會調(diào)用????rotoscope()???,它現(xiàn)在是一個 Click 命令,不需要傳遞任何參數(shù)。

選項也可以使用????環(huán)境變量?????自動填充。例如,將????ARCHIVE????常量改為一個選項:

@click.option('archive', '--archive', default='/Users/mark/archive', envvar='ROTO_ARCHIVE', type=click.Path())

使用相同的路徑驗證器。這一次,讓 Click 填充環(huán)境變量,如果環(huán)境變量沒有提供任何內(nèi)容,則默認為舊常量的值。

Click 可以做更多的事情,它有彩色的控制臺輸出、提示和子命令,可以讓你構(gòu)建復(fù)雜的 CLI 工具。瀏覽 Click 文檔會發(fā)現(xiàn)它的更多功能。

現(xiàn)在添加一些測試。

測試

Click 對使用 CLI 運行器????運行端到端測試?????提供了一些建議。你可以用它來實現(xiàn)一個完整的測試(在????示例項目?????中,測試在????tests????文件夾中。)

測試位于測試類的一個方法中。大多數(shù)約定與我在其他 Python 項目中使用的非常接近,但有一些細節(jié),因為 rotoscope 使用????click????。在????test?????方法中,我創(chuàng)建了一個????CliRunner????。測試使用它在一個隔離的文件系統(tǒng)中運行此命令。然后測試在隔離的文件系統(tǒng)中創(chuàng)建????incoming?????和????archive?????目錄和一個虛擬的????incoming/test.txt?????文件,然后它調(diào)用 CliRunner,就像你調(diào)用命令行應(yīng)用程序一樣。運行完成后,測試會檢查隔離的文件系統(tǒng),并驗證????incoming?????為空,并且????archive????包含兩個文件(最新鏈接和存檔文件)。

from os import listdir, mkdir
from click.testing import CliRunner
from rotoscope.rotoscope import rotoscope

class TestRotoscope:
    def test_roto_good(self, tmp_path):
        runner = CliRunner()

        with runner.isolated_filesystem(temp_dir=tmp_path) as td:
            mkdir("incoming")
            mkdir("archive")
            with open("incoming/test.txt", "w") as f:
                f.write("hello")

            result = runner.invoke(rotoscope, ["incoming", "--archive", "archive"])
            assert result.exit_code == 0

            print(td)
            incoming_f = listdir("incoming")
            archive_f = listdir("archive")
            assert len(incoming_f) == 0
            assert len(archive_f) == 2

要在控制臺上執(zhí)行這些測試,在項目的根目錄中運行????tox???。

在執(zhí)行測試期間,我在代碼中發(fā)現(xiàn)了一個錯誤。當我進行 Click 轉(zhuǎn)換時,???rotoscope????只是取消了最新文件的鏈接,無論它是否存在。測試從一個新的文件系統(tǒng)(不是我的主文件夾)開始,很快就失敗了。我可以通過在一個很好的隔離和自動化測試環(huán)境中運行來防止這種錯誤。這將避免很多“它在我的機器上正常工作”的問題。

搭建骨架腳本和模塊

我們可以使用????scaffold?????和????click????完成一些高級操作。有很多方法可以升級一個普通的 Python 腳本,甚至可以將你的簡單實用程序變成成熟的 CLI 工具。

原文鏈接:https://www.51cto.com/article/719465.html

欄目分類
最近更新