#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 自动生成 gRPC Python 代码脚本(相对导入版) 递归扫描 protos/ 目录并生成到 generated/ """ import os import subprocess from pathlib import Path def generate_grpc_code(proto_dir: str, output_dir: str): proto_path = Path(proto_dir) if not proto_path.exists(): raise FileNotFoundError(f"❌ proto 目录不存在: {proto_dir}") proto_files = [str(p) for p in proto_path.rglob("*.proto")] if not proto_files: print("⚠️ 未找到任何 .proto 文件") return os.makedirs(output_dir, exist_ok=True) print(f"🔧 生成目录: {output_dir}") for proto_file in proto_files: print(f" ⏳ 编译 {proto_file}") # 只需要指定根目录 include_paths = [f"-I{proto_dir}"] cmd = [ "python", "-m", "grpc_tools.protoc", *include_paths, f"--python_out={output_dir}", f"--grpc_python_out={output_dir}", proto_file, ] try: result = subprocess.run(cmd, check=True, capture_output=True, text=True) if result.stdout.strip(): print(result.stdout) if result.stderr.strip(): print("⚠️ 编译警告:", result.stderr) except subprocess.CalledProcessError as e: print("❌ 编译失败:") print(e.stdout) print(e.stderr) continue # 确保每个目录都有 __init__.py ensure_init_files(output_dir) # 修改 _pb2_grpc.py 为相对导入 convert_grpc_imports_to_relative(output_dir) print(f"✅ 所有 proto 文件已生成到: {output_dir}") def ensure_init_files(output_dir: str): """递归生成 __init__.py 确保 Python 包可导入""" for root, dirs, files in os.walk(output_dir): init_file = os.path.join(root, "__init__.py") if not os.path.exists(init_file): open(init_file, "w", encoding="utf-8").close() def convert_grpc_imports_to_relative(output_dir: str): """把 _pb2_grpc.py 中的绝对导入改为相对导入""" for root, dirs, files in os.walk(output_dir): for file in files: if file.endswith("_pb2_grpc.py"): path = os.path.join(root, file) with open(path, "r", encoding="utf-8") as f: lines = f.readlines() new_lines = [] for line in lines: # 将类似 `import xxx_pb2 as xxx__pb2` 改为 `from . import xxx_pb2 as xxx__pb2` if line.startswith("import ") and "_pb2 as " in line: parts = line.split(" as ") module_name = parts[0].replace("import", "").strip() alias = parts[1].strip() new_lines.append(f"from . import {module_name} as {alias}\n") else: new_lines.append(line) with open(path, "w", encoding="utf-8") as f: f.writelines(new_lines) if __name__ == "__main__": ROOT_DIR = Path(__file__).resolve().parents[1] PROTO_DIR = ROOT_DIR / "protos" OUTPUT_DIR = ROOT_DIR / "generated" print("🚀 开始生成 gRPC Python 代码(相对导入版)...") generate_grpc_code(str(PROTO_DIR), str(OUTPUT_DIR)) print("\n🎉 gRPC Python 代码生成完成!")