2024年5月18日 星期六

Python 透過 click 仿 PowerShell 自動填充參數

Python 透過 Click 仿 PowerShell 自動填充參數

在 Linux 上很少看到像 PowerShell 那樣依照位置自動填充參數的功能,但透過 Click,我們可以實現大約九成的相似效果。

動態填充的意思是可以給參數設置一個順序,這個順序會自動抓取未加前綴的變數,依序自動填入。舉個例子:

function DemoFunction {
    Param(
        [Parameter(Position = 0, Mandatory)]
        [string]$Id,
        [Parameter(Position = 1, Mandatory)]
        [string]$Name
    )
    return $null
}

你可以通過以下的方式顛倒指定順序:

DemoFunction "CHG" -Id 777

這是一個非常方便的功能。接下來,我們看看如何在 Python 中實現類似的功能。


Python 自動參數填充

我們使用 Click,這是 Python 的一個第三方模組。
官方網站有詳細的文檔:Click Documentation (8.1.x)

以下示例分成兩個檔案:command1.py 和 setup.py

先來看 setup.py,這是 Python 內建的功能,用來啟動代碼。

from setuptools import setup, find_packages

setup(
    name='myapp',
    version='0.1.0',
    # packages=find_packages(),
    py_modules=['command1'],
    install_requires=[
        'click',
    ],
    entry_points={
        'console_scripts': [
            'myappcmd1=command1:pycli1',
        ],
    }
)


接下來是 command1.py

import click

# 自動填入剩餘參數的函數閉包
def auto_fill_argument(required=False):
    def callback(ctx, param, value):
        if 'args' not in ctx.params:
            ctx.params['args'] = ()
        args = list(ctx.params['args'])
        if value is None:
            if args:
                value = args.pop(0)
                ctx.params['args'] = tuple(args)
            elif required:
                raise click.MissingParameter(param_hint=[f"--{param.name}", f"-{param.opts[1]}"])
        ctx.params['args'] = tuple(args)
        return value
    return callback

# 自定義命令類以實現多餘參數檢查
class AddWithExtraArgsCheck(click.Command):
    def invoke(self, ctx):
        if 'args' in ctx.params and ctx.params['args']:
            params_info = ", ".join(f"{param}: {value if value is not None else ''}" for param, value in ctx.params.items() if param != 'args')
            extra_args = " ".join(ctx.params['args'])
            raise click.UsageError(
                f"Got unexpected extra arguments ({extra_args})"
                f", already assigned ({params_info})",
                ctx=ctx
            )
        return super().invoke(ctx)

# 宣告選項群
@click.group()
def pycli1():
    pass

# 選項命令1:: upload
@pycli1.command(cls=AddWithExtraArgsCheck)
@click.option('--identity', '-id', type=str, callback=auto_fill_argument(required=True), help="指定雲端文件夾的 ID")
@click.option('--name', '-n', type=str, callback=auto_fill_argument(required=True), help="指定文件路徑")
@click.option('--type', '-t', type=click.Choice(['any', 'folder', 'file'], case_sensitive=False), default='any', help="指定路徑類型: any (預設), folder, file")
@click.argument('args', nargs=-1)
def upload(identity, name, type, args):
    """上傳文件到指定的雲端文件夾"""
    click.echo(f"雲端文件夾ID:{identity}, 正在上傳文件:{name}, 路徑類型:{type}", err=False)

# 選項命令2:: getmyinfo
@pycli1.command()
def getmyinfo():
    """獲取我的信息"""
    click.echo(f"執行 getmyinfo 動作,目前沒有任何參數", err=False)

if __name__ == '__main__':
    pycli1()


使用範例:

myappcmd1 upload "C:\path\to\your\file.txt" -id 12345

如此一來,就可以實現自動參數填充的功能。


通過指定 callback=auto_fill_argument(required=True) 可以將參數標記為自動填入項目,並且可以進一步標記為必須項目。該參數可以為空,但至少要保留括號。

此外,通過在選項命令處指定 cls=AddWithExtraArgsCheck,可以檢查是否有多餘的參數。這樣做的原因是,啟用 args 後會關閉原生的參數檢查功能,但如果不啟用 args,又無法接收任意參數,所以只能這樣妥協使用。

沒有留言:

張貼留言