尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

gRPC Python Bazel 规则下游集成测试指南:py_proto_library 与 py_grpc_library 的 Workspace 级验证

gRPC Python Bazel 规则下游集成测试指南:py_proto_library 与 py_grpc_library 的 Workspace 级验证 gRPC Python Bazel 规则下游集成测试指南py_proto_library 与 py_grpc_library 的 Workspace 级验证【免费下载链接】grpcC based gRPC (C, Python, Ruby, Objective-C, PHP, C#)项目地址: https://gitcode.com/GitHub_Trending/gr/grpc本指南围绕 gRPC 仓库中的 test/distrib/bazel/python/README.md 及其所在的 Bazel Workspace 测试目录展开核心主题是验证下游下游工程/外部 Workspace能够直接使用com_github_grpc_grpc//src/python/grpcio:grpcio、py_proto_library与py_grpc_library三件套完成 Python gRPC 代码生成与集成。读完本文你将掌握 gRPC Python Bazel 规则的正确加载方式、BUILD 目标声明范式、跨包与命名空间 proto 的导入语义以及如何在自己的项目中复用这套集成测试。一、测试目录的定位为什么要做Workspace 级验证该测试目录的核心意图正如其 README 所述确保下游项目能够使用 gRPC 仓库对外提供的 Python Bazel 规则。与仓库内部单元测试不同这里的重点是外部消费者视角下游工程通过local_repository或版本依赖方式引用com_github_grpc_grpc下游工程的BUILD文件必须能正常load并调用py_proto_library/py_grpc_library生成的*_pb2.py与*_pb2_grpc.py必须能通过py_test在真实运行时导入并执行一次完整的 RPC 调用。目录结构见 test/distrib/bazel/python包含一个独立的 Bazel 工程自带WORKSPACE、多个BUILD文件、一组.proto定义、若干py_test用例以及一个基于analysistest的规则行为测试。二、Workspace 配置模拟真实下游工程WORKSPACE 完整模拟了一个真实下游工程的依赖装配流程其中有两个关键细节值得注意。2.1 以 local_repository 引入 gRPC 与自定义仓库名local_repository( name com_github_grpc_grpc, path ../../../.., ) # Ensure rules dont rely on __main__ naming convention. workspace(name python_test_repo)path ../../../..指向上游三层的 gRPC 仓库根目录即当前测试目录位于test/distrib/bazel/python/向上四级即仓库根显式声明workspace(name python_test_repo)并附注释 Ensure rules dont rely on__main__naming convention其目的是验证规则实现不依赖 Bazel 默认主仓库名__main__这是下游工程最容易踩坑、也最需要被回归测试覆盖的点另外还声明了一个some_other_repo本地仓库path ../python_second_test_repo用于跨仓库 proto 依赖测试。2.2 依赖拉取与 Python 工具链装配load(com_github_grpc_grpc//bazel:grpc_deps.bzl, grpc_deps) grpc_deps() load(com_github_grpc_grpc//bazel:grpc_extra_deps.bzl, grpc_extra_deps) grpc_extra_deps()随后依次完成bazel_skylib_workspace()装配 skylib 工作区system_python(name system_python, minimum_python_version 3.10)声明系统 Python 的最低版本为 3.10pip_parse(...)以com_google_protobuf//python:requirements.txt为基础、并对 Python 3.11 提供requirements_311.txt覆盖的 pip 依赖解析python_register_toolchains(name python_3_11, python_version 3.11)注册 Python 3.11 工具链。这条配置链展示了下游用户在使用 gRPC Python Bazel 规则前需要完成的全部准备工作可作为自己工程WORKSPACE的模板。三、BUILD 文件解剖py_proto_library py_grpc_library 的标准用法BUILD 是本文档的核心实操样本它演示了 gRPC Python Bazel 规则在下游工程中的全部标准调用方式。首先看规则加载load( com_github_grpc_grpc//bazel:python_rules.bzl, py_grpc_library, ) load(com_google_protobuf//bazel:py_proto_library.bzl, py_proto_library) load(rules_proto//proto:defs.bzl, proto_library) load(rules_python//python:defs.bzl, py_library, py_test)py_grpc_library由 gRPC 仓库的 bazel/python_rules.bzl 提供py_proto_library来自com_google_protobuf默认走 protobuf 官方实现详见下文源码分析整个 package 声明了default_testonly 1表明这是一个纯测试包。3.1 最基础的三段式proto_library → py_proto_library → py_grpc_libraryproto_library( name helloworld_proto, srcs [helloworld.proto], deps [ :hello_dep_proto, com_google_protobuf//:duration_proto, com_google_protobuf//:timestamp_proto, ], ) py_proto_library( name helloworld_py_pb2, deps [:helloworld_proto], ) py_grpc_library( name helloworld_py_pb2_grpc, srcs [:helloworld_proto], deps [:helloworld_py_pb2], )这是 gRPC Python Bazel 集成的标准姿势与源码中规则签名严格对应py_proto_library(name, deps, use_protobuf True, **kwargs)deps只接受一个proto_library目标默认委托给 protobuf 官方实现com_google_protobuf//bazel:py_proto_library.bzl当use_protobuf False时则走 gRPC 仓库自带的_py_proto_library私有实现py_grpc_library(name, srcs, deps, strip_prefixes [], grpc_library Label(//src/python/grpcio/grpc:grpcio), **kwargs)srcs传 proto 定义deps传与之配对的py_proto_library目标两者都必须恰好为一个否则fail(Can only compile a single proto at a time.)。helloworld.proto见 helloworld.proto引入了google/protobuf/timestamp.proto、google/protobuf/duration.proto以及位于subdir/下的hello_dep.proto覆盖了自定义依赖 well-known 类型两种依赖形态。3.2 生成产物的命名规则从 bazel/python_rules.bzl 顶部的格式常量可以看出生成的 Python 文件命名约定_GENERATED_PROTO_FORMAT {}_pb2.py _GENERATED_PROTO_STUB_FORMAT {}_pb2.pyi _GENERATED_GRPC_PROTO_FORMAT {}_pb2_grpc.py即helloworld_proto会生成helloworld_pb2.py含.pyi类型桩与helloworld_pb2_grpc.py这与传统protoc手工生成的文件名完全一致保证 Bazel 构建产物能无缝对接到已有的 Python 工作流。四、规则实现原理protoc 与 grpc_python_plugin 的封装理解规则签名后再深入到 bazel/python_rules.bzl 看底层实现能解释为什么必须恰好一个 proto以及虚拟导入路径从何而来。4.1 py_proto_library 的 aspect 驱动生成_py_proto_library规则通过_gen_py_aspect一个遍历deps的 aspect完成代码生成。核心动作是调用protocarguments ([ --python_out{}.format(out_dir.path), --pyi_out{}.format(out_dir.path), ] [ --proto_path{}.format(get_include_directory(i)) for i in includes.to_list() ] [ --proto_path{}.format(context.genfiles_dir.path), ])一次调用同时产出_pb2.py与_pb2.pyi_protoc默认为com_google_protobuf//:protoc_protobuf_library默认为com_google_protobuf//:protobuf_python对于 well-known proto 会提前返回直接复用 protobuf 自带的 Python 库避免重复生成。_generate_py_impl还处理了一个重要场景当py_proto_library直接依赖的proto_library位于其他 package 时会在当前 package 生成一层薄薄的 re-import 文件内容形如from pkg import *从而允许 Python 导入路径与 Bazel package 布局解耦。测试目录中的in_subpackage/正是为验证该行为而设。4.2 py_grpc_library 的插件调用与 grpc_library 替换_generate_pb2_grpc_src规则在protoc基础上追加了 gRPC Python 插件_grpc_plugin: attr.label( executable True, cfg exec, default Label(//src/compiler:grpc_python_plugin), ), ... plugin_flags [grpc_2_0] context.attr.strip_prefixes插件目标指向 src/compiler:grpc_python_plugin对应 python_plugin.cc 与 python_generator.cc 的实现。grpc_2_0标志让生成器产出兼容 gRPC 2.0 API 的 stub 代码。规则属性中还有一个关键的grpc_librarygrpc_library: attr.label( default Label(//src/python/grpcio/grpc:grpcio), providers [PyInfo], ),它默认指向仓库内的grpcio目标src/python/grpcio/grpc/BUILD.bazel 中的grpcio库但在下游工程中可以通过grpc_library参数替换为来自 PyPI 的grpcio包。这正是 README 中点名com_github_grpc_grpc//src/python/grpcio:grpcio的原因——该路径是下游引用 gRPC Python 运行时库的官方入口。五、测试用例全景每一类集成风险都有对应的回归测试该目录用一套py_test矩阵覆盖了下游集成中可能出现的各种边界情况全部定义在 BUILD 中。5.1 端到端 RPC 冒烟测试import_testhelloworld.py 不只是能 import而是完整跑了一次 gRPC 调用用grpc.server(futures.ThreadPoolExecutor())起服务端server.add_insecure_port(localhost:0)让系统分配空闲端口客户端通过grpc.insecure_channel建立连接调用GreeterStub.SayHello带wait_for_readyTrue请求中携带google.protobuf.Timestamp响应中回传Duration断言response.message Hello, you!且request_duration.nanos 0。这验证了生成代码 well-known 类型 真实网络调用整条链路的可用性。5.2 子目录 proto 与移动导入路径hello_dep / helloworld_movedhello_dep_proto的srcs [subdir/hello_dep.proto]验证.proto可以放在 package 的严格子目录中helloworld_moved_proto使用import_prefix foo/bar与strip_import_prefix 将 proto 的导入路径整体搬移到foo/bar/命名空间下。对应的 helloworld_moved.py 中相应改为from foo.bar import helloworld_pb2、from foo.bar import helloworld_pb2_grpc验证py_proto_library/py_grpc_library与proto_library的import_prefix/strip_import_prefix参数完全兼容。5.3 跨 package 与跨仓库导入import_from_this_package_subpackage_test与import_from_proto_library_package_test共用:subpackage_py_pb2其deps指向//in_subpackage:subpackage_proto。前者在包含py_proto_library的 package中导入后者在包含proto_library的 package中导入验证 4.1 节所述 re-import 机制的两个方向都能工作见 import_from_this_package.pyin_subpackage/BUILD 中的subpackage_proto还deps到了外部仓库some_other_repo//proto:my_proto把跨仓库 proto 依赖也纳入了覆盖范围。5.4 反射与传递依赖import_from_grpcio_reflection_test直接以com_github_grpc_grpc//src/python/grpcio_reflection/grpc_reflection/v1alpha:grpc_reflection为依赖验证下游可以单独消费 gRPC 的 reflection 子包transitive_proto_dep_testtransitive_proto_dep.py只import helloworld_pb2而不 import 其传递依赖hello_dep_pb2、duration_pb2、timestamp_pb2验证生成的代码在不显式声明未使用依赖时也能被成功导入。5.5 grpc_library 属性替换grpc_library_replacement_testBUILD 中定义了py_library( name grpc_library_replacement, srcs [grpc_library_replacement.py], ) py_grpc_library( name helloworld_py_pb2_grpc_library_changed, srcs [:helloworld_proto], grpc_library :grpc_library_replacement, deps [:helloworld_py_pb2], )其中 grpc_library_replacement.py 特意是空文件注释说明其用途是测试py_grpc_library的grpc_library属性生效。这验证了规则支持将默认的仓库内grpcio目标替换为任意py_library例如 PyPI 的grpcio从而避免下游被迫构建仓库内 C 扩展。5.6 genrule 动态生成的 protoimport_generated_proto_testgenrule( name gen_echo_proto, srcs [echo.proto], outs [gen_echo.proto], cmd cp $(location echo.proto) $(location gen_echo.proto), ) proto_library( name gen_echo_proto_lib, srcs [:gen_echo_proto], )echo.proto 是一个最小化的自包含服务定义EchoService经genrule复制后同样走py_proto_library→py_grpc_library→py_test全流程验证规则能处理构建期动态产生的 proto 文件。5.7 规则行为级测试python_rules_test.bzl除了运行时py_testpython_rules_test.bzl 还基于bazel_skylib//lib:unittest.bzl的analysistest对规则输出做了静态断言断言helloworld_pb2.py与subdir/hello_dep_pb2.py同时出现在目标的files、default_runfiles与PyInfo.transitive_sources中注释中明确 TODO待py_proto_library支持输出.pyi文件后补充对helloworld_pb2.pyi的断言通过python_rules_test_suite(name python_rules_test)汇总为 test suite并由 BUILD 中的python_rules_test目标暴露。六、命名空间组合矩阵import_prefix 与 strip_import_prefix 的四种排列namespaced/upper/example/BUILD 专门针对无法把 proto 锚定在 workspace 根目录的场景做了穷举测试构造了四种排列proto_library 配置说明import_prefix foo/bar,strip_import_prefix None只加前缀、不裁剪import_prefix foo/bar,strip_import_prefix /pkg加前缀且裁剪本 package 路径import_prefix None,strip_import_prefix None完全不动import_prefix None,strip_import_prefix /pkg只裁剪、不加前缀该 BUILD 文件中的注释还给出了每种组合下生成文件的物理落盘位置例如# Both Import and Strip # bazel-bin/namespaced/upper/example/_virtual_imports/namespaced_example_proto/upper/example/namespaced_example_pb2.py这正是 4.1/4.2 节中_VIRTUAL_IMPORTS /_virtual_imports/逻辑的体现当存在strip_import_prefix时Python 模块生成在_virtual_imports目录下规则实现会在PyInfo.imports中补入{workspace_name}/{virtual_import_path}使生成模块可被import。py_grpc_library的strip_prefixes参数则用于在生成的 stub 中裁剪foo_pb2模块的导入前缀与py_library的imports属性配合使用见 python_rules.bzl 中py_grpc_library的 docstring。七、运行方式与使用建议7.1 如何运行本测试在仓库根目录执行测试位于test/distrib/bazel/python子工程拥有独立WORKSPACE# 运行该子工程下的全部测试 bazel test //... --configpython # 具体 config 以仓库 tools/bazel.rc 为准 # 或只运行规则行为级 suite bazel test //:python_rules_test注意该子工程是独立 Workspaceworkspace(name python_test_repo)因此需要在对应目录下作为独立 Bazel 工程执行。7.2 对下游工程的三条落地建议规则加载路径始终通过com_github_grpc_grpc//bazel:python_rules.bzl加载py_grpc_library通过com_google_protobuf//bazel:py_proto_library.bzl加载py_proto_library这与本测试目录的做法一致运行时依赖入口com_github_grpc_grpc//src/python/grpcio:grpcio是官方对外暴露的 Python gRPC 运行时目标若不想构建仓库内 C 扩展可用grpc_library参数指向 PyPI 版grpcio的py_library命名空间处理当 proto 无法锚定在 workspace 根目录时务必用import_prefix/strip_import_prefix组合并配合py_grpc_library的strip_prefixes参数同时参考 namespaced/upper/example 的四种排列验证生成模块的导入路径。八、小结test/distrib/bazel/python虽然 README 只有寥寥三行却是一个覆盖面极广的下游集成测试工程它通过独立的 Bazel Workspace 验证了py_proto_library、py_grpc_library与com_github_grpc_grpc//src/python/grpcio:grpcio的对外可用性覆盖 well-known 类型、子目录 proto、import_prefix/strip_import_prefix命名空间、跨 package / 跨仓库依赖、反射子包、传递依赖、grpc_library属性替换以及 genrule 动态 proto 等全部高风险场景并辅以analysistest对规则输出的静态断言。对任何计划在自身 Bazel 工程中集成 gRPC Python 的开发者而言这份测试既是可复制的 BUILD 模板也是可迁移的回归测试清单。【免费下载链接】grpcC based gRPC (C, Python, Ruby, Objective-C, PHP, C#)项目地址: https://gitcode.com/GitHub_Trending/gr/grpc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表