手把手教你组件化开发Python命令行工具

背景

作为研发,如果你想提高开发效率、解放生产力、CI/CD,一定离不开脚本。随着功能不断完善,脚本体量越来越大,统一一门脚本语言,解耦复用模块,组件化开发脚本变得更加重要。

所以,统一持续使用一门合适的脚本语言是非常有必要的。

作为移动端研发,我推荐使用Python,相比Shell,Ruby,Gradle,JS,Swift等等,优势如下:

  1. 入门简单,社区庞大,生态完善,很多坑别人都踩过,很多功能都有人实现过(这点很重要,不然你可能会因为一些大坑带来的挫败感停滞不前);

  2. 各端都有各自常用的脚本语言,而Python貌似更加的平台不相关,开发多端复用的工具也许Python更合适;

  3. 应用广泛,也许你后期可以拓展学习人工智能,大数据等;

下面详细介绍下组件化开发一套Python命令行工具需要用到的工具链及相关配置。(本文以MacOS为例)

安装Python3.x

Mac系统自带Python,可能会被系统工具依赖,如果玩坏了影响范围较大。一般版本较旧,且不方便管理,这里我建议自己再安装一套,推荐用Homebrew安装。(或者官网下载

# 安装3.x版本 
brew install python3 
# 系统自带的Python在 /usr/bin/python3 
# 使用Homebrew安装的Python一般在 /usr/local/bin/python3 
# 查看安装位置,安装后会打印brew安装的路径 
which python3 
# python包管理工具,下面介绍 
which pip3 

# 如果路径不对,一般是PATH没有记录新路径,添加一下即可 
echo $PATH 
# 卸载 
brew uninstall python3

关于pip3

类似iOS的Cocoapods,Java的Maven,前端的npm,pip3是官方自带的包管理工具,从PyPI或私有源上你可以轻松找到可供你使用的轮子(以下都称package)。

# 下载package 
python3 -m pip install <package> 
# 或者 
pip3 install <package> 

# 查看已安装package 
pip3 show <package> 
# 查看下载到本地的所有package 
pip3 list [-v] 

# 卸载 
pip3 uninstall <package>

关于pip3导入包的作用域

全局作用域

一般来说,pip3 install默认导入到全局作用域,即模块导入到/usr/local/lib/python3.x/site-packages路径下,可执行文件导入到/usr/local/bin/。

在系统目录下,可被所有用户共享。

用户作用域

添加--user选项,导入到用户作用域,即模块导入到/Users/lyon/Library/Python/3.x/ lib/python/site-packages,可执行文件导入到/Users/lyon/Library/Python/3.11/bin/。

在当前用户目录下,不可被其他用户共享。

注:/Users/lyon/Library/此路径一般不在环境变量PATH中,也就是说,如果pip3 install导入到用户作用域下可能因命令不可见导致调用失败。

安装pipx

pip3安装package以用户为最小隔离单位,即同一个用户安装的package在同一目录下。而且不支持同一个package的多版本共存,如果用户开发或使用的多个工程(以下称applicationorapp)依赖的同一个package版本冲突时将无法解决。

pipx是python package,封装并优化了pip的安装逻辑,对多个app依赖的packages隔离管理,解决了上述问题。

# 安装 
brew install pipx 
# 添加pipx的工作目录到PATH变量中 
pipx ensurepath

安装poetry

pip3/pipx准确说是针对工具使用者的包管理工具,比如要下载名为AppUploader的工具,使用者通过pip install安装时会自动下载该工具依赖的packages。

对于工具开发者,至少需要添加及管理依赖、隔离开发和生产环境、配置命令行入口的功能,可选工具有PipenvvenvsetuptoolsPoetry等,这些工具也都可以看作是对pip功能的封装。

推荐poetry,提供了从开发、依赖、编译、发布一整套的工程化解决方案,相比setuptools使用体验更好。

# 安装 
pipx install poetry 
# 创建新工程 
poetry new YOUR_PROJ_NAME 
# 初始化已有工程 
poetry init 
# 添加依赖 
poetry add requests 
# 导入/对齐依赖 
poetry install 
# 更新依赖 
poetry update requests 
# 激活虚环境(追加PATH,覆盖生产环境目录) 
poetry shell

poetry初始化之后,主要配置pyproject.toml文件,样例如下:

[tool.poetry]
name = "metis-ios-workflow"
version = "1.0.2"
description = "CLI tools for Metis."
authors = ["xxx"]
readme = "README.md"
packages = [
    {include = "metis_ios_workflow", from = "src"},
]
license = "MIT"
homepage = "xxx"

[tool.poetry.scripts]
metis = "metis_ios_workflow.cli.entry:metis"

[tool.poetry.dependencies]
python = "^3.11"
requests = "^2.31.0"
jira = {version = "^3.5.2", extras = ["cli"]}
appstoreconnect = {git = "https://github.com/xxx/xxx.git", branch = "xxx"}

[tool.poetry.group.dev.dependencies]
flake8 = "^6.0.0"
flake8-quotes = "*"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

代码风格检查

主流的代码风格检查工具有flake8blackisort,其中black和isort提供格式化代码的入口。

关于代码风格建议详见《PEP 8 – Style Guide for Python Code》,根据规范制定的错误码详见pycodestyle

# 安装(仅开发环境) 
poetry add flake8 isort black --group=dev

安装及配置pre-commit

pre-commit可以集中管理git hooks各个阶段的脚本,并且提供丰富的插件及自定义插件的能力,flake8和isort均提供了相关插件,代码检查时机可通过配置pre-commit完成,在git pre-commit时自动检查,检查不通过则commit失败。

# step1:安装 
pipx install pre-commit 
# step2:通过`.pre-commit-config.yaml`配置pre-commit(git仓库根目录) 
# step3:导入git hook插件(git仓库根目录执行) 
pre-commit install

其中.pre-commit-config.yaml文件配置如下:

repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.2.0
    hooks:
      - id: end-of-file-fixer
      - id: trailing-whitespace
  - repo: https://github.com/pycqa/flake8
    rev: 4.0.1
    hooks:
      - id: flake8
        additional_dependencies: [flake8_commas, flake8_quotes]
  - repo: https://github.com/pycqa/isort
    rev: 5.12.0
    hooks:
      - id: isort

安装及配置PyCharm CE

相比VSCode,PyCharm默认集成了代码提示,风格检查,查看API,Debug工具,自动导入poetry依赖,自动激活虚环境等一系列强大功能,而且与IDEA交互风格非常相似,熟悉以上IDE的研发可以无缝切换,推荐使用PyCharm。

JetBrains推出了很多优秀的开发工具,其中IDEA、WebStorm等是目前业内最受欢迎的IDE,PyCharm是旗下专业的Python IDE,可以下载社区免费版

自动格式化代码

“cmd+,”进入偏好设置,找到External Tools,点击+号,这里分别添加black和isort外部工具。

black配置如下,其中:

$PyInterpreterDirectory$是python3解释器路径,在poetry虚环境下即是其可执行文件目录,确保black cli在此目录下即可。

$FilePath$是当前选中的文件/目录,作为black cli入参,指定需要格式化的文件/目录。

isort配置如下:

选中文件/目录后,可在菜单栏中点击调用对应的命令完成代码格式化。

也可以如下配置快捷键。(或者监听文件变化自动执行,参考:Automate linting & formatting in PyCharm with your favourite tools

[图片上传失败...(image-a0e2a1-1694170216976)]

定制命令入口

[图片上传失败...(image-e820ba-1694165964506)]

对于一套功能较复杂的CLI,如git命令,以git [options] <command> [<args>]的形式组合了clone/commit/push等功能,方便统一查看帮助信息,也可以结构化复用相关参数,需要定制层级分明、可组合式的命令行入口。

推荐使用click三方库辅助设计,通过poetry add click安装。

定制一套形如metis <command> <subcommand> [<args>]的CLI,可如下设计:

metis作为命令入口,添加各子功能模块。

import click

from metis_ios_workflow.cli.cmds.app import app
from metis_ios_workflow.cli.cmds.gitlab import gitlab
from metis_ios_workflow.cli.cmds.jira import jira
from metis_ios_workflow.cli.cmds.notify import notify
from metis_ios_workflow.cli.cmds.pod import pod
from metis_ios_workflow.cli.cmds.release import release
from metis_ios_workflow.cli.cmds.scrum import scrum
from metis_ios_workflow.cli.cmds.sentry import sentry
from metis_ios_workflow.cli.cmds.xcrc import xcrc

CONTEXT_SETTINGS = {"help_option_names": ["-h", "--help"]}


@click.group(context_settings=CONTEXT_SETTINGS)
@click.version_option(None, "-v", "-V", "--version")
def metis():
    pass

metis.add_command(app)
metis.add_command(notify)
metis.add_command(release)
metis.add_command(jira)
metis.add_command(gitlab)
metis.add_command(scrum)
metis.add_command(pod)
metis.add_command(sentry)
metis.add_command(xcrc)

metis release pre-release命令为例,将pre-release命令添加到release组下。

import click

from metis_ios_workflow.app_release_assist.app_review_monitor import app_review_monitor
from metis_ios_workflow.app_release_assist.post_build import post_build_cli
from metis_ios_workflow.app_release_assist.post_release import post_release_cli
from metis_ios_workflow.app_release_assist.pre_release import pre_release_cli
from metis_ios_workflow.utils.macro import BuildConfiguration


@click.group(help="发版助手")
def release():
    pass
    
    
@release.command(help="「预发版」任务处理")
@click.option("--target_list", type=click.STRING, required=True, help="App Target列表,以,间隔")
@click.option("-v", "--version", type=click.STRING, required=True, help="App三位版本号")
@click.option(
    "--ref",
    type=click.STRING,
    required=False,
    help="设置从特定的ref(branch/tag/commitSHA)开分支,不传时默认从master分支的最新提交开分支",
)
@click.option(
    "--pbxproj_path",
    type=click.STRING,
    required=True,
    help="project.pbxproj文件路径,用于自动更新对应App的版本号",
)
def pre_release(target_list, version, ref, pbxproj_path):
    pre_release_cli(target_list, version, ref, pbxproj_path)

使用者角度可以明确metis工具集的功能范围及使用介绍。

metis --help
                
Usage: metis [OPTIONS] COMMAND [ARGS]...

Options:
  -v, -V, --version  Show the version and exit.
  -h, --help         Show this message and exit.

Commands:
  app      app相关工具
  gitlab   gitlab工具
  jira     jira工具
  notify   企微通知工具
  pod      Cocoapods工具
  release  发版助手
  scrum    scrum助手
  sentry   sentry工具
  xcrc     XCRemoteCache相关

命令入参详细介绍。

metis release pre-release --help

Usage: metis release pre-release [OPTIONS]

  「预发版」任务处理

Options:
  --target_list TEXT   App Target列表,以,间隔  [required]
  -v, --version TEXT   App三位版本号  [required]
  --ref TEXT           设置从特定的ref(branch/tag/commitSHA)开分支,不传时默认从master分支的最新提交开
                       分支
  --pbxproj_path TEXT  project.pbxproj文件路径,用于自动更新对应App的版本号  [required]
  -h, --help           Show this message and exit.

发布

发布渠道有三种:PyPI、私有源、git仓库,对应使用方三种安装方式。

# PyPI
# 发布
poetry publish
# 安装
pip3 install <PACKAGE_NAME>

# 私有源
# 首次需配置私有源
poetry config repo.REPO_NAME REPO_INDEX_URL
# 发布
poetry publish --repository=<REPO_NAME>
# 安装
pip3 install <PACKAGE_NAME> --extra-index-url <REPO_INDEX_URL> --trusted-host <DOMAIN_OF_REPO_INDEX_URL>

# Git仓库
# 发布:提交到远端分支即视为发布
# 安装
pip3 install git+ssh://git@github.com/xxx/xxx.git@BRANCH_NAME

总结

  1. 安装Python3开发包;
  2. pipx:隔离依赖packages,避免版本冲突;
  3. poetry:适用开发者的工程配置、依赖管理工具集;
  4. 代码风格检查:flake8/black/isort + pre-commit,自动执行代码检查;
  5. PyCharm:自带完善功能的IDE,配置自动格式化代码;
  6. click:三方开发库,定制可组合式的命令行工具集;
  7. 三种发布及安装方式;

以上是组件化开发一套命令行工具,需要使用的工具链及配置。

工欲善其事必先利其器,也许你实现过以调用py文件的方式执行脚本,不过随着功能逐渐完善,脚本文件逐渐增多,建立一套相对规范的组件化工程将助力你更高效开发高可用的工具集。

参考

pipx

poetry

PEP 8 – Style Guide for Python Code

flake8

black

isort

pre-commit

click[图片上传失败...(image-3523d4-1694417794422)]

[图片上传失败...(image-eccf1b-1694417794422)]

[图片上传失败...(image-f3a304-1694417794422)]

[图片上传失败...(image-bc7ec2-1694417794422)]

[图片上传失败...(image-bd810b-1694417794422)]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容