用惯了Django的manage.py所以在FastAPI的项目里,也写了一个类似的脚步:
#!/usr/bin/env python import subprocess import sys from pathlib import Path from typing import List, Tuple def get_part(s: str) -> str: """根据输入的选项,返回对应的命令参数 Usage:: >>> get_part('1') 'patch' >>> get_part('patch') 'patch' """ choices = {"1": "patch", "2": "minor", "3": "major"} choices.update({v: v for v in choices.values()}) try: return choices[s] except KeyError: print(f"Invalid part: {s!r}") sys.exit(1) def run_and_echo(cmd: str) -> int: """先打印出SHELL命令,然后执行它""" print("-->", cmd) return subprocess.run(cmd, shell=True).returncode def get_current_version() -> str: """获取pyproject.toml文件里的版本号""" r = subprocess.run(["poetry", "version", "-s"], capture_output=True) return r.stdout.decode().strip() def exit_if_run_failed(cmd: str) -> None: """打印和执行SHELL命令,并在执行失败时退出脚本""" if rc := run_and_echo(cmd): sys.exit(rc) def bump(): """根据输入的选项,执行bumpversion命令去升级版本和打标签""" version = get_current_version() print(f"Current version: {version}") if sys.argv[1:]: part = get_part(sys.argv[1]) else: tip = ( "Choices:\n1. patch\n2. minor\n3. major\n\n" "Which one to bump?(Leave blank to use `patch`) " ) if a := input(tip).strip(): part = get_part(a) else: part = "patch" parse = '"(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)"' # NOQA cmd = ( f"bumpversion --commit --parse {parse}" f" --current-version {version} {part} pyproject.toml" ) if part != "patch": cmd + " --tag" exit_if_run_failed(cmd) exit_if_run_failed("git push && git push --tags && git log -1") class UpgradeDependencies: """升级依赖到最新版本 (直接用poetry update的话,只会升级小版本,不会升级大版本) """ @staticmethod def parse_extras(version_info: str, extras_tip: str) -> str: extras_info = version_info.split(extras_tip)[-1] return extras_info.split("[", 1)[-1].split("]")[0].replace('"', "") @classmethod def build_args(cls, package_lines: List[str], extras_tip: str) -> List[str]: args: List[str] = [] for m in package_lines: package, version_info = m.split("=", 1) if (package := package.strip()).lower() == "python": continue if extras_tip in version_info: package += "[" + cls.parse_extras(version_info, extras_tip) + "]" args.append(f'"{package}@latest"') else: args.append(f"{package}@latest") return args @classmethod def get_args(cls) -> Tuple[List[str], List[str]]: main_title = "[tool.poetry.dependencies]" dev_title = "[tool.poetry.dev-dependencies]" toml_file = Path(__file__).parent.resolve().parent / "pyproject.toml" text = toml_file.read_text("utf8").split(main_title)[-1] main, dev = text.split(dev_title) devs = dev.split("[tool.")[0].strip().splitlines() mains = main.strip().splitlines() extras_tip = "extras" return cls.build_args(mains, extras_tip), cls.build_args(devs, extras_tip) def update(): """升级所有依赖包到最新版""" main_args, dev_args = UpgradeDependencies.get_args() command = "poetry add" upgrade = "{0} {1} && {0} --dev {2}".format( command, " ".join(main_args), " ".join(dev_args) ) exit_if_run_failed(upgrade) def lint(remove=None): """格式化加静态检查""" remove_imports = "autoflake --in-place --remove-all-unused-imports" cmd = "" paths = "." if args := sys.argv[1:]: if (flag := "-r") in args: if remove is None: remove = True args = [i for i in args if i != flag] paths = " ".join(args) if remove and all(Path(i).is_file() for i in args): cmd = f"{remove_imports} {paths} && " + cmd lint_them = "{0} {2} {1} && {0} {3} {1} && {0} {4} {1} && {0} {5} {1}" tools = ("isort", "black", "pflake8", "mypy") cmd += lint_them.format("poetry run", paths, *tools) exit_if_run_failed(cmd) def dev(): """启动服务:相当于django的runserver""" cmd = "poetry run python main.py" if args := sys.argv[1:]: cmd += " " + " ".join(args) exit_if_run_failed(cmd) def makemigrations(): """生成数据库迁移文件,类似Django的./manage.py makemigrations""" exit_if_run_failed("aerich migrate") def migrate(): """更新数据库表结构:相当于django的./manage.py migrate""" exit_if_run_failed("aerich upgrade")标签:cmd,run,args,--,FastAPI,py,version,str,def From: https://www.cnblogs.com/waketzheng/p/16916834.html